Merged 1.2 into master

This commit is contained in:
Robin Krom 2015-10-27 21:24:50 +01:00
commit 9eb4f37523
57 changed files with 1809 additions and 720 deletions

View file

@ -57,30 +57,47 @@ namespace Greenshot.Controls {
if (e.Index > -1) {
FontFamily fontFamily = Items[e.Index] as FontFamily;
FontStyle fs = FontStyle.Regular;
FontStyle fontStyle = FontStyle.Regular;
if (!fontFamily.IsStyleAvailable(FontStyle.Regular)) {
if (fontFamily.IsStyleAvailable(FontStyle.Bold)) {
fs = FontStyle.Bold;
fontStyle = FontStyle.Bold;
} else if (fontFamily.IsStyleAvailable(FontStyle.Italic)) {
fs = FontStyle.Italic;
fontStyle = FontStyle.Italic;
} else if (fontFamily.IsStyleAvailable(FontStyle.Strikeout)) {
fs = FontStyle.Strikeout;
fontStyle = FontStyle.Strikeout;
} else if (fontFamily.IsStyleAvailable(FontStyle.Underline)) {
fs = FontStyle.Underline;
fontStyle = FontStyle.Underline;
}
}
using (Font font = new Font(fontFamily, this.Font.Size + 5, fs, GraphicsUnit.Pixel)) {
// Make sure the text is visible by centering it in the line
using(StringFormat stringFormat = new StringFormat()) {
stringFormat.LineAlignment = StringAlignment.Center;
e.Graphics.DrawString(fontFamily.Name, font, Brushes.Black, e.Bounds, stringFormat);
}
try {
DrawText(e.Graphics, fontFamily, fontStyle, e.Bounds, fontFamily.Name);
} catch {
// If the drawing failed, BUG-1770 seems to have a weird case that causes: Font 'Lucida Sans Typewriter' does not support style 'Regular'
DrawText(e.Graphics, FontFamily.GenericSansSerif, FontStyle.Regular, e.Bounds, fontFamily.Name);
}
}
// Uncomment this if you actually like the way the focus rectangle looks
//e.DrawFocusRectangle ();
}
/// <summary>
/// Helper method to draw the string
/// </summary>
/// <param name="graphics"></param>
/// <param name="fontFamily"></param>
/// <param name="fontStyle"></param>
/// <param name="bounds"></param>
/// <param name="text"></param>
private void DrawText(Graphics graphics, FontFamily fontFamily, FontStyle fontStyle, Rectangle bounds, string text) {
using (Font font = new Font(fontFamily, this.Font.Size + 5, fontStyle, GraphicsUnit.Pixel)) {
// Make sure the text is visible by centering it in the line
using (StringFormat stringFormat = new StringFormat()) {
stringFormat.LineAlignment = StringAlignment.Center;
graphics.DrawString(text, font, Brushes.Black, bounds, stringFormat);
}
}
}
void BindableToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs("Text"));

View file

@ -135,7 +135,7 @@ namespace Greenshot.Destinations {
string filepath = FilenameHelper.FillVariables(conf.OutputFilePath, false);
try {
fullPath = Path.Combine(filepath, filename);
} catch (ArgumentException ae) {
} catch (ArgumentException) {
// configured filename or path not valid, show error message...
LOG.InfoFormat("Generated path or filename not valid: {0}, {1}", filepath, filename);

View file

@ -146,9 +146,6 @@ namespace Greenshot {
// Only use double-buffering when we are NOT in a Terminal Server session
DoubleBuffered = !isTerminalServerSession;
// Not needed for a Tool Window, but still for the task manager it's important
Icon = GreenshotResources.getGreenshotIcon();
// Use the self drawn image, first we create the background to be the backcolor (as we animate from this)
gBitmap = ImageHelper.CreateEmpty(90, 90, PixelFormat.Format24bppRgb, BackColor, 96, 96);
pictureBox1.Image = gBitmap;

View file

@ -31,8 +31,7 @@ namespace Greenshot.Forms {
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
Icon = GreenshotResources.getGreenshotIcon();
WindowDetails.ToForeground(Handle);
ToFront = true;
}
public BugReportForm(string bugText) : this() {

View file

@ -61,7 +61,7 @@ namespace Greenshot.Forms {
this.Cursor = System.Windows.Forms.Cursors.Cross;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Name = "CaptureForm";
this.ShowIcon = false;
this.ShowIcon = true;
this.ShowInTaskbar = false;
this.TopMost = true;
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.CaptureFormKeyDown);

View file

@ -123,6 +123,7 @@ namespace Greenshot.Forms {
LOG.Debug("Closing captureform");
WindowDetails.UnregisterIgnoreHandle(Handle);
}
/// <summary>
/// This creates the capture form
/// </summary>
@ -130,7 +131,7 @@ namespace Greenshot.Forms {
/// <param name="windows"></param>
public CaptureForm(ICapture capture, List<WindowDetails> windows) {
if (_currentForm != null) {
LOG.Debug("Found currentForm, Closing already opened CaptureForm");
LOG.Warn("Found currentForm, Closing already opened CaptureForm");
_currentForm.Close();
_currentForm = null;
Application.DoEvents();
@ -176,7 +177,7 @@ namespace Greenshot.Forms {
ResumeLayout();
// Fix missing focus
WindowDetails.ToForeground(Handle);
ToFront = true;
TopMost = true;
}

View file

@ -31,7 +31,6 @@ namespace Greenshot.Forms {
public DropShadowSettingsForm(DropShadowEffect effect) {
this.effect = effect;
InitializeComponent();
this.Icon = GreenshotResources.getGreenshotIcon();
ShowSettings();
}

View file

@ -162,8 +162,6 @@ namespace Greenshot {
}
private void updateUI() {
Icon = GreenshotResources.getGreenshotIcon();
// Disable access to the settings, for feature #3521446
preferencesToolStripMenuItem.Visible = !coreConfiguration.DisableSettings;
toolStripSeparator12.Visible = !coreConfiguration.DisableSettings;

View file

@ -270,6 +270,9 @@ namespace Greenshot {
return;
}
// BUG-1809: Add message filter, to filter out all the InputLangChanged messages which go to a target control with a handle > 32 bit.
Application.AddMessageFilter(new WmInputLangChangeRequestFilter());
// From here on we continue starting Greenshot
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
@ -359,7 +362,6 @@ namespace Greenshot {
throw;
}
notifyIcon.Icon = GreenshotResources.getGreenshotIcon();
Icon = GreenshotResources.getGreenshotIcon();
// Disable access to the settings, for feature #3521446
contextmenu_settings.Visible = !_conf.DisableSettings;
@ -508,14 +510,20 @@ namespace Greenshot {
get {return contextMenu;}
}
#region hotkeys
protected override void WndProc(ref Message m) {
if (HotkeyControl.HandleMessages(ref m)) {
return;
}
base.WndProc(ref m);
// BUG-1809 prevention, filter the InputLangChange messages
if (WmInputLangChangeRequestFilter.PreFilterMessageExternal(ref m))
{
return;
}
base.WndProc(ref m);
}
#region hotkeys
/// <summary>
/// Helper method to cleanly register a hotkey
/// </summary>
@ -1223,14 +1231,27 @@ namespace Greenshot {
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
Exception exceptionToLog = e.ExceptionObject as Exception;
string exceptionText = EnvironmentInfo.BuildReport(exceptionToLog);
LOG.Error(EnvironmentInfo.ExceptionToString(exceptionToLog));
LOG.Error("Exception caught in the UnhandledException handler.");
LOG.Error(exceptionText);
if (exceptionText != null && exceptionText.Contains("InputLanguageChangedEventArgs"))
{
// Ignore for BUG-1809
return;
}
new BugReportForm(exceptionText).ShowDialog();
}
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) {
Exception exceptionToLog = e.Exception;
string exceptionText = EnvironmentInfo.BuildReport(exceptionToLog);
LOG.Error(EnvironmentInfo.ExceptionToString(exceptionToLog));
LOG.Error("Exception caught in the ThreadException handler.");
LOG.Error(exceptionText);
if (exceptionText != null && exceptionText.Contains("InputLanguageChangedEventArgs"))
{
// Ignore for BUG-1809
return;
}
new BugReportForm(exceptionText).ShowDialog();
}

View file

@ -33,7 +33,6 @@ namespace Greenshot.Forms {
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
Icon = GreenshotResources.getGreenshotIcon();
checkbox_dontaskagain.Checked = false;
}

View file

@ -37,7 +37,6 @@ namespace Greenshot.Forms {
public ResizeSettingsForm(ResizeEffect effect) {
this.effect = effect;
InitializeComponent();
this.Icon = GreenshotResources.getGreenshotIcon();
value_pixel = Language.GetString("editor_resize_pixel");
value_percent = Language.GetString("editor_resize_percent");
combobox_width.Items.Add(value_pixel);

View file

@ -57,7 +57,6 @@ namespace Greenshot {
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
Icon = GreenshotResources.getGreenshotIcon();
// Fix for Vista/XP differences
if (Environment.OSVersion.Version.Major >= 6) {

View file

@ -30,7 +30,6 @@ namespace Greenshot.Forms {
public TornEdgeSettingsForm(TornEdgeEffect effect) {
this.effect = effect;
InitializeComponent();
this.Icon = GreenshotResources.getGreenshotIcon();
ShowSettings();
}

View file

@ -1,10 +1,22 @@
/*
* Created by SharpDevelop.
* User: jens
* Date: 09.04.2012
* Time: 19:24
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2015 Thomas Braun, Jens Klingen, Robin Krom
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
* For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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 GreenshotPlugin.Core;
@ -28,7 +40,7 @@ namespace Greenshot.Help
}
public static void LoadHelp() {
string uri = findOnlineHelpUrl(Language.CurrentLanguage);
string uri = FindOnlineHelpUrl(Language.CurrentLanguage);
if(uri == null) {
uri = Language.HelpFilePath;
}
@ -36,7 +48,7 @@ namespace Greenshot.Help
}
/// <returns>URL of help file in selected ietf, or (if not present) default ietf, or null (if not present, too. probably indicating that there is no internet connection)</returns>
private static string findOnlineHelpUrl(string currentIETF) {
private static string FindOnlineHelpUrl(string currentIETF) {
string ret = null;
string extHelpUrlForCurrrentIETF = EXT_HELP_URL;
@ -45,12 +57,12 @@ namespace Greenshot.Help
extHelpUrlForCurrrentIETF += currentIETF.ToLower() + "/";
}
HttpStatusCode? httpStatusCode = getHttpStatus(extHelpUrlForCurrrentIETF);
HttpStatusCode? httpStatusCode = GetHttpStatus(extHelpUrlForCurrrentIETF);
if(httpStatusCode == HttpStatusCode.OK) {
ret = extHelpUrlForCurrrentIETF;
} else if(httpStatusCode != null && !extHelpUrlForCurrrentIETF.Equals(EXT_HELP_URL)) {
LOG.DebugFormat("Localized online help not found at {0}, will try {1} as fallback", extHelpUrlForCurrrentIETF, EXT_HELP_URL);
httpStatusCode = getHttpStatus(EXT_HELP_URL);
httpStatusCode = GetHttpStatus(EXT_HELP_URL);
if(httpStatusCode == HttpStatusCode.OK) {
ret = EXT_HELP_URL;
} else {
@ -68,9 +80,9 @@ namespace Greenshot.Help
/// </summary>
/// <param name="url">URL for which the HTTP status is to be checked</param>
/// <returns>An HTTP status code, or null if there is none (probably indicating that there is no internet connection available</returns>
private static HttpStatusCode? getHttpStatus(string url) {
private static HttpStatusCode? GetHttpStatus(string url) {
try {
HttpWebRequest req = (HttpWebRequest)NetworkHelper.CreateWebRequest(url);
HttpWebRequest req = NetworkHelper.CreateWebRequest(url);
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
return res.StatusCode;
} catch(WebException e) {

View file

@ -967,7 +967,13 @@ namespace Greenshot.Helpers {
//}
using (CaptureForm captureForm = new CaptureForm(_capture, _windows)) {
DialogResult result = captureForm.ShowDialog();
// Make sure the form is hidden after showing, even if an exception occurs, so all errors will be shown
DialogResult result = DialogResult.Cancel;
try {
result = captureForm.ShowDialog(MainForm.Instance);
} finally {
captureForm.Hide();
}
if (result == DialogResult.OK) {
_selectedCaptureWindow = captureForm.SelectedCaptureWindow;
_captureRect = captureForm.CaptureRectangle;
@ -975,11 +981,11 @@ namespace Greenshot.Helpers {
if (_selectedCaptureWindow != null) {
_capture.CaptureDetails.Title = _selectedCaptureWindow.Text;
}
if (_captureRect.Height > 0 && _captureRect.Width > 0) {
// Take the captureRect, this already is specified as bitmap coordinates
_capture.Crop(_captureRect);
// save for re-capturing later and show recapture context menu option
// Important here is that the location needs to be offsetted back to screen coordinates!
Rectangle tmpRectangle = _captureRect;

View file

@ -18,30 +18,33 @@
* 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.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using GreenshotPlugin.UnmanagedHelpers;
using GreenshotPlugin.Core;
using Greenshot.IniFile;
using Greenshot.Drawing;
using GreenshotPlugin.UnmanagedHelpers;
using log4net;
namespace Greenshot.Helpers {
namespace Greenshot.Helpers
{
/// <summary>
/// Description of EnvironmentInfo.
/// </summary>
public static class EnvironmentInfo {
public static class EnvironmentInfo
{
private static readonly ILog LOG = LogManager.GetLogger(typeof(EnvironmentInfo));
private static bool? isWindows = null;
public static bool IsWindows {
get {
if (isWindows.HasValue) {
public static bool IsWindows
{
get
{
if (isWindows.HasValue)
{
return isWindows.Value;
}
isWindows = Environment.OSVersion.Platform.ToString().StartsWith("Win");
@ -49,75 +52,104 @@ namespace Greenshot.Helpers {
}
}
public static bool IsNet45OrNewer() {
public static bool IsNet45OrNewer()
{
// Class "ReflectionContext" exists from .NET 4.5 onwards.
return Type.GetType("System.Reflection.ReflectionContext", false) != null;
}
public static string EnvironmentToString(bool newline) {
public static string EnvironmentToString(bool newline)
{
StringBuilder environment = new StringBuilder();
environment.Append("Software version: " + Application.ProductVersion);
if (IniConfig.IsPortable) {
environment.Append(" Portable");
environment.Append(" Portable");
}
environment.Append(" (" + OSInfo.Bits + " bit)");
if (newline) {
if (newline)
{
environment.AppendLine();
} else {
}
else
{
environment.Append(", ");
}
environment.Append(".NET runtime version: " + Environment.Version);
if (IsNet45OrNewer()) {
if (IsNet45OrNewer())
{
environment.Append("+");
}
if (newline) {
if (newline)
{
environment.AppendLine();
} else {
}
else
{
environment.Append(", ");
}
environment.Append("Time: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss zzz"));
if (IsWindows) {
if (newline) {
if (IsWindows)
{
if (newline)
{
environment.AppendLine();
} else {
}
else
{
environment.Append(", ");
}
environment.Append(String.Format("OS: {0} {1} {2} (x{3}) {4}", OSInfo.Name, OSInfo.Edition, OSInfo.ServicePack, OSInfo.Bits, OSInfo.VersionString));
if (newline) {
if (newline)
{
environment.AppendLine();
} else {
}
else
{
environment.Append(", ");
}
// Get some important information for fixing GDI related Problems
environment.Append("GDI object count: " + User32.GetGuiResourcesGDICount());
if (newline) {
environment.AppendFormat("GDI object count: {0}", User32.GetGuiResourcesGDICount());
if (newline)
{
environment.AppendLine();
} else {
}
else
{
environment.Append(", ");
}
environment.Append("User object count: " + User32.GetGuiResourcesUserCount());
} else {
if (newline) {
environment.AppendLine();
} else {
environment.Append(", ");
}
environment.Append("OS: " + Environment.OSVersion.Platform.ToString());
environment.AppendFormat("User object count: {0}", User32.GetGuiResourcesUserCount());
}
if (newline) {
else
{
if (newline)
{
environment.AppendLine();
}
else
{
environment.Append(", ");
}
environment.AppendFormat("OS: {0}", Environment.OSVersion.Platform);
}
if (newline)
{
environment.AppendLine();
} else {
}
else
{
environment.Append(", ");
}
environment.Append("Surface count: " + Surface.Count);
// TODO: Is this needed?
// environment.AppendFormat("Surface count: {0}", Surface.Count);
return environment.ToString();
}
public static string ExceptionToString(Exception ex) {
public static string ExceptionToString(Exception ex)
{
if (ex == null)
return "null\r\n";
@ -125,44 +157,54 @@ namespace Greenshot.Helpers {
report.AppendLine("Exception: " + ex.GetType().ToString());
report.AppendLine("Message: " + ex.Message);
if (ex.Data != null && ex.Data.Count > 0) {
if (ex.Data != null && ex.Data.Count > 0)
{
report.AppendLine();
report.AppendLine("Additional Information:");
foreach (object key in ex.Data.Keys) {
foreach (object key in ex.Data.Keys)
{
object data = ex.Data[key];
if (data != null) {
if (data != null)
{
report.AppendLine(key + " : " + data);
}
}
}
if (ex is ExternalException) {
if (ex is ExternalException)
{
// e.g. COMException
report.AppendLine().AppendLine("ErrorCode: 0x" + (ex as ExternalException).ErrorCode.ToString("X"));
}
report.AppendLine().AppendLine("Stack:").AppendLine(ex.StackTrace);
if (ex is ReflectionTypeLoadException) {
if (ex is ReflectionTypeLoadException)
{
report.AppendLine().AppendLine("LoaderExceptions: ");
foreach (Exception cbE in (ex as ReflectionTypeLoadException).LoaderExceptions) {
foreach (Exception cbE in (ex as ReflectionTypeLoadException).LoaderExceptions)
{
report.AppendLine(cbE.Message);
}
}
if (ex.InnerException != null) {
if (ex.InnerException != null)
{
report.AppendLine("--- InnerException: ---");
report.AppendLine(ExceptionToString(ex.InnerException));
}
return report.ToString();
}
public static string BuildReport(Exception exception) {
public static string BuildReport(Exception exception)
{
StringBuilder exceptionText = new StringBuilder();
exceptionText.AppendLine(EnvironmentToString(true));
exceptionText.AppendLine(ExceptionToString(exception));
exceptionText.AppendLine("Configuration dump:");
using (TextWriter writer = new StringWriter(exceptionText)) {
IniConfig.GetIniSection<CoreConfiguration>().Write(writer, true);
using (TextWriter writer = new StringWriter(exceptionText))
{
// TODO: Create summary of properties
//var iniConfig = IniConfig.Current.WriteToStreamAsync();
}
return exceptionText.ToString();
@ -173,13 +215,16 @@ namespace Greenshot.Helpers {
/// Provides detailed information about the host operating system.
/// Code is available at: http://www.csharp411.com/determine-windows-version-and-edition-with-c/
/// </summary>
static public class OSInfo {
static public class OSInfo
{
#region BITS
/// <summary>
/// Determines if the current application is 32 or 64-bit.
/// </summary>
static public int Bits {
get {
static public int Bits
{
get
{
return IntPtr.Size * 8;
}
}
@ -190,9 +235,12 @@ namespace Greenshot.Helpers {
/// <summary>
/// Gets the edition of the operating system running on this computer.
/// </summary>
static public string Edition {
get {
if (s_Edition != null) {
static public string Edition
{
get
{
if (s_Edition != null)
{
return s_Edition; //***** RETURN *****//
}
@ -202,22 +250,30 @@ namespace Greenshot.Helpers {
OSVERSIONINFOEX osVersionInfo = new OSVERSIONINFOEX();
osVersionInfo.dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX));
if (GetVersionEx(ref osVersionInfo)) {
if (GetVersionEx(ref osVersionInfo))
{
int majorVersion = osVersion.Version.Major;
int minorVersion = osVersion.Version.Minor;
byte productType = osVersionInfo.wProductType;
short suiteMask = osVersionInfo.wSuiteMask;
#region VERSION 4
if (majorVersion == 4) {
if (productType == VER_NT_WORKSTATION) {
if (majorVersion == 4)
{
if (productType == VER_NT_WORKSTATION)
{
// Windows NT 4.0 Workstation
edition = "Workstation";
} else if (productType == VER_NT_SERVER) {
if ((suiteMask & VER_SUITE_ENTERPRISE) != 0) {
}
else if (productType == VER_NT_SERVER)
{
if ((suiteMask & VER_SUITE_ENTERPRISE) != 0)
{
// Windows NT 4.0 Server Enterprise
edition = "Enterprise Server";
} else {
}
else
{
// Windows NT 4.0 Server
edition = "Standard Server";
}
@ -226,38 +282,60 @@ namespace Greenshot.Helpers {
#endregion VERSION 4
#region VERSION 5
else if (majorVersion == 5) {
if (productType == VER_NT_WORKSTATION) {
if ((suiteMask & VER_SUITE_PERSONAL) != 0) {
else if (majorVersion == 5)
{
if (productType == VER_NT_WORKSTATION)
{
if ((suiteMask & VER_SUITE_PERSONAL) != 0)
{
// Windows XP Home Edition
edition = "Home";
} else {
}
else
{
// Windows XP / Windows 2000 Professional
edition = "Professional";
}
} else if (productType == VER_NT_SERVER) {
if (minorVersion == 0) {
if ((suiteMask & VER_SUITE_DATACENTER) != 0) {
}
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) {
}
else if ((suiteMask & VER_SUITE_ENTERPRISE) != 0)
{
// Windows 2000 Advanced Server
edition = "Advanced Server";
} else {
}
else
{
// Windows 2000 Server
edition = "Server";
}
} else {
if ((suiteMask & VER_SUITE_DATACENTER) != 0) {
}
else
{
if ((suiteMask & VER_SUITE_DATACENTER) != 0)
{
// Windows Server 2003 Datacenter Edition
edition = "Datacenter";
} else if ((suiteMask & VER_SUITE_ENTERPRISE) != 0) {
}
else if ((suiteMask & VER_SUITE_ENTERPRISE) != 0)
{
// Windows Server 2003 Enterprise Edition
edition = "Enterprise";
} else if ((suiteMask & VER_SUITE_BLADE) != 0) {
}
else if ((suiteMask & VER_SUITE_BLADE) != 0)
{
// Windows Server 2003 Web Edition
edition = "Web Edition";
} else {
}
else
{
// Windows Server 2003 Standard Edition
edition = "Standard";
}
@ -267,10 +345,13 @@ namespace Greenshot.Helpers {
#endregion VERSION 5
#region VERSION 6
else if (majorVersion == 6) {
else if (majorVersion == 6)
{
int ed;
if (GetProductInfo(majorVersion, minorVersion, osVersionInfo.wServicePackMajor, osVersionInfo.wServicePackMinor, out ed)) {
switch (ed) {
if (GetProductInfo(majorVersion, minorVersion, osVersionInfo.wServicePackMajor, osVersionInfo.wServicePackMinor, out ed))
{
switch (ed)
{
case PRODUCT_BUSINESS:
edition = "Business";
break;
@ -399,9 +480,12 @@ namespace Greenshot.Helpers {
/// <summary>
/// Gets the name of the operating system running on this computer.
/// </summary>
static public string Name {
get {
if (s_Name != null) {
static public string Name
{
get
{
if (s_Name != null)
{
return s_Name; //***** RETURN *****//
}
@ -411,27 +495,37 @@ namespace Greenshot.Helpers {
OSVERSIONINFOEX osVersionInfo = new OSVERSIONINFOEX();
osVersionInfo.dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX));
if (GetVersionEx(ref osVersionInfo)) {
if (GetVersionEx(ref osVersionInfo))
{
int majorVersion = osVersion.Version.Major;
int minorVersion = osVersion.Version.Minor;
byte productType = osVersionInfo.wProductType;
short suiteMask = osVersionInfo.wSuiteMask;
switch (osVersion.Platform) {
switch (osVersion.Platform)
{
case PlatformID.Win32Windows:
if (majorVersion == 4) {
if (majorVersion == 4)
{
string csdVersion = osVersionInfo.szCSDVersion;
switch (minorVersion) {
switch (minorVersion)
{
case 0:
if (csdVersion == "B" || csdVersion == "C") {
if (csdVersion == "B" || csdVersion == "C")
{
name = "Windows 95 OSR2";
} else {
}
else
{
name = "Windows 95";
}
break;
case 10:
if (csdVersion == "A") {
if (csdVersion == "A")
{
name = "Windows 98 Second Edition";
} else {
}
else
{
name = "Windows 98";
}
break;
@ -442,12 +536,14 @@ namespace Greenshot.Helpers {
}
break;
case PlatformID.Win32NT:
switch (majorVersion) {
switch (majorVersion)
{
case 3:
name = "Windows NT 3.51";
break;
case 4:
switch (productType) {
switch (productType)
{
case 1:
name = "Windows NT 4.0";
break;
@ -457,12 +553,14 @@ namespace Greenshot.Helpers {
}
break;
case 5:
switch (minorVersion) {
switch (minorVersion)
{
case 0:
name = "Windows 2000";
break;
case 1:
switch (suiteMask) {
switch (suiteMask)
{
case 0x0200:
name = "Windows XP Professional";
break;
@ -472,7 +570,8 @@ namespace Greenshot.Helpers {
}
break;
case 2:
switch (suiteMask) {
switch (suiteMask)
{
case 0x0200:
name = "Windows XP Professional x64";
break;
@ -496,9 +595,11 @@ namespace Greenshot.Helpers {
}
break;
case 6:
switch (minorVersion) {
switch (minorVersion)
{
case 0:
switch (productType) {
switch (productType)
{
case 3:
name = "Windows Server 2008";
break;
@ -508,7 +609,8 @@ namespace Greenshot.Helpers {
}
break;
case 1:
switch (productType) {
switch (productType)
{
case 3:
name = "Windows Server 2008 R2";
break;
@ -520,8 +622,14 @@ namespace Greenshot.Helpers {
case 2:
name = "Windows 8";
break;
case 3:
name = "Windows 8.1";
break;
}
break;
case 10:
name = "Windows 10";
break;
}
break;
}
@ -553,7 +661,8 @@ namespace Greenshot.Helpers {
#region OSVERSIONINFOEX
[StructLayout(LayoutKind.Sequential)]
private struct OSVERSIONINFOEX {
private struct OSVERSIONINFOEX
{
public int dwOSVersionInfoSize;
public int dwMajorVersion;
public int dwMinorVersion;
@ -629,14 +738,17 @@ namespace Greenshot.Helpers {
/// <summary>
/// Gets the service pack information of the operating system running on this computer.
/// </summary>
static public string ServicePack {
get {
static public string ServicePack
{
get
{
string servicePack = String.Empty;
OSVERSIONINFOEX osVersionInfo = new OSVERSIONINFOEX();
osVersionInfo.dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX));
if (GetVersionEx(ref osVersionInfo)) {
if (GetVersionEx(ref osVersionInfo))
{
servicePack = osVersionInfo.szCSDVersion;
}
@ -650,8 +762,10 @@ namespace Greenshot.Helpers {
/// <summary>
/// Gets the build version number of the operating system running on this computer.
/// </summary>
public static int BuildVersion {
get {
public static int BuildVersion
{
get
{
return Environment.OSVersion.Version.Build;
}
}
@ -662,8 +776,10 @@ namespace Greenshot.Helpers {
/// <summary>
/// Gets the full version string of the operating system running on this computer.
/// </summary>
static public string VersionString {
get {
static public string VersionString
{
get
{
return string.Format("{0}.{1} build {3} revision {2:X}", Environment.OSVersion.Version.Major, Environment.OSVersion.Version.Minor, Environment.OSVersion.Version.Revision, Environment.OSVersion.Version.Build);
}
}
@ -673,8 +789,10 @@ namespace Greenshot.Helpers {
/// <summary>
/// Gets the full version of the operating system running on this computer.
/// </summary>
static public Version Version {
get {
static public Version Version
{
get
{
return Environment.OSVersion.Version;
}
}
@ -685,8 +803,10 @@ namespace Greenshot.Helpers {
/// <summary>
/// Gets the major version number of the operating system running on this computer.
/// </summary>
static public int MajorVersion {
get {
static public int MajorVersion
{
get
{
return Environment.OSVersion.Version.Major;
}
}
@ -696,8 +816,10 @@ namespace Greenshot.Helpers {
/// <summary>
/// Gets the minor version number of the operating system running on this computer.
/// </summary>
static public int MinorVersion {
get {
static public int MinorVersion
{
get
{
return Environment.OSVersion.Version.Minor;
}
}
@ -707,12 +829,14 @@ namespace Greenshot.Helpers {
/// <summary>
/// Gets the revision version number of the operating system running on this computer.
/// </summary>
static public int RevisionVersion {
get {
static public int RevisionVersion
{
get
{
return Environment.OSVersion.Version.Revision;
}
}
#endregion REVISION
#endregion VERSION
}
}
}

View file

@ -9,14 +9,16 @@
</asmv3:application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!--The ID below indicates app support for Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!--The ID below indicates app support for Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!--The ID below indicates app support for Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!--The ID below indicates app support for Windows 8.1 -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!--Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<!-- Set UAC level to "asInvoker" and disable registry virtualization -->

View file

@ -9,6 +9,26 @@ All details to our tickets can be found here: https://greenshot.atlassian.net
@DETAILVERSION@
Bugs Resolved:
* BUG-1809: OverflowException when changing the windows input language
* BUG-1835: Imgur: uploads were cancelled due to a timeout which was set too small
* BUG-1833: Imgur: API-Key issues when using anonymous uploads
* Github Pull 1: Fixed error calling external command. Thanks to viper3400
1.2.6.7-359dcf3 RELEASE
Bugs Resolved:
* BUG-1769: Switched to OAuth 2 for Picasa Authentication, OAuth 1.x will be terminated as of 20th of April 2015.
* BUG-1770: Fix problems when a font doesn't want to draw itself.
* Bug 1774: Problems logging in to Box.com
Features add:
* FEATURE-838: Added support for dragging an image from google image search results directly into the editor.
1.2.5.19-RELEASE-63412d3b9e29
Bugs Resolved:
* BUG-1578, BUG-1657: by default, Imgur plugin no longer passes title and filename to Imgur (for new installations, existing configuration files won't be affected)
* BUG-1655: Greenshot uses the default proxy, even if the "use default proxy" check-box is not checked.

View file

@ -18,9 +18,11 @@
* 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.Windows.Forms;
using Greenshot.IniFile;
using GreenshotPlugin.Core;
using System;
namespace GreenshotBoxPlugin {
/// <summary>
@ -38,10 +40,37 @@ namespace GreenshotBoxPlugin {
public bool AfterUploadLinkToClipBoard;
[IniProperty("UseSharedLink", Description = "Use the shared link, instead of the private, on the clipboard", DefaultValue = "True")]
public bool UseSharedLink;
public bool UseSharedLink {
get;
set;
}
[IniProperty("FolderId", Description = "Folder ID to upload to, only change if you know what you are doing!", DefaultValue = "0")]
public string FolderId {
get;
set;
}
[IniProperty("BoxToken", Description = "Token.", DefaultValue = "")]
public string BoxToken;
[IniProperty("RefreshToken", Description = "Box authorization refresh Token", Encrypted = true)]
public string RefreshToken {
get;
set;
}
/// <summary>
/// Not stored
/// </summary>
public string AccessToken {
get;
set;
}
/// <summary>
/// Not stored
/// </summary>
public DateTimeOffset AccessTokenExpires {
get;
set;
}
/// <summary>
/// A form for token

View file

@ -18,16 +18,14 @@
* 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 Greenshot.IniFile;
using GreenshotPlugin.Core;
using System.Collections.Generic;
using System.Drawing;
using System.Net;
using System.Text;
using Greenshot.IniFile;
using GreenshotPlugin.Controls;
using GreenshotPlugin.Core;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
namespace GreenshotBoxPlugin {
@ -37,101 +35,24 @@ namespace GreenshotBoxPlugin {
public static class BoxUtils {
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(BoxUtils));
private static readonly BoxConfiguration Config = IniConfig.GetIniSection<BoxConfiguration>();
private const string RedirectUri = "https://www.box.com/home/";
private const string UploadFileUri = "https://upload.box.com/api/2.0/files/content";
private const string AuthorizeUri = "https://www.box.com/api/oauth2/authorize";
private const string TokenUri = "https://www.box.com/api/oauth2/token";
private const string FilesUri = "https://www.box.com/api/2.0/files/{0}";
private static bool Authorize() {
string authorizeUrl = string.Format("{0}?client_id={1}&response_type=code&state=dropboxplugin&redirect_uri={2}", AuthorizeUri, BoxCredentials.ClientId, RedirectUri);
OAuthLoginForm loginForm = new OAuthLoginForm("Box Authorize", new Size(1060, 600), authorizeUrl, RedirectUri);
loginForm.ShowDialog();
if (!loginForm.isOk) {
return false;
}
var callbackParameters = loginForm.CallbackParameters;
if (callbackParameters == null || !callbackParameters.ContainsKey("code")) {
return false;
}
string authorizationResponse = PostAndReturn(new Uri(TokenUri), string.Format("grant_type=authorization_code&code={0}&client_id={1}&client_secret={2}", callbackParameters["code"], BoxCredentials.ClientId, BoxCredentials.ClientSecret));
var authorization = JsonSerializer.Deserialize<Authorization>(authorizationResponse);
Config.BoxToken = authorization.AccessToken;
IniConfig.Save();
return true;
}
/// <summary>
/// Download a url response as string
/// </summary>
/// <param name="url">An Uri to specify the download location</param>
/// <param name="postMessage"></param>
/// <returns>string with the file content</returns>
public static string PostAndReturn(Uri url, string postMessage) {
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url);
webRequest.Method = "POST";
webRequest.KeepAlive = true;
webRequest.Credentials = CredentialCache.DefaultCredentials;
webRequest.ContentType = "application/x-www-form-urlencoded";
byte[] data = Encoding.UTF8.GetBytes(postMessage);
using (var requestStream = webRequest.GetRequestStream()) {
requestStream.Write(data, 0, data.Length);
}
return NetworkHelper.GetResponse(webRequest);
}
/// <summary>
/// Upload parameters by post
/// </summary>
/// <param name="url"></param>
/// <param name="parameters"></param>
/// <returns>response</returns>
public static string HttpPost(string url, IDictionary<string, object> parameters) {
var webRequest = (HttpWebRequest)NetworkHelper.CreateWebRequest(url);
webRequest.Method = "POST";
webRequest.KeepAlive = true;
webRequest.Credentials = CredentialCache.DefaultCredentials;
webRequest.Headers.Add("Authorization", "Bearer " + Config.BoxToken);
NetworkHelper.WriteMultipartFormData(webRequest, parameters);
return NetworkHelper.GetResponse(webRequest);
}
/// <summary>
/// Upload file by PUT
/// Put string
/// </summary>
/// <param name="url"></param>
/// <param name="content"></param>
/// <param name="settings">OAuth2Settings</param>
/// <returns>response</returns>
public static string HttpPut(string url, string content) {
var webRequest = (HttpWebRequest)NetworkHelper.CreateWebRequest(url);
webRequest.Method = "PUT";
webRequest.KeepAlive = true;
webRequest.Credentials = CredentialCache.DefaultCredentials;
webRequest.Headers.Add("Authorization", "Bearer " + Config.BoxToken);
public static string HttpPut(string url, string content, OAuth2Settings settings) {
var webRequest= OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.PUT, url, settings);
byte[] data = Encoding.UTF8.GetBytes(content);
using (var requestStream = webRequest.GetRequestStream()) {
requestStream.Write(data, 0, data.Length);
}
return NetworkHelper.GetResponse(webRequest);
}
/// <summary>
/// Get REST request
/// </summary>
/// <param name="url"></param>
/// <returns>response</returns>
public static string HttpGet(string url) {
var webRequest = (HttpWebRequest)NetworkHelper.CreateWebRequest(url);
webRequest.Method = "GET";
webRequest.KeepAlive = true;
webRequest.Credentials = CredentialCache.DefaultCredentials;
webRequest.Headers.Add("Authorization", "Bearer " + Config.BoxToken);
return NetworkHelper.GetResponse(webRequest);
return NetworkHelper.GetResponseAsString(webRequest);
}
/// <summary>
@ -143,45 +64,52 @@ namespace GreenshotBoxPlugin {
/// <param name="filename">Filename of box upload</param>
/// <returns>url to uploaded image</returns>
public static string UploadToBox(SurfaceContainer image, string title, string filename) {
while (true) {
const string folderId = "0";
if (string.IsNullOrEmpty(Config.BoxToken)) {
if (!Authorize()) {
return null;
}
}
// Fill the OAuth2Settings
OAuth2Settings settings = new OAuth2Settings();
settings.AuthUrlPattern = "https://app.box.com/api/oauth2/authorize?client_id={ClientId}&response_type=code&state={State}&redirect_uri={RedirectUrl}";
settings.TokenUrl = "https://api.box.com/oauth2/token";
settings.CloudServiceName = "Box";
settings.ClientId = BoxCredentials.ClientId;
settings.ClientSecret = BoxCredentials.ClientSecret;
settings.RedirectUrl = "https://www.box.com/home/";
settings.BrowserSize = new Size(1060, 600);
settings.AuthorizeMode = OAuth2AuthorizeMode.EmbeddedBrowser;
// Copy the settings from the config, which is kept in memory and on the disk
settings.RefreshToken = Config.RefreshToken;
settings.AccessToken = Config.AccessToken;
settings.AccessTokenExpires = Config.AccessTokenExpires;
try {
var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, UploadFileUri, settings);
IDictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("filename", image);
parameters.Add("parent_id", folderId);
parameters.Add("file", image);
parameters.Add("parent_id", Config.FolderId);
var response = "";
try {
response = HttpPost(UploadFileUri, parameters);
} catch (WebException ex) {
if (ex.Status == WebExceptionStatus.ProtocolError) {
Config.BoxToken = null;
continue;
}
}
NetworkHelper.WriteMultipartFormData(webRequest, parameters);
var response = NetworkHelper.GetResponseAsString(webRequest);
LOG.DebugFormat("Box response: {0}", response);
// Check if the token is wrong
if ("wrong auth token".Equals(response)) {
Config.BoxToken = null;
IniConfig.Save();
continue;
}
var upload = JsonSerializer.Deserialize<Upload>(response);
if (upload == null || upload.Entries == null || upload.Entries.Count == 0) return null;
if (Config.UseSharedLink) {
string filesResponse = HttpPut(string.Format(FilesUri, upload.Entries[0].Id), "{\"shared_link\": {\"access\": \"open\"}}");
string filesResponse = HttpPut(string.Format(FilesUri, upload.Entries[0].Id), "{\"shared_link\": {\"access\": \"open\"}}", settings);
var file = JsonSerializer.Deserialize<FileEntry>(filesResponse);
return file.SharedLink.Url;
}
return string.Format("http://www.box.com/files/0/f/0/1/f_{0}", upload.Entries[0].Id);
} finally {
// Copy the settings back to the config, so they are stored.
Config.RefreshToken = settings.RefreshToken;
Config.AccessToken = settings.AccessToken;
Config.AccessTokenExpires = settings.AccessTokenExpires;
Config.IsDirty = true;
IniConfig.Save();
}
}
}

View file

@ -39,8 +39,6 @@ namespace GreenshotBoxPlugin {
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
this.Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
}
}
}

View file

@ -18,6 +18,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/>.
*/
using System;
using System.Windows.Forms;
using GreenshotDropboxPlugin.Forms;
@ -39,7 +40,6 @@ namespace GreenshotDropboxPlugin {
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
this.Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
}
}

View file

@ -121,7 +121,7 @@ namespace ExternalCommand {
if (config.OutputToClipboard) {
ClipboardHelper.SetClipboardData(output);
}
if (uriMatches != null && uriMatches.Count >= 0) {
if (uriMatches != null && uriMatches.Count > 0) {
exportInformation.Uri = uriMatches[0].Groups[1].Value;
LOG.InfoFormat("Got URI : {0} ", exportInformation.Uri);
if (config.UriToClipboard) {
@ -134,10 +134,10 @@ namespace ExternalCommand {
exportInformation.ExportMade = false;
exportInformation.ErrorMessage = error;
}
} catch (Exception ex) {
LOG.WarnFormat("Error calling external command: {0} ", exportInformation.ErrorMessage);
} catch (Exception ex) {
exportInformation.ExportMade = false;
exportInformation.ErrorMessage = ex.Message;
LOG.WarnFormat("Error calling external command: {0} ", exportInformation.ErrorMessage);
}
}

View file

@ -37,7 +37,6 @@ namespace ExternalCommand {
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
this.Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
AcceptButton = buttonOk;
CancelButton = buttonCancel;
UpdateView();

View file

@ -38,7 +38,6 @@ namespace ExternalCommand {
public SettingsFormDetail(string commando) {
InitializeComponent();
this.Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
AcceptButton = buttonOk;
CancelButton = buttonCancel;
this.commando = commando;

View file

@ -18,6 +18,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/>.
*/
using System;
using GreenshotPlugin.Controls;

View file

@ -37,7 +37,6 @@ namespace GreenshotFlickrPlugin {
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
Icon = GreenshotResources.getGreenshotIcon();
CancelButton = buttonCancel;
AcceptButton = buttonOK;
}

View file

@ -18,13 +18,15 @@
* 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 GreenshotPlugin.Controls;
namespace GreenshotImgurPlugin {
/// <summary>
/// This class is needed for design-time resolving of the language files
/// </summary>
public class ImgurForm : GreenshotPlugin.Controls.GreenshotForm {
public class ImgurForm : GreenshotForm {
public ImgurForm() : base() {
}
}

View file

@ -117,6 +117,7 @@ namespace GreenshotImgurPlugin
this.finishedButton.TabIndex = 11;
this.finishedButton.Text = "Finished";
this.finishedButton.UseVisualStyleBackColor = true;
this.finishedButton.Click += new System.EventHandler(this.FinishedButtonClick);
//
// clipboardButton
//

View file

@ -53,7 +53,6 @@ namespace GreenshotImgurPlugin {
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
this.Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
AcceptButton = finishedButton;
CancelButton = finishedButton;
// Init sorting
@ -168,6 +167,11 @@ namespace GreenshotImgurPlugin {
}
}
private void FinishedButtonClick(object sender, EventArgs e)
{
this.Hide();
}
private void OpenButtonClick(object sender, EventArgs e) {
if (listview_imgur_uploads.SelectedItems != null && listview_imgur_uploads.SelectedItems.Count > 0) {
for (int i = 0; i < listview_imgur_uploads.SelectedItems.Count; i++) {

View file

@ -35,7 +35,6 @@ namespace GreenshotImgurPlugin {
InitializeComponent();
CancelButton = buttonCancel;
AcceptButton = buttonOK;
this.Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
ImgurUtils.LoadHistory();

View file

@ -30,9 +30,11 @@ namespace GreenshotImgurPlugin {
/// </summary>
[IniSection("Imgur", Description="Greenshot Imgur Plugin configuration")]
public class ImgurConfiguration : IniSection {
[IniProperty("ImgurApiUrl", Description="Url to Imgur system.", DefaultValue="http://api.imgur.com/2")]
[IniProperty("ImgurApiUrl", Description="Url to Imgur system.", DefaultValue= "http://api.imgur.com/2")]
public string ImgurApiUrl;
[IniProperty("ImgurApi3Url", Description = "Url to Imgur system.", DefaultValue = "https://api.imgur.com/3")]
public string ImgurApi3Url;
[IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
public OutputFormat UploadFormat;
[IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]

View file

@ -143,10 +143,15 @@ namespace GreenshotImgurPlugin
try {
XmlDocument doc = new XmlDocument();
doc.LoadXml(response);
XmlNodeList nodes = doc.GetElementsByTagName("hash");
XmlNodeList nodes = doc.GetElementsByTagName("id");
if(nodes.Count > 0) {
imgurInfo.Hash = nodes.Item(0).InnerText;
}
nodes = doc.GetElementsByTagName("hash");
if (nodes.Count > 0)
{
imgurInfo.Hash = nodes.Item(0).InnerText;
}
nodes = doc.GetElementsByTagName("deletehash");
if(nodes.Count > 0) {
imgurInfo.DeleteHash = nodes.Item(0).InnerText;
@ -161,16 +166,41 @@ namespace GreenshotImgurPlugin
}
nodes = doc.GetElementsByTagName("datetime");
if(nodes.Count > 0) {
imgurInfo.Timestamp = DateTime.Parse(nodes.Item(0).InnerText);
try
{
imgurInfo.Timestamp = DateTime.Parse(nodes.Item(0).InnerText);
}
catch (Exception)
{
// Version 3 has seconds since Epoch
double secondsSince;
if (double.TryParse(nodes.Item(0).InnerText, out secondsSince))
{
var epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
imgurInfo.Timestamp = epoch.AddSeconds(secondsSince).DateTime;
}
}
}
nodes = doc.GetElementsByTagName("original");
if(nodes.Count > 0) {
imgurInfo.Original = nodes.Item(0).InnerText;
}
// Version 3 API only has Link
nodes = doc.GetElementsByTagName("link");
if (nodes.Count > 0)
{
imgurInfo.Original = nodes.Item(0).InnerText;
}
nodes = doc.GetElementsByTagName("imgur_page");
if(nodes.Count > 0) {
if (nodes.Count > 0)
{
imgurInfo.Page = nodes.Item(0).InnerText;
}
else
{
// Version 3 doesn't have a page link in the response
imgurInfo.Page = string.Format("http://imgur.com/{0}", imgurInfo.Hash);
}
nodes = doc.GetElementsByTagName("small_square");
if(nodes.Count > 0) {
imgurInfo.SmallSquare = nodes.Item(0).InnerText;

View file

@ -31,14 +31,15 @@ namespace GreenshotImgurPlugin {
/// <summary>
/// Description of ImgurUtils.
/// </summary>
public class ImgurUtils {
public static class ImgurUtils {
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ImgurUtils));
private const string IMGUR_ANONYMOUS_API_KEY = "8116a978913f3cf5dfc8e1117a055056";
private const string IMGUR_ANONYMOUS_API_KEY = "60e8838e21d6b66";
private const string SMALL_URL_PATTERN = "http://i.imgur.com/{0}s.png";
private static ImgurConfiguration config = IniConfig.GetIniSection<ImgurConfiguration>();
private ImgurUtils() {
}
/// <summary>
/// Load the complete history of the imgur uploads, with the corresponding information
/// </summary>
public static void LoadHistory() {
if (config.runtimeImgurHistory.Count == config.ImgurUploadHistory.Count) {
return;
@ -57,9 +58,9 @@ namespace GreenshotImgurPlugin {
continue;
}
try {
ImgurInfo imgurInfo = ImgurUtils.RetrieveImgurInfo(hash, config.ImgurUploadHistory[hash]);
ImgurInfo imgurInfo = RetrieveImgurInfo(hash, config.ImgurUploadHistory[hash]);
if (imgurInfo != null) {
ImgurUtils.RetrieveImgurThumbnail(imgurInfo);
RetrieveImgurThumbnail(imgurInfo);
config.runtimeImgurHistory.Add(hash, imgurInfo);
} else {
LOG.DebugFormat("Deleting not found ImgUr {0} from config.", hash);
@ -90,6 +91,14 @@ namespace GreenshotImgurPlugin {
}
}
/// <summary>
/// Use this to make sure Imgur knows from where the upload comes.
/// </summary>
/// <param name="webRequest"></param>
private static void SetClientId(HttpWebRequest webRequest) {
webRequest.Headers.Add("Authorization", "Client-ID " + IMGUR_ANONYMOUS_API_KEY);
}
/// <summary>
/// Do the actual upload to Imgur
/// For more details on the available parameters, see: http://api.imgur.com/resources_anon
@ -113,11 +122,12 @@ namespace GreenshotImgurPlugin {
string responseString = null;
if (config.AnonymousAccess) {
// add key, we only use the other parameters for the AnonymousAccess
otherParameters.Add("key", IMGUR_ANONYMOUS_API_KEY);
HttpWebRequest webRequest = (HttpWebRequest)NetworkHelper.CreateWebRequest(config.ImgurApiUrl + "/upload.xml?" + NetworkHelper.GenerateQueryParameters(otherParameters));
webRequest.Method = "POST";
webRequest.ContentType = "image/" + outputSettings.Format.ToString();
//otherParameters.Add("key", IMGUR_ANONYMOUS_API_KEY);
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(config.ImgurApi3Url + "/upload.xml?" + NetworkHelper.GenerateQueryParameters(otherParameters), HTTPMethod.POST);
webRequest.ContentType = "image/" + outputSettings.Format;
webRequest.ServicePoint.Expect100Continue = false;
SetClientId(webRequest);
try {
using (var requestStream = webRequest.GetRequestStream()) {
ImageOutput.SaveToStream(surfaceToUpload, requestStream, outputSettings);
@ -127,7 +137,7 @@ namespace GreenshotImgurPlugin {
using (StreamReader reader = new StreamReader(response.GetResponseStream(), true)) {
responseString = reader.ReadToEnd();
}
LogCredits(response);
LogRateLimitInfo(response);
}
} catch (Exception ex) {
LOG.Error("Upload to imgur gave an exeption: ", ex);
@ -174,34 +184,43 @@ namespace GreenshotImgurPlugin {
return ImgurInfo.ParseResponse(responseString);
}
/// <summary>
/// Retrieve the thumbnail of an imgur image
/// </summary>
/// <param name="imgurInfo"></param>
public static void RetrieveImgurThumbnail(ImgurInfo imgurInfo) {
if (imgurInfo.SmallSquare == null) {
LOG.Warn("Imgur URL was null, not retrieving thumbnail.");
return;
}
LOG.InfoFormat("Retrieving Imgur image for {0} with url {1}", imgurInfo.Hash, imgurInfo.SmallSquare);
HttpWebRequest webRequest = (HttpWebRequest)NetworkHelper.CreateWebRequest(imgurInfo.SmallSquare);
webRequest.Method = "GET";
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(string.Format(SMALL_URL_PATTERN, imgurInfo.Hash), HTTPMethod.GET);
webRequest.ServicePoint.Expect100Continue = false;
SetClientId(webRequest);
using (WebResponse response = webRequest.GetResponse()) {
LogCredits(response);
LogRateLimitInfo(response);
Stream responseStream = response.GetResponseStream();
imgurInfo.Image = Image.FromStream(responseStream);
}
return;
}
/// <summary>
/// Retrieve information on an imgur image
/// </summary>
/// <param name="hash"></param>
/// <param name="deleteHash"></param>
/// <returns>ImgurInfo</returns>
public static ImgurInfo RetrieveImgurInfo(string hash, string deleteHash) {
string url = config.ImgurApiUrl + "/image/" + hash;
LOG.InfoFormat("Retrieving Imgur info for {0} with url {1}", hash, url);
HttpWebRequest webRequest = (HttpWebRequest)NetworkHelper.CreateWebRequest(url);
webRequest.Method = "GET";
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.GET);
webRequest.ServicePoint.Expect100Continue = false;
SetClientId(webRequest);
string responseString;
try {
using (WebResponse response = webRequest.GetResponse()) {
LogCredits(response);
LogRateLimitInfo(response);
using (StreamReader reader = new StreamReader(response.GetResponseStream(), true)) {
responseString = reader.ReadToEnd();
}
@ -220,20 +239,21 @@ namespace GreenshotImgurPlugin {
return imgurInfo;
}
/// <summary>
/// Delete an imgur image, this is done by specifying the delete hash
/// </summary>
/// <param name="imgurInfo"></param>
public static void DeleteImgurImage(ImgurInfo imgurInfo) {
LOG.InfoFormat("Deleting Imgur image for {0}", imgurInfo.DeleteHash);
try {
string url = config.ImgurApiUrl + "/delete/" + imgurInfo.DeleteHash;
HttpWebRequest webRequest = (HttpWebRequest)NetworkHelper.CreateWebRequest(url);
//webRequest.Method = "DELETE";
webRequest.Method = "GET";
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.GET);
webRequest.ServicePoint.Expect100Continue = false;
SetClientId(webRequest);
string responseString;
using (WebResponse response = webRequest.GetResponse()) {
LogCredits(response);
LogRateLimitInfo(response);
using (StreamReader reader = new StreamReader(response.GetResponseStream(), true)) {
responseString = reader.ReadToEnd();
}
@ -252,17 +272,42 @@ namespace GreenshotImgurPlugin {
config.ImgurUploadHistory.Remove(imgurInfo.Hash);
imgurInfo.Image = null;
}
private static void LogCredits(WebResponse response) {
try {
int credits = 0;
if (int.TryParse(response.Headers["X-RateLimit-Remaining"], out credits)) {
config.Credits = credits;
/// <summary>
/// Helper for logging
/// </summary>
/// <param name="nameValues"></param>
/// <param name="key"></param>
private static void LogHeader(IDictionary<string, string> nameValues, string key) {
if (nameValues.ContainsKey(key)) {
LOG.InfoFormat("key={0}", nameValues[key]);
}
}
/// <summary>
/// Log the current rate-limit information
/// </summary>
/// <param name="response"></param>
private static void LogRateLimitInfo(WebResponse response) {
IDictionary<string, string> nameValues = new Dictionary<string, string>();
foreach (string key in response.Headers.AllKeys) {
if (!nameValues.ContainsKey(key)) {
nameValues.Add(key, response.Headers[key]);
}
LOG.InfoFormat("X-RateLimit-Limit={0}", response.Headers["X-RateLimit-Limit"]);
LOG.InfoFormat("X-RateLimit-Remaining={0}", response.Headers["X-RateLimit-Remaining"]);
} catch {}
}
LogHeader(nameValues, "X-RateLimit-Limit");
LogHeader(nameValues, "X-RateLimit-Remaining");
LogHeader(nameValues, "X-RateLimit-UserLimit");
LogHeader(nameValues, "X-RateLimit-UserRemaining");
LogHeader(nameValues, "X-RateLimit-UserReset");
LogHeader(nameValues, "X-RateLimit-ClientLimit");
LogHeader(nameValues, "X-RateLimit-ClientRemaining");
// Update the credits in the config, this is shown in a form
int credits = 0;
if (nameValues.ContainsKey("X-RateLimit-Remaining") && int.TryParse(nameValues["X-RateLimit-Remaining"], out credits)) {
config.Credits = credits;
}
}
}
}

View file

@ -34,7 +34,6 @@ namespace GreenshotJiraPlugin {
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
}
}
}

View file

@ -19,11 +19,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using GreenshotPlugin.Controls;
namespace GreenshotOCR {
/// <summary>
/// This class is needed for design-time resolving of the language files
/// </summary>
public class OCRForm : GreenshotPlugin.Controls.GreenshotForm {
public class OCRForm : GreenshotForm {
public OCRForm() : base() {
}
}

View file

@ -37,7 +37,6 @@ namespace GreenshotOCR {
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
this.Icon = GreenshotResources.getGreenshotIcon();
comboBox_languages.Items.Clear();
int index=0;

View file

@ -35,7 +35,6 @@ namespace GreenshotPhotobucketPlugin {
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
}
}
}

View file

@ -38,7 +38,6 @@ namespace GreenshotPicasaPlugin {
InitializeComponent();
CancelButton = buttonCancel;
AcceptButton = buttonOK;
Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
}
}

View file

@ -20,6 +20,7 @@
using System.Windows.Forms;
using Greenshot.IniFile;
using GreenshotPlugin.Core;
using System;
namespace GreenshotPicasaPlugin {
/// <summary>
@ -36,11 +37,45 @@ namespace GreenshotPicasaPlugin {
[IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Picasa link to clipboard.", DefaultValue = "true")]
public bool AfterUploadLinkToClipBoard;
[IniProperty("PicasaToken", Description = "Picasa Token", Encrypted = true)]
public string PicasaToken;
[IniProperty("AddFilename", Description = "Is the filename passed on to Picasa", DefaultValue = "False")]
public bool AddFilename {
get;
set;
}
[IniProperty("PicasaTokenSecret", Description = "PicasaTokenSecret", Encrypted = true)]
public string PicasaTokenSecret;
[IniProperty("UploadUser", Description = "The picasa user to upload to", DefaultValue = "default")]
public string UploadUser {
get;
set;
}
[IniProperty("UploadAlbum", Description = "The picasa album to upload to", DefaultValue = "default")]
public string UploadAlbum {
get;
set;
}
[IniProperty("RefreshToken", Description = "Picasa authorization refresh Token", Encrypted = true)]
public string RefreshToken {
get;
set;
}
/// <summary>
/// Not stored
/// </summary>
public string AccessToken {
get;
set;
}
/// <summary>
/// Not stored
/// </summary>
public DateTimeOffset AccessTokenExpires {
get;
set;
}
/// <summary>
/// A form for token

View file

@ -25,7 +25,7 @@ namespace GreenshotPicasaPlugin {
/// You can set your own values here
/// </summary>
public static class PicasaCredentials {
public static string ConsumerKey = "@credentials_picasa_consumer_key@";
public static string ConsumerSecret = "@credentials_picasa_consumer_secret@";
public static string ClientId = "@credentials_picasa_consumer_key@";
public static string ClientSecret = "@credentials_picasa_consumer_secret@";
}
}

View file

@ -17,25 +17,25 @@
* 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.Xml;
using Greenshot.IniFile;
using Greenshot.Plugin;
using GreenshotPlugin.Core;
using System;
using System.Net;
using System.Xml;
namespace GreenshotPicasaPlugin {
/// <summary>
/// Description of PicasaUtils.
/// </summary>
public class PicasaUtils {
private const string GoogleAccountUri = "https://www.google.com/accounts/";
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 PicasaUtils() {
}
private const string AuthUrl = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={ClientId}&redirect_uri={RedirectUrl}&state={State}&scope=" + PicasaScope;
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
@ -46,46 +46,46 @@ namespace GreenshotPicasaPlugin {
/// <param name="filename"></param>
/// <returns>PicasaResponse</returns>
public static string UploadToPicasa(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) {
OAuthSession oAuth = new OAuthSession(PicasaCredentials.ConsumerKey, PicasaCredentials.ConsumerSecret);
oAuth.BrowserSize = new Size(1020, 590);
oAuth.AccessTokenUrl = GoogleAccountUri + "OAuthGetAccessToken";
oAuth.AuthorizeUrl = GoogleAccountUri + "OAuthAuthorizeToken";
oAuth.RequestTokenUrl = GoogleAccountUri + "OAuthGetRequestToken";
oAuth.LoginTitle = "Picasa authorization";
oAuth.Token = Config.PicasaToken;
oAuth.TokenSecret = Config.PicasaTokenSecret;
oAuth.RequestTokenParameters.Add("scope", "https://picasaweb.google.com/data/");
oAuth.RequestTokenParameters.Add("xoauth_displayname", "Greenshot");
if (string.IsNullOrEmpty(oAuth.Token)) {
if (!oAuth.Authorize()) {
return null;
}
if (!string.IsNullOrEmpty(oAuth.Token)) {
Config.PicasaToken = oAuth.Token;
}
if (!string.IsNullOrEmpty(oAuth.TokenSecret)) {
Config.PicasaTokenSecret = oAuth.TokenSecret;
}
IniConfig.Save();
}
// Fill the OAuth2Settings
OAuth2Settings settings = new OAuth2Settings();
settings.AuthUrlPattern = AuthUrl;
settings.TokenUrl = TokenUrl;
settings.CloudServiceName = "Picasa";
settings.ClientId = PicasaCredentials.ClientId;
settings.ClientSecret = PicasaCredentials.ClientSecret;
settings.AuthorizeMode = OAuth2AuthorizeMode.LocalServer;
// Copy the settings from the config, which is kept in memory and on the disk
settings.RefreshToken = Config.RefreshToken;
settings.AccessToken = Config.AccessToken;
settings.AccessTokenExpires = Config.AccessTokenExpires;
try {
IDictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("slug", OAuthSession.UrlEncode3986(filename));
string response = oAuth.MakeOAuthRequest(HTTPMethod.POST, "https://picasaweb.google.com/data/feed/api/user/default/albumid/default", headers, null, null, new SurfaceContainer(surfaceToUpload, outputSettings, filename));
var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, string.Format(UploadUrl, Config.UploadUser, Config.UploadAlbum), settings);
if (Config.AddFilename) {
webRequest.Headers.Add("Slug", NetworkHelper.EscapeDataString(filename));
}
SurfaceContainer container = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
container.Upload(webRequest);
string response = NetworkHelper.GetResponseAsString(webRequest);
return ParseResponse(response);
} catch (Exception ex) {
LOG.Error("Upload error: ", ex);
throw;
} finally {
if (!string.IsNullOrEmpty(oAuth.Token)) {
Config.PicasaToken = oAuth.Token;
}
if (!string.IsNullOrEmpty(oAuth.TokenSecret)) {
Config.PicasaTokenSecret = oAuth.TokenSecret;
}
// Copy the settings back to the config, so they are stored.
Config.RefreshToken = settings.RefreshToken;
Config.AccessToken = settings.AccessToken;
Config.AccessTokenExpires = settings.AccessTokenExpires;
Config.IsDirty = true;
IniConfig.Save();
}
}
/// <summary>
/// Parse the upload URL from the response
/// </summary>
/// <param name="response"></param>
/// <returns></returns>
public static string ParseResponse(string response) {
if (response == null) {
return null;

View file

@ -18,17 +18,20 @@
* 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 GreenshotPlugin.Core;
using GreenshotPlugin.UnmanagedHelpers;
using Greenshot.IniFile;
using log4net;
namespace GreenshotPlugin.Controls {
/// <summary>
/// Extend this Form to have the possibility for animations on your form
/// </summary>
public class AnimatingForm : GreenshotForm {
private static readonly ILog LOG = LogManager.GetLogger(typeof(AnimatingForm));
private const int DEFAULT_VREFRESH = 60;
private int vRefresh = 0;
private Timer timer = null;
@ -110,7 +113,11 @@ namespace GreenshotPlugin.Controls {
/// <param name="sender"></param>
/// <param name="e"></param>
void timer_Tick(object sender, EventArgs e) {
Animate();
try {
Animate();
} catch (Exception ex) {
LOG.Warn("An exception occured while animating:", ex);
}
}
/// <summary>

View file

@ -38,11 +38,11 @@ namespace GreenshotPlugin.Controls {
protected static CoreConfiguration coreConfiguration;
private static IDictionary<Type, FieldInfo[]> reflectionCache = new Dictionary<Type, FieldInfo[]>();
private IComponentChangeService m_changeService;
private bool isDesignModeLanguageSet = false;
private bool applyLanguageManually = false;
private bool storeFieldsManually = false;
private IDictionary<string, Control> designTimeControls;
private IDictionary<string, ToolStripItem> designTimeToolStripItems;
private bool _isDesignModeLanguageSet = false;
private bool _applyLanguageManually = false;
private bool _storeFieldsManually = false;
private IDictionary<string, Control> _designTimeControls;
private IDictionary<string, ToolStripItem> _designTimeToolStripItems;
static GreenshotForm() {
if (!IsInDesignMode) {
@ -68,29 +68,37 @@ namespace GreenshotPlugin.Controls {
protected bool ManualLanguageApply {
get {
return applyLanguageManually;
return _applyLanguageManually;
}
set {
applyLanguageManually = value;
_applyLanguageManually = value;
}
}
protected bool ManualStoreFields {
get {
return storeFieldsManually;
return _storeFieldsManually;
}
set {
storeFieldsManually = value;
_storeFieldsManually = value;
}
}
/// <summary>
/// When this is set, the form will be brought to the foreground as soon as it is shown.
/// </summary>
protected bool ToFront {
get;
set;
}
/// <summary>
/// Code to initialize the language etc during design time
/// </summary>
protected void InitializeForDesigner() {
if (DesignMode) {
designTimeControls = new Dictionary<string, Control>();
designTimeToolStripItems = new Dictionary<string, ToolStripItem>();
_designTimeControls = new Dictionary<string, Control>();
_designTimeToolStripItems = new Dictionary<string, ToolStripItem>();
try {
ITypeResolutionService typeResService = GetService(typeof(ITypeResolutionService)) as ITypeResolutionService;
@ -119,8 +127,8 @@ namespace GreenshotPlugin.Controls {
/// <param name="e"></param>
protected override void OnPaint(PaintEventArgs e) {
if (DesignMode) {
if (!isDesignModeLanguageSet) {
isDesignModeLanguageSet = true;
if (!_isDesignModeLanguageSet) {
_isDesignModeLanguageSet = true;
try {
ApplyLanguage();
} catch (Exception) {
@ -131,8 +139,11 @@ namespace GreenshotPlugin.Controls {
}
protected override void OnLoad(EventArgs e) {
// Every GreenshotForm should have it's default icon
// And it might not ne needed for a Tool Window, but still for the task manager / switcher it's important
Icon = GreenshotResources.getGreenshotIcon();
if (!DesignMode) {
if (!applyLanguageManually) {
if (!_applyLanguageManually) {
ApplyLanguage();
}
FillFields();
@ -145,12 +156,23 @@ namespace GreenshotPlugin.Controls {
}
}
/// <summary>
/// Make sure the form is visible, if this is wanted
/// </summary>
/// <param name="e">EventArgs</param>
protected override void OnShown(EventArgs e) {
base.OnShown(e);
if (ToFront) {
WindowDetails.ToForeground(Handle);
}
}
/// <summary>
/// check if the form was closed with an OK, if so store the values in the GreenshotControls
/// </summary>
/// <param name="e"></param>
protected override void OnClosed(EventArgs e) {
if (!DesignMode && !storeFieldsManually) {
if (!DesignMode && !_storeFieldsManually) {
if (DialogResult == DialogResult.OK) {
LOG.Info("Form was closed with OK: storing field values.");
StoreFields();
@ -230,17 +252,17 @@ namespace GreenshotPlugin.Controls {
if (ce.Component != null && ((IComponent)ce.Component).Site != null) {
Control control = ce.Component as Control;
if (control != null) {
if (!designTimeControls.ContainsKey(control.Name)) {
designTimeControls.Add(control.Name, control);
if (!_designTimeControls.ContainsKey(control.Name)) {
_designTimeControls.Add(control.Name, control);
} else {
designTimeControls[control.Name] = control;
_designTimeControls[control.Name] = control;
}
} else if (ce.Component is ToolStripItem) {
ToolStripItem item = ce.Component as ToolStripItem;
if (!designTimeControls.ContainsKey(item.Name)) {
designTimeToolStripItems.Add(item.Name, item);
if (!_designTimeControls.ContainsKey(item.Name)) {
_designTimeToolStripItems.Add(item.Name, item);
} else {
designTimeToolStripItems[item.Name] = item;
_designTimeToolStripItems[item.Name] = item;
}
}
}
@ -360,10 +382,10 @@ namespace GreenshotPlugin.Controls {
}
if (DesignMode) {
foreach (Control designControl in designTimeControls.Values) {
foreach (Control designControl in _designTimeControls.Values) {
ApplyLanguage(designControl);
}
foreach (ToolStripItem designToolStripItem in designTimeToolStripItems.Values) {
foreach (ToolStripItem designToolStripItem in _designTimeToolStripItems.Values) {
ApplyLanguage(designToolStripItem);
}
}
@ -413,7 +435,7 @@ namespace GreenshotPlugin.Controls {
if (section != null) {
IniValue iniValue = null;
if (!section.Values.TryGetValue(configBindable.PropertyName, out iniValue)) {
LOG.WarnFormat("Wrong property '{0}' configured for field '{1}'",configBindable.PropertyName,field.Name);
LOG.DebugFormat("Wrong property '{0}' configured for field '{1}'",configBindable.PropertyName,field.Name);
continue;
}

View file

@ -44,37 +44,37 @@ namespace GreenshotPlugin.Controls {
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.addressTextBox = new System.Windows.Forms.TextBox();
this.browser = new ExtendedWebBrowser();
this._addressTextBox = new System.Windows.Forms.TextBox();
this._browser = new ExtendedWebBrowser();
this.SuspendLayout();
//
// addressTextBox
// _addressTextBox
//
this.addressTextBox.Cursor = System.Windows.Forms.Cursors.Arrow;
this.addressTextBox.Dock = System.Windows.Forms.DockStyle.Top;
this.addressTextBox.Enabled = false;
this.addressTextBox.Location = new System.Drawing.Point(0, 0);
this.addressTextBox.Name = "addressTextBox";
this.addressTextBox.Size = new System.Drawing.Size(595, 20);
this.addressTextBox.TabIndex = 3;
this.addressTextBox.TabStop = false;
this._addressTextBox.Cursor = System.Windows.Forms.Cursors.Arrow;
this._addressTextBox.Dock = System.Windows.Forms.DockStyle.Top;
this._addressTextBox.Enabled = false;
this._addressTextBox.Location = new System.Drawing.Point(0, 0);
this._addressTextBox.Name = "addressTextBox";
this._addressTextBox.Size = new System.Drawing.Size(595, 20);
this._addressTextBox.TabIndex = 3;
this._addressTextBox.TabStop = false;
//
// browser
// _browser
//
this.browser.Dock = System.Windows.Forms.DockStyle.Fill;
this.browser.Location = new System.Drawing.Point(0, 20);
this.browser.MinimumSize = new System.Drawing.Size(100, 100);
this.browser.Name = "browser";
this.browser.Size = new System.Drawing.Size(595, 295);
this.browser.TabIndex = 4;
this._browser.Dock = System.Windows.Forms.DockStyle.Fill;
this._browser.Location = new System.Drawing.Point(0, 20);
this._browser.MinimumSize = new System.Drawing.Size(100, 100);
this._browser.Name = "browser";
this._browser.Size = new System.Drawing.Size(595, 295);
this._browser.TabIndex = 4;
//
// OAuthLoginForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(595, 315);
this.Controls.Add(this.browser);
this.Controls.Add(this.addressTextBox);
this.Controls.Add(this._browser);
this.Controls.Add(this._addressTextBox);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "OAuthLoginForm";
@ -85,8 +85,8 @@ namespace GreenshotPlugin.Controls {
#endregion
private System.Windows.Forms.TextBox addressTextBox;
private ExtendedWebBrowser browser;
private System.Windows.Forms.TextBox _addressTextBox;
private ExtendedWebBrowser _browser;
}
}

View file

@ -35,62 +35,74 @@ namespace GreenshotPlugin.Controls {
/// </summary>
public partial class OAuthLoginForm : Form {
private static readonly ILog LOG = LogManager.GetLogger(typeof(OAuthLoginForm));
private string callbackUrl = null;
private IDictionary<string, string> callbackParameters = null;
private string _callbackUrl = null;
private IDictionary<string, string> _callbackParameters = null;
public IDictionary<string, string> CallbackParameters {
get { return callbackParameters; }
get {
return _callbackParameters;
}
}
public bool isOk {
public bool IsOk {
get {
return DialogResult == DialogResult.OK;
}
}
public OAuthLoginForm(string browserTitle, Size size, string authorizationLink, string callbackUrl) {
this.callbackUrl = callbackUrl;
_callbackUrl = callbackUrl;
InitializeComponent();
ClientSize = size;
Icon = GreenshotResources.getGreenshotIcon();
Text = browserTitle;
addressTextBox.Text = authorizationLink;
_addressTextBox.Text = authorizationLink;
// The script errors are suppressed by using the ExtendedWebBrowser
browser.ScriptErrorsSuppressed = false;
browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(browser_DocumentCompleted);
browser.Navigate(new Uri(authorizationLink));
_browser.ScriptErrorsSuppressed = false;
_browser.DocumentCompleted += Browser_DocumentCompleted;
_browser.Navigated += Browser_Navigated;
_browser.Navigating += Browser_Navigating;
_browser.Navigate(new Uri(authorizationLink));
}
/// <summary>
/// Make sure the form is visible
/// </summary>
/// <param name="e">EventArgs</param>
protected override void OnShown(EventArgs e) {
base.OnShown(e);
WindowDetails.ToForeground(Handle);
}
private void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
LOG.DebugFormat("document completed with url: {0}", browser.Url);
checkUrl();
}
private void webBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e) {
LOG.DebugFormat("Navigating to url: {0}", browser.Url);
addressTextBox.Text = e.Url.ToString();
private void Browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
LOG.DebugFormat("document completed with url: {0}", _browser.Url);
CheckUrl();
}
private void browser_Navigated(object sender, WebBrowserNavigatedEventArgs e) {
LOG.DebugFormat("Navigated to url: {0}", browser.Url);
checkUrl();
private void Browser_Navigating(object sender, WebBrowserNavigatingEventArgs e) {
LOG.DebugFormat("Navigating to url: {0}", _browser.Url);
_addressTextBox.Text = e.Url.ToString();
}
private void checkUrl() {
if (browser.Url.ToString().StartsWith(callbackUrl)) {
string queryParams = browser.Url.Query;
private void Browser_Navigated(object sender, WebBrowserNavigatedEventArgs e) {
LOG.DebugFormat("Navigated to url: {0}", _browser.Url);
CheckUrl();
}
private void CheckUrl() {
if (_browser.Url.ToString().StartsWith(_callbackUrl)) {
string queryParams = _browser.Url.Query;
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;
}
}
private void addressTextBox_KeyPress(object sender, KeyPressEventArgs e) {
private void AddressTextBox_KeyPress(object sender, KeyPressEventArgs e) {
//Cancel the key press so the user can't enter a new url
e.Handled = true;
}

View file

@ -41,14 +41,13 @@ namespace GreenshotPlugin.Controls {
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
Icon = GreenshotResources.getGreenshotIcon();
checkBox_reduceColors.Checked = Settings.ReduceColors;
trackBarJpegQuality.Enabled = OutputFormat.jpg.Equals(outputSettings.Format);
trackBarJpegQuality.Value = Settings.JPGQuality;
textBoxJpegQuality.Enabled = OutputFormat.jpg.Equals(outputSettings.Format);
textBoxJpegQuality.Text = Settings.JPGQuality.ToString();
WindowDetails.ToForeground(Handle);
ToFront = true;
}
void Button_okClick(object sender, EventArgs e) {

View file

@ -291,7 +291,7 @@ namespace GreenshotPlugin.Core {
}
}
[IniProperty("WebRequestTimeout", Description = "The connect timeout value for webrequets, these are seconds", DefaultValue = "10")]
[IniProperty("WebRequestTimeout", Description = "The connect timeout value for webrequets, these are seconds", DefaultValue = "100")]
public int WebRequestTimeout;
[IniProperty("WebRequestReadWriteTimeout", Description = "The read/write timeout value for webrequets, these are seconds", DefaultValue = "100")]
public int WebRequestReadWriteTimeout;
@ -407,6 +407,17 @@ namespace GreenshotPlugin.Core {
return base.PreCheckValue(propertyName, propertyValue);
}
/// <summary>
/// This method will be called before writing the configuration
/// </summary>
public override void BeforeSave() {
try {
// Store version, this can be used later to fix settings after an update
LastSaveWithVersion = Assembly.GetEntryAssembly().GetName().Version.ToString();
} catch {
}
}
/// <summary>
/// This method will be called after reading the configuration, so eventually some corrections can be made
/// </summary>
@ -498,8 +509,8 @@ namespace GreenshotPlugin.Core {
OutputFileReduceColorsTo = 256;
}
if (WebRequestTimeout < 1) {
WebRequestTimeout = 10;
if (WebRequestTimeout <= 10) {
WebRequestTimeout = 100;
}
if (WebRequestReadWriteTimeout < 1) {
WebRequestReadWriteTimeout = 100;

View file

@ -33,6 +33,17 @@ using System.Text;
using System.Text.RegularExpressions;
namespace GreenshotPlugin.Core {
/// <summary>
/// HTTP Method to make sure we have the correct method
/// </summary>
public enum HTTPMethod {
GET,
POST,
PUT,
DELETE,
HEAD
};
/// <summary>
/// Description of NetworkHelper.
/// </summary>
@ -54,11 +65,7 @@ namespace GreenshotPlugin.Core {
/// <param name="uri">An Uri to specify the download location</param>
/// <returns>string with the file content</returns>
public static string GetAsString(Uri uri) {
HttpWebRequest webRequest = CreateWebRequest(uri);
webRequest.Method = "GET";
webRequest.KeepAlive = true;
webRequest.Credentials = CredentialCache.DefaultCredentials;
return GetResponse(webRequest);
return GetResponseAsString(CreateWebRequest(uri));
}
/// <summary>
@ -86,6 +93,26 @@ namespace GreenshotPlugin.Core {
return null;
}
/// <summary>
/// Download the uri into a memorystream, without catching exceptions
/// </summary>
/// <param name="url">Of an image</param>
/// <returns>MemoryStream which is already seeked to 0</returns>
public static MemoryStream GetAsMemoryStream(string url) {
HttpWebRequest request = CreateWebRequest(url);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) {
MemoryStream memoryStream = new MemoryStream();
using (Stream responseStream = response.GetResponseStream()) {
if (responseStream != null) {
responseStream.CopyTo(memoryStream);
}
// Make sure it can be used directly
memoryStream.Seek(0, SeekOrigin.Begin);
}
return memoryStream;
}
}
/// <summary>
/// Download the uri to Bitmap
/// </summary>
@ -93,15 +120,27 @@ namespace GreenshotPlugin.Core {
/// <returns>Bitmap</returns>
public static Image DownloadImage(string url) {
try {
HttpWebRequest request = CreateWebRequest(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
if (request.HaveResponse) {
using (Stream responseStream = response.GetResponseStream()) {
if (responseStream != null) {
using (Image image = Image.FromStream(responseStream)) {
return ImageHelper.Clone(image, PixelFormat.Format32bppArgb);
string content;
using (MemoryStream memoryStream = GetAsMemoryStream(url)) {
try {
using (Image image = Image.FromStream(memoryStream)) {
return ImageHelper.Clone(image, PixelFormat.Format32bppArgb);
}
} catch (Exception) {
// If we arrive here, the image loading didn't work, try to see if the response has a http(s) URL to an image and just take this instead.
using (StreamReader streamReader = new StreamReader(memoryStream, Encoding.UTF8, true)) {
content = streamReader.ReadLine();
}
Regex imageUrlRegex = new Regex(@"(http|https)://.*(\.png|\.gif|\.jpg|\.tiff|\.jpeg|\.bmp)");
Match match = imageUrlRegex.Match(content);
if (match.Success) {
using (MemoryStream memoryStream2 = GetAsMemoryStream(match.Value)) {
using (Image image = Image.FromStream(memoryStream2)) {
return ImageHelper.Clone(image, PixelFormat.Format32bppArgb);
}
}
}
throw;
}
}
} catch (Exception e) {
@ -111,14 +150,36 @@ namespace GreenshotPlugin.Core {
}
/// <summary>
/// Helper method to create a web request, eventually with proxy
/// Helper method to create a web request with a lot of default settings
/// </summary>
/// <param name="uri">string with uri to connect to</param>
/// <returns>WebRequest</returns>
public static HttpWebRequest CreateWebRequest(string uri) {
return CreateWebRequest(new Uri(uri));
}
/// <summary>
/// Helper method to create a web request with a lot of default settings
/// </summary>
/// <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) {
return CreateWebRequest(new Uri(uri), method);
}
/// <summary>
/// Helper method to create a web request with a lot of default settings
/// </summary>
/// <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) {
HttpWebRequest webRequest = CreateWebRequest(uri);
webRequest.Method = method.ToString();
return webRequest;
}
/// <summary>
/// Helper method to create a web request, eventually with proxy
/// </summary>
@ -132,6 +193,10 @@ namespace GreenshotPlugin.Core {
// BUG-1655: Fix that Greenshot always uses the default proxy even if the "use default proxy" checkbox is unset
webRequest.Proxy = null;
}
// Make sure the default credentials are available
webRequest.Credentials = CredentialCache.DefaultCredentials;
// Allow redirect, this is usually needed so that we don't get a problem when a service moves
webRequest.AllowAutoRedirect = true;
// Set default timeouts
webRequest.Timeout = Config.WebRequestTimeout*1000;
@ -320,19 +385,57 @@ namespace GreenshotPlugin.Core {
string footer = "\r\n--" + boundary + "--\r\n";
formDataStream.Write(Encoding.UTF8.GetBytes(footer), 0, Encoding.UTF8.GetByteCount(footer));
}
/// <summary>
/// Post the parameters "x-www-form-urlencoded"
/// </summary>
/// <param name="webRequest"></param>
public static void UploadFormUrlEncoded(HttpWebRequest webRequest, IDictionary<string, object> parameters) {
webRequest.ContentType = "application/x-www-form-urlencoded";
string urlEncoded = NetworkHelper.GenerateQueryParameters(parameters);
byte[] data = Encoding.UTF8.GetBytes(urlEncoded);
using (var requestStream = webRequest.GetRequestStream()) {
requestStream.Write(data, 0, data.Length);
}
}
/// <summary>
/// Log the headers of the WebResponse, if IsDebugEnabled
/// </summary>
/// <param name="response">WebResponse</param>
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) {
LOG.DebugFormat("Reponse-header: {0}={1}", key, response.Headers[key]);
}
}
/// <summary>
/// Process the web response.
/// </summary>
/// <param name="webRequest">The request object.</param>
/// <returns>The response data.</returns>
/// TODO: This method should handle the StatusCode better!
public static string GetResponse(HttpWebRequest webRequest) {
public static string GetResponseAsString(HttpWebRequest webRequest) {
return GetResponseAsString(webRequest, false);
}
/// <summary>
///
/// </summary>
/// <param name="webRequest"></param>
/// <returns></returns>
public static string GetResponseAsString(HttpWebRequest webRequest, bool alsoReturnContentOnError) {
string responseData = null;
try {
HttpWebResponse response = (HttpWebResponse) webRequest.GetResponse();
LOG.InfoFormat("Response status: {0}", response.StatusCode);
bool isHttpError = (int) response.StatusCode >= 300;
DebugHeaders(response);
Stream responseStream = response.GetResponseStream();
if (responseStream != null) {
using (StreamReader reader = new StreamReader(responseStream, true)) {
@ -348,7 +451,13 @@ namespace GreenshotPlugin.Core {
LOG.ErrorFormat("HTTP error {0}", response.StatusCode);
using (Stream responseStream = response.GetResponseStream()) {
if (responseStream != null) {
LOG.ErrorFormat("Content: {0}", new StreamReader(responseStream, true).ReadToEnd());
using (StreamReader streamReader = new StreamReader(responseStream, true)) {
string errorContent = streamReader.ReadToEnd();
if (alsoReturnContentOnError) {
return errorContent;
}
LOG.ErrorFormat("Content: {0}", errorContent);
}
}
}
}
@ -367,7 +476,7 @@ namespace GreenshotPlugin.Core {
public static DateTime GetLastModified(Uri uri) {
try {
HttpWebRequest webRequest = CreateWebRequest(uri);
webRequest.Method = "HEAD";
webRequest.Method = HTTPMethod.HEAD.ToString();
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
LOG.DebugFormat("RSS feed was updated at {0}", webResponse.LastModified);
return webResponse.LastModified;

View file

@ -19,30 +19,198 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using GreenshotPlugin.Controls;
using log4net;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using GreenshotPlugin.Controls;
using System.Security.Cryptography.X509Certificates;
using log4net;
namespace GreenshotPlugin.Core {
/// <summary>
/// Provides a predefined set of algorithms that are supported officially by the protocol
/// Provides a predefined set of algorithms that are supported officially by the OAuth 1.x protocol
/// </summary>
public enum OAuthSignatureTypes {
HMACSHA1,
PLAINTEXT,
RSASHA1
}
public enum HTTPMethod { GET, POST, PUT, DELETE };
/// <summary>
/// Specify the autorize 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
MonitorTitle, // Not implemented yet: Will monitor for title changes
Pin, // Not implemented yet: Will ask the user to enter the shown PIN
EmbeddedBrowser // Will open into an embedded _browser (OAuthLoginForm), and catch the redirect
}
/// <summary>
/// Settings for the OAuth 2 protocol
/// </summary>
public class OAuth2Settings {
public OAuth2Settings() {
AdditionalAttributes = new Dictionary<string, string>();
// Create a default state
State = Guid.NewGuid().ToString();
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 autorization 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 {
get {
return 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 {
bool expired = true;
if (!string.IsNullOrEmpty(AccessToken) && AccessTokenExpires != null) {
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;
}
}
/// <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";
@ -102,7 +270,7 @@ namespace GreenshotPlugin.Core {
private readonly string _consumerKey;
private readonly string _consumerSecret;
// default browser size
// default _browser size
private Size _browserSize = new Size(864, 587);
private string _loginTitle = "Authorize Greenshot access";
@ -339,7 +507,7 @@ namespace GreenshotPlugin.Core {
LOG.DebugFormat("Opening AuthorizationLink: {0}", AuthorizationLink);
OAuthLoginForm oAuthLoginForm = new OAuthLoginForm(LoginTitle, BrowserSize, AuthorizationLink, CallbackUrl);
oAuthLoginForm.ShowDialog();
if (oAuthLoginForm.isOk) {
if (oAuthLoginForm.IsOk) {
if (oAuthLoginForm.CallbackParameters != null) {
string tokenValue;
if (oAuthLoginForm.CallbackParameters.TryGetValue(OAUTH_TOKEN_KEY, out tokenValue)) {
@ -672,11 +840,9 @@ namespace GreenshotPlugin.Core {
}
}
// Create webrequest
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(requestURL);
webRequest.Method = method.ToString();
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(requestURL, method);
webRequest.ServicePoint.Expect100Continue = false;
webRequest.UserAgent = _userAgent;
webRequest.Timeout = 100000;
if (UseHTTPHeadersForAuthorization && authHeader != null) {
LOG.DebugFormat("Authorization: OAuth {0}", authHeader);
@ -720,7 +886,7 @@ namespace GreenshotPlugin.Core {
string responseData;
try {
responseData = NetworkHelper.GetResponse(webRequest);
responseData = NetworkHelper.GetResponseAsString(webRequest);
LOG.DebugFormat("Response: {0}", responseData);
} catch (Exception ex) {
LOG.Error("Couldn't retrieve response: ", ex);
@ -732,4 +898,417 @@ namespace GreenshotPlugin.Core {
return responseData;
}
}
/// <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);
private string _loopbackCallback = "http://localhost:{0}/authorize/";
/// <summary>
/// The call back format. Expects one port parameter.
/// Default: http://localhost:{0}/authorize/
/// </summary>
public string LoopbackCallbackUrl {
get {
return _loopbackCallback;
}
set {
_loopbackCallback = value;
}
}
private string _closePageResponse =
@"<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>";
/// <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 {
return _closePageResponse;
}
set {
_closePageResponse = value;
}
}
private string _redirectUri;
/// <summary>
/// The URL to redirect to
/// </summary>
protected string RedirectUri {
get {
if (!string.IsNullOrEmpty(_redirectUri)) {
return _redirectUri;
}
return _redirectUri = string.Format(_loopbackCallback, GetRandomUnusedPort());
}
}
private string _cloudServiceName;
private IDictionary<string, string> _returnValues = new Dictionary<string, string>();
/// <summary>
/// The OAuth code receiver
/// </summary>
/// <param name="authorizationUrl"></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(new AsyncCallback(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 = System.Text.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();
}
}
}
/// <summary>
/// Code to simplify OAuth 2
/// </summary>
public static class OAuth2Helper {
private const string REFRESH_TOKEN = "refresh_token";
private const string ACCESS_TOKEN = "access_token";
private const string CODE = "code";
private const string CLIENT_ID = "client_id";
private const string CLIENT_SECRET = "client_secret";
private const string GRANT_TYPE = "grant_type";
private const string AUTHORIZATION_CODE = "authorization_code";
private const string REDIRECT_URI = "redirect_uri";
private const string EXPIRES_IN = "expires_in";
/// <summary>
/// Generate an OAuth 2 Token by using the supplied code
/// </summary>
/// <param name="code">Code to get the RefreshToken</param>
/// <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
data.Add(CODE, settings.Code);
data.Add(CLIENT_ID, settings.ClientId);
data.Add(REDIRECT_URI, settings.RedirectUrl);
data.Add(CLIENT_SECRET, settings.ClientSecret);
data.Add(GRANT_TYPE, AUTHORIZATION_CODE);
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(string.Format("{0} - {1}", refreshTokenResult["error"], refreshTokenResult["error_description"]));
} else {
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"
settings.AccessToken = (string)refreshTokenResult[ACCESS_TOKEN] as string;
settings.RefreshToken = (string)refreshTokenResult[REFRESH_TOKEN] as string;
object seconds = refreshTokenResult[EXPIRES_IN];
if (seconds != null) {
settings.AccessTokenExpires = DateTimeOffset.Now.AddSeconds((double)seconds);
}
settings.Code = null;
}
/// <summary>
/// Go out and retrieve a new access token via refresh-token with the TokenUrl in the settings
/// Will upate the access token, refresh token, expire date
/// </summary>
/// <param name="settings"></param>
public static void GenerateAccessToken(OAuth2Settings settings) {
IDictionary<string, object> data = new Dictionary<string, object>();
data.Add(REFRESH_TOKEN, settings.RefreshToken);
data.Add(CLIENT_ID, settings.ClientId);
data.Add(CLIENT_SECRET, settings.ClientSecret);
data.Add(GRANT_TYPE, REFRESH_TOKEN);
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;
} else {
if (accessTokenResult.ContainsKey("error_description")) {
throw new Exception(string.Format("{0} - {1}", accessTokenResult["error"], accessTokenResult["error_description"]));
} else {
throw new Exception((string)accessTokenResult["error"]);
}
}
}
settings.AccessToken = (string)accessTokenResult[ACCESS_TOKEN] as string;
if (accessTokenResult.ContainsKey(REFRESH_TOKEN)) {
// Refresh the refresh token :)
settings.RefreshToken = (string)accessTokenResult[REFRESH_TOKEN] as string;
}
object seconds = accessTokenResult[EXPIRES_IN];
if (seconds != null) {
settings.AccessTokenExpires = DateTimeOffset.Now.AddSeconds((double)seconds);
} else {
settings.AccessTokenExpires = DateTimeOffset.MaxValue;
}
}
/// <summary>
/// Authenticate 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 Authenticate(OAuth2Settings settings) {
bool completed = true;
switch (settings.AuthorizeMode) {
case OAuth2AuthorizeMode.LocalServer:
completed = AuthenticateViaLocalServer(settings);
break;
case OAuth2AuthorizeMode.EmbeddedBrowser:
completed = AuthenticateViaEmbeddedBrowser(settings);
break;
default:
throw new NotImplementedException(string.Format("Authorize mode '{0}' is not 'yet' implemented.", settings.AuthorizeMode));
}
return completed;
}
/// <summary>
/// Authenticate 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 AuthenticateViaEmbeddedBrowser(OAuth2Settings settings) {
if (string.IsNullOrEmpty(settings.CloudServiceName)) {
throw new ArgumentNullException("CloudServiceName");
}
if (settings.BrowserSize == Size.Empty) {
throw new ArgumentNullException("BrowserSize");
}
OAuthLoginForm loginForm = new OAuthLoginForm(string.Format("Authorize {0}", settings.CloudServiceName), settings.BrowserSize, settings.FormattedAuthUrl, settings.RedirectUrl);
loginForm.ShowDialog();
if (loginForm.IsOk) {
string code;
if (loginForm.CallbackParameters.TryGetValue(CODE, out code) && !string.IsNullOrEmpty(code)) {
settings.Code = code;
GenerateRefreshToken(settings);
return true;
}
}
return false;
}
/// <summary>
/// Authenticate 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 AuthenticateViaLocalServer(OAuth2Settings settings) {
var codeReceiver = new LocalServerCodeReceiver();
IDictionary<string, string> result = codeReceiver.ReceiveCode(settings);
string code;
if (result.TryGetValue(CODE, out code) && !string.IsNullOrEmpty(code)) {
settings.Code = code;
GenerateRefreshToken(settings);
return true;
}
string error;
if (result.TryGetValue("error", out error)) {
string errorDescription;
if (result.TryGetValue("error_description", out errorDescription)) {
throw new Exception(errorDescription);
}
if ("access_denied" == error) {
throw new UnauthorizedAccessException("Access denied");
} else {
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 (!Authenticate(settings)) {
throw new Exception("Authentication cancelled");
}
}
if (settings.IsAccessTokenExpired) {
GenerateAccessToken(settings);
// Get Refresh / Access token
if (string.IsNullOrEmpty(settings.RefreshToken)) {
if (!Authenticate(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

@ -18,6 +18,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/>.
*/
using System;
using System.Collections.Generic;
using System.Globalization;
@ -28,39 +29,39 @@ using log4net;
namespace GreenshotPlugin.Core {
public class SourceforgeFile {
private string file;
private readonly string _file;
public string File {
get {return file;}
get {return _file;}
}
private DateTime pubdate;
private readonly DateTime _pubdate;
public DateTime Pubdate {
get {return pubdate;}
get {return _pubdate;}
}
private string link;
private readonly string _link;
public string Link {
get {return link;}
get {return _link;}
}
private string directLink;
private readonly string _directLink;
public string DirectLink {
get {return directLink;}
get {return _directLink;}
}
private Version version;
private Version _version;
public Version Version {
get {return version;}
get {return _version;}
set {
version = value;
_version = value;
}
}
private string language;
private string _language;
public string Language {
get {return language;}
set {language = value;}
get {return _language;}
set {_language = value;}
}
public bool isExe {
get {
if (file != null) {
return file.ToLower().EndsWith(".exe");
if (_file != null) {
return _file.ToLower().EndsWith(".exe");
}
return false;
}
@ -68,8 +69,8 @@ namespace GreenshotPlugin.Core {
public bool isUnstable {
get {
if (file != null) {
return file.ToLower().Contains("unstable");
if (_file != null) {
return _file.ToLower().Contains("unstable");
}
return false;
}
@ -77,18 +78,18 @@ namespace GreenshotPlugin.Core {
public bool isReleaseCandidate {
get {
if (file != null) {
return Regex.IsMatch(file.ToLower(), "rc[0-9]+");
if (_file != null) {
return Regex.IsMatch(_file.ToLower(), "rc[0-9]+");
}
return false;
}
}
public SourceforgeFile(string file, string pubdate, string link, string directLink) {
this.file = file;
this.pubdate = DateTime.Parse(pubdate);
this.link = link;
this.directLink = directLink;
this._file = file;
DateTime.TryParse(pubdate, out _pubdate);
this._link = link;
this._directLink = directLink;
}
}
/// <summary>
@ -113,10 +114,9 @@ namespace GreenshotPlugin.Core {
/// </summary>
/// <returns>Dictionary<string, Dictionary<string, RssFile>> with files and their RssFile "description"</returns>
public static Dictionary<string, Dictionary<string, SourceforgeFile>> readRSS() {
HttpWebRequest webRequest;
XmlDocument rssDoc = new XmlDocument();
try {
webRequest = (HttpWebRequest)NetworkHelper.CreateWebRequest(RSSFEED);
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(RSSFEED);
XmlTextReader rssReader = new XmlTextReader(webRequest.GetResponse().GetResponseStream());
// Load the XML content into a XmlDocument

View file

@ -1,90 +1,166 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2015 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.IO;
using System.Security.Cryptography;
using System.Text;
using log4net;
namespace GreenshotPlugin.Core {
public static class EncryptionHelper {
private static readonly ILog LOG = LogManager.GetLogger(typeof(EncryptionHelper));
private const string RGBIV = "dlgjowejgogkklwj";
private const string KEY = "lsjvkwhvwujkagfauguwcsjgu2wueuff";
/// <summary>
/// A simply rijndael aes encryption, can be used to store passwords
/// </summary>
/// <param name="ClearText">the string to call upon</param>
/// <returns>an encryped string in base64 form</returns>
public static string Encrypt(this string ClearText) {
string returnValue = ClearText;
try {
byte[] clearTextBytes = Encoding.ASCII.GetBytes(ClearText);
SymmetricAlgorithm rijn = SymmetricAlgorithm.Create();
using (MemoryStream ms = new MemoryStream()) {
byte[] rgbIV = Encoding.ASCII.GetBytes(RGBIV);
byte[] key = Encoding.ASCII.GetBytes(KEY);
using (CryptoStream cs = new CryptoStream(ms, rijn.CreateEncryptor(key, rgbIV), CryptoStreamMode.Write)) {
cs.Write(clearTextBytes, 0, clearTextBytes.Length);
cs.FlushFinalBlock();
returnValue = Convert.ToBase64String(ms.ToArray());
}
}
} catch (Exception ex) {
LOG.ErrorFormat("Error encrypting, error: ", ex.Message);
}
return returnValue;
}
/// <summary>
/// A simply rijndael aes decryption, can be used to store passwords
/// </summary>
/// <param name="EncryptedText">a base64 encoded rijndael encrypted string</param>
/// <returns>Decrypeted text</returns>
public static string Decrypt(this string EncryptedText) {
string returnValue = EncryptedText;
try {
byte[] encryptedTextBytes = Convert.FromBase64String(EncryptedText);
using (MemoryStream ms = new MemoryStream()) {
SymmetricAlgorithm rijn = SymmetricAlgorithm.Create();
byte[] rgbIV = Encoding.ASCII.GetBytes(RGBIV);
byte[] key = Encoding.ASCII.GetBytes(KEY);
using (CryptoStream cs = new CryptoStream(ms, rijn.CreateDecryptor(key, rgbIV), CryptoStreamMode.Write)) {
cs.Write(encryptedTextBytes, 0, encryptedTextBytes.Length);
cs.FlushFinalBlock();
returnValue = Encoding.ASCII.GetString(ms.ToArray());
}
}
} catch (Exception ex) {
LOG.ErrorFormat("Error decrypting {0}, error: ", EncryptedText, ex.Message);
}
return returnValue;
}
}
}
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2015 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.IO;
using System.Security.Cryptography;
using System.Text;
using log4net;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Reflection;
namespace GreenshotPlugin.Core {
public static class StringExtensions {
private static readonly ILog LOG = LogManager.GetLogger(typeof(StringExtensions));
private const string RGBIV = "dlgjowejgogkklwj";
private const string KEY = "lsjvkwhvwujkagfauguwcsjgu2wueuff";
/// <summary>
/// Format a string with the specified object
/// </summary>
/// <param name="format">String with formatting, like {name}</param>
/// <param name="source">Object used for the formatting</param>
/// <returns>Formatted string</returns>
public static string FormatWith(this string format, object source) {
return FormatWith(format, null, source);
}
/// <summary>
/// Format the string "format" with the source
/// </summary>
/// <param name="format"></param>
/// <param name="provider"></param>
/// <param name="source">object with properties, if a property has the type IDictionary string,string it can used these parameters too</param>
/// <returns>Formatted string</returns>
public static string FormatWith(this string format, IFormatProvider provider, object source) {
if (format == null) {
throw new ArgumentNullException("format");
}
IDictionary<string, object> properties = new Dictionary<string, object>();
foreach(var propertyInfo in source.GetType().GetProperties()) {
if (propertyInfo.CanRead && propertyInfo.CanWrite) {
object value = propertyInfo.GetValue(source, null);
if (propertyInfo.PropertyType != typeof(IDictionary<string, string>)) {
properties.Add(propertyInfo.Name, value);
} else {
IDictionary<string, string> dictionary = (IDictionary<string, string>)value;
foreach (var propertyKey in dictionary.Keys) {
properties.Add(propertyKey, dictionary[propertyKey]);
}
}
}
}
Regex r = new Regex(@"(?<start>\{)+(?<property>[\w\.\[\]]+)(?<format>:[^}]+)?(?<end>\})+", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
List<object> values = new List<object>();
string rewrittenFormat = r.Replace(format, delegate(Match m) {
Group startGroup = m.Groups["start"];
Group propertyGroup = m.Groups["property"];
Group formatGroup = m.Groups["format"];
Group endGroup = m.Groups["end"];
object value;
if (properties.TryGetValue(propertyGroup.Value, out value)) {
values.Add(value);
} else {
values.Add(source);
}
return new string('{', startGroup.Captures.Count) + (values.Count - 1) + formatGroup.Value + new string('}', endGroup.Captures.Count);
});
return string.Format(provider, rewrittenFormat, values.ToArray());
}
/// <summary>
/// A simply rijndael aes encryption, can be used to store passwords
/// </summary>
/// <param name="ClearText">the string to call upon</param>
/// <returns>an encryped string in base64 form</returns>
public static string Encrypt(this string ClearText) {
string returnValue = ClearText;
try {
byte[] clearTextBytes = Encoding.ASCII.GetBytes(ClearText);
SymmetricAlgorithm rijn = SymmetricAlgorithm.Create();
using (MemoryStream ms = new MemoryStream()) {
byte[] rgbIV = Encoding.ASCII.GetBytes(RGBIV);
byte[] key = Encoding.ASCII.GetBytes(KEY);
using (CryptoStream cs = new CryptoStream(ms, rijn.CreateEncryptor(key, rgbIV), CryptoStreamMode.Write)) {
cs.Write(clearTextBytes, 0, clearTextBytes.Length);
cs.FlushFinalBlock();
returnValue = Convert.ToBase64String(ms.ToArray());
}
}
} catch (Exception ex) {
LOG.ErrorFormat("Error encrypting, error: ", ex.Message);
}
return returnValue;
}
/// <summary>
/// A simply rijndael aes decryption, can be used to store passwords
/// </summary>
/// <param name="EncryptedText">a base64 encoded rijndael encrypted string</param>
/// <returns>Decrypeted text</returns>
public static string Decrypt(this string EncryptedText) {
string returnValue = EncryptedText;
try {
byte[] encryptedTextBytes = Convert.FromBase64String(EncryptedText);
using (MemoryStream ms = new MemoryStream()) {
SymmetricAlgorithm rijn = SymmetricAlgorithm.Create();
byte[] rgbIV = Encoding.ASCII.GetBytes(RGBIV);
byte[] key = Encoding.ASCII.GetBytes(KEY);
using (CryptoStream cs = new CryptoStream(ms, rijn.CreateDecryptor(key, rgbIV), CryptoStreamMode.Write)) {
cs.Write(encryptedTextBytes, 0, encryptedTextBytes.Length);
cs.FlushFinalBlock();
returnValue = Encoding.ASCII.GetString(ms.ToArray());
}
}
} catch (Exception ex) {
LOG.ErrorFormat("Error decrypting {0}, error: ", EncryptedText, ex.Message);
}
return returnValue;
}
/// <summary>
/// Read "streamextensions" :)
/// </summary>
/// <param name="input">Stream</param>
/// <param name="output">Stream</param>
public static void CopyTo(this Stream input, Stream output) {
byte[] buffer = new byte[16 * 1024]; // Fairly arbitrary size
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0) {
output.Write(buffer, 0, bytesRead);
}
}
}
}

View file

@ -0,0 +1,65 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2015 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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 GreenshotPlugin.UnmanagedHelpers;
using System.Windows.Forms;
using log4net;
namespace GreenshotPlugin.Core
{
/// <summary>
/// This IMessageFilter filters out all WM_INPUTLANGCHANGEREQUEST messages which go to a handle which is >32 bits.
/// The need for this is documented here: http://stackoverflow.com/a/32021586
/// </summary>
public class WmInputLangChangeRequestFilter : IMessageFilter
{
private static readonly ILog LOG = LogManager.GetLogger(typeof(WmInputLangChangeRequestFilter));
/// <summary>
/// This will do some filtering
/// </summary>
/// <param name="m">Message</param>
/// <returns>true if the message should be filtered</returns>
public bool PreFilterMessage(ref Message m)
{
return PreFilterMessageExternal(ref m);
}
/// <summary>
/// Also used in the MainForm WndProc
/// </summary>
/// <param name="m">Message</param>
/// <returns>true if the message should be filtered</returns>
public static bool PreFilterMessageExternal(ref Message m)
{
WindowsMessages message = (WindowsMessages)m.Msg;
if (message == WindowsMessages.WM_INPUTLANGCHANGEREQUEST || message == WindowsMessages.WM_INPUTLANGCHANGE)
{
LOG.WarnFormat("Filtering: {0}, {1:X} - {2:X} - {3:X}", message, m.LParam.ToInt64(), m.WParam.ToInt64(), m.HWnd.ToInt64());
// For now we always return true
return true;
// But it could look something like this:
//return (m.LParam.ToInt64() | 0x7FFFFFFF) != 0;
}
return false;
}
}
}

View file

@ -37,6 +37,7 @@
<Compile Include="Core\CaptureHandler.cs" />
<Compile Include="Core\EventDelay.cs" />
<Compile Include="Core\FastBitmap.cs" />
<Compile Include="Core\WmInputLangChangeRequestFilter.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="IEInterop\IHTMLBodyElement.cs" />
<Compile Include="IEInterop\IHTMLCurrentStyle.cs" />
@ -155,7 +156,7 @@
<Compile Include="Core\CoreConfiguration.cs" />
<Compile Include="Core\CredentialsHelper.cs" />
<Compile Include="Core\Effects.cs" />
<Compile Include="Core\EncryptionHelper.cs" />
<Compile Include="Core\StringExtensions.cs" />
<Compile Include="Core\FilenameHelper.cs" />
<Compile Include="Core\ImageOutput.cs" />
<Compile Include="Core\InterfaceUtils.cs" />

View file

@ -58,7 +58,10 @@ namespace Greenshot.IniFile {
/// <summary>
/// Flag to specify if values have been changed
/// </summary>
public bool IsDirty = false;
public bool IsDirty {
get;
set;
}
/// <summary>
/// Supply values we can't put as defaults

View file

@ -1,9 +1,11 @@
version: 1.3.0.{build}
version: 1.2.7.{build}
branches:
only:
- master
- 1.2
skip_tags: true
configuration: Release
platform: Any CPU
clone_depth: 1
assembly_info:
patch: true
file: '**\AssemblyInfo.*'
@ -11,32 +13,40 @@ assembly_info:
assembly_file_version: '{version}'
assembly_informational_version: '{version}-$(build_type)-$(APPVEYOR_REPO_COMMIT)'
environment:
credentials_box_client_id: key
credentials_box_client_secret: secret
credentials_dropbox_consumer_key: key
credentials_dropbox_consumer_secret: secret
credentials_flickr_consumer_key: key
credentials_flickr_consumer_secret: secret
credentials_imgur_consumer_key: key
credentials_imgur_consumer_secret: secret
credentials_photobucket_consumer_key: key
credentials_photobucket_consumer_secret: secret
credentials_picasa_consumer_key: key
credentials_picasa_consumer_secret: secret
sourceforge_host: frs.sourceforge.net
sourceforge_user: user
sourceforge_password: password
sourceforge_hostkey: ssh-rsa 2048 b0:a8:eb:30:ce:1a:0e:6a:4d:7a:6b:3a:0a:c6:27:60
sourceforge_targetpath: /home/frs/project/greenshot/Greenshot/Greenshot 1.3/
build_type: UNSTABLE
rsakey: key
credentials_box_client_id:
secure: 8MKxTOowo2fat6cNXGbFfvn6typiEtmCKsrptrWiEFUEoKlT1DUn40iGNcIELRA1
credentials_box_client_secret:
secure: hJhzDVJuGd/WMnoSXhosvOM/1PGcYlKbtQjA6xyrmnmZcqCTMzqIdA6JXlo/V2Br
credentials_dropbox_consumer_key:
secure: Da/6KY1cu9CUM3iOqSpcUw==
credentials_dropbox_consumer_secret:
secure: KkyKyUY+buT/MZagXDP4cw==
credentials_flickr_consumer_key:
secure: fY8s0OkOMYwCjSZoL/6yZcP8xeT6J2EJLjbUMI5lAW42S5QT2U2B41KrmeP2NpnQ
credentials_flickr_consumer_secret:
secure: 9TthlljPHXWPkDDeG3uiFVJ9YJwHZOV0ZsojaIBBuvw=
credentials_imgur_consumer_key:
secure: z8S4QZ3/InPe3dgCf0CNyS0VGKuRyjjP8WMAq+AkK5OZJxZcbIxwobjgelE5CWYL
credentials_imgur_consumer_secret:
secure: ovfXJRorkkKUzbMXuZ4m0U6KF4icngmS+nzSljXJGSKfhI+GNXbMNa//mKYfTCXI
credentials_photobucket_consumer_key:
secure: oo9GD1Y8dkrli6hMfnnYsw==
credentials_photobucket_consumer_secret:
secure: GiNPoe9klM/YkoHIA/YHqOYrIaYwSFK7Ph9m8jT9uPP1l6+Hd5K8dVMw5DNa50oG
credentials_picasa_consumer_key:
secure: bjKXhFZkDqaq98XBrz5oQKQfT8CLpuv2ZAiBIwkzloaAPUs97b5yx6h/xFaE4NLS
credentials_picasa_consumer_secret:
secure: yNptTpmJWypbu9alOQtetxU66drr2FKxoPflNgRJdag=
build_type: RELEASE
rsakey:
secure: GNomwdlwZOCyd8d7xEWTnMVs1lpOeHvF+tlnvcbXGovLRtwAp2Ufu0r7paGY7BHGGkIs2WE7xUfyQ9UauVB+58JZ6fwVega8ucUgVJhl4x0QQNN2d6sULUhHfhuEHmxw+FDO/FxKFE6Lmf+ZRY+OGiw0wKIl4qD7mGRHcDQTipNEsTbau8HzqRVCdu3dx7pODC61DlsbO71xLF7UlqnmuZE+91Zz3V6AgaqE246n1499d6bXBYw1AH+8opNnKDFLnTHf7hUVcZn9mj6tKZXeTCuVUOr/SVQcgHKxlBlqzhfaEkxCR5GPtzQRqwDMxEycmFvj2wNP/sie6UEGhQxE4YMCc2OgqNOkpc5BbP/fxLr/SLFOEf1XXzTWCFMhsgpHx7TZbgQH26sa0rK/xaBRacZlwAaNk7V2nFZT7TebYEFy6zWNr9Y+IyeXIofj42XQTNXv8d8hyh+UYLByVEFYRf2DnActQkZQyNdWjZ+CxDV50QSZZs8FT3IIqraHYKsj2ITAN5LrUtWCi7bpNJL0UGo0EJiB2i0bp++tEAAwyrCljxI8d4bbGl/flHk/xd+ysQPnomndijeObjguEzqT8pyXZluSZhF+lI50mIDhMdtdAfMi5yn5RW7P6NWOSlC8xgQQgMZylsuSvRflKbEd/gsoDyEOnakNcdH2jekt9OD6GnuYM7iHkbMC89LBZ0VaHNGvCC+BQXdGUG7O9R3NthZcDXE7q7xbtGRB5ncVQDRfKoT5HVfiV6bSDrcfRODiuR59mZgiSYtZG+3kQWYUKn2wagvZKckGukA0SlOuTRCKZhgLcVHhWeRWeGE3iJ8K6BeHf2EgB8Qr6ayTyTUjBcn+u4qqWKgkvG4qRavlvrBSdMrAXWIKE8vSq1od0A2ZzP6+HCsrkuUR+HFfpE2dpjeckoa5vATQgyn8j5x11iIOB9HnT3YKbZ0aTU4rQgYMJXA/fPcgKDGkAPdgtGbQLssy/mwSdsXBYtMgEcs7vI9laR8Ik+NK2dbFHGFPnxS43WToGyKBxojt8SZbgPJXm22WRrN1+9AZvvhI7/mpZiEE7HWgNRClZYuqbfCMpelLGvVq832OLjelrWMJ0XBVNHnOw0p8qZKI1UpqQJXX1nL8j3JttEVHsfryIanM03kNDL0dX1VAKECKUMCVQ6i6tG4VWsR0C2JccPJ3PSoPgo5KMJhuZNaBoiPjZ2eaMREV6vUYbBYzrvdDQzUcE2stacREl4eJzGJ4GP5h08GQmIirGF/SCyZV1CadAbKZVjqb70XpIbE6NT/+84O82LZR4ui5KgTAv87lTZgvNJ7LxM7rRg1awj/iBxQeARNJxuPMPlk1CVx8Z3091UdL1K1avPKa85lCRwCkDKLcJPO9tlqi4dVjCrwpoCJkQMm3fbTl/BgHn00/RsnFZ2qfl5m2DyF+XuaOPauzsRdLUFAC4h44qoUuzRb4Pv6RFhN5CI4fddRKafNBHU9f69UCkO080/hIjTdj0+bpr4oNY4UEi80huyJY/c0iUPE8o48qBB8F3cW30SwhPmuphn4/18lB8GEwEPqoatmli4QRaDFUCUf9Hj0DEUqEAya/OHOW7/PvWcw/l/ZaIMUpOZ6q0xvPDAXokFRJAWzZhG7hNbWNEzQ3f/BjlYlYsBtMY0JUU8mH6YxwIzIGbHiLTBC0OglH0rDd5W+3NaUG9FZ//o9MAP5j2QqwSuFWXppbigh4zk+h17eJn5zhld7dtvOr+YmgYULj6NFIDKBZHUJdqLYScVzdc1p812FCCBcLmmw4RnwuF+RldHixTdy4UZ17T/hD4OLpWCINl9lUAficC0OFeLJLHxFW6Em8SCbZ3aUtFDIQD8oTqzUHZhGWYF2ukrOc8Dzm4FQ8xy3BhqfntTod1gwoilIirsP/z+GGMnTltkqiqK+gCmkVOfICwNFmHltZeJrmDQ4YU5abR09Yr1TaAk3CzWjV1XGBaf/oek0+tFkMOtZNdFRdlzLLE90PsZZFFnZhFBoNoOhYnMB9K2VqgEpJs0nXvF6qBOllptcpBYUYMzMdb0Ggu6m1d/phxuBuOsm+Xtr0Zw8Xd0vxIOQNDGsskCDIEUYWYajw2i66MmRPRyFEennXfLA0WIPpztXvfsrKjf42rjE3RukBsRff1Sci68cel4fGfmvj2y7gW0Tt
before_build:
- ps: iex (new-object Net.WebClient).DownloadString("https://bitbucket.org/greenshot/greenshot/raw/master/prebuild.ps1")
- ps: iex (new-object Net.WebClient).DownloadString("https://bitbucket.org/greenshot/greenshot/raw/1.2/prebuild.ps1")
build:
project: greenshot\greenshot.sln
verbosity: normal
after_build:
- ps: iex (new-object Net.WebClient).DownloadString("https://bitbucket.org/greenshot/greenshot/raw/master/build.ps1")
- ps: iex (new-object Net.WebClient).DownloadString("https://bitbucket.org/greenshot/greenshot/raw/1.2/build.ps1")
test: off
artifacts:
- path: Greenshot\releases\Greenshot*INSTALLER*.exe
@ -49,12 +59,26 @@ artifacts:
name: Readme
- path: Greenshot\releases\Greenshot-DEBUGSYMBOLS*.zip
name: DEBUGSYMBOLS
deploy_script:
- ps: iex (new-object Net.WebClient).DownloadString("https://bitbucket.org/greenshot/greenshot/raw/master/deploy.ps1")
deploy:
- provider: GitHub
tag: Greenshot-$(build_type)-$(APPVEYOR_BUILD_VERSION)
description:
auth_token:
secure: 4sYcNGg7byBFtR7EkJHS8d3H3qP0u0LodlJWCV7g/4jEyv3vvVxqzh19zZ6Zgrf1
prerelease: true
on:
build_type: UNSTABLE
- provider: GitHub
tag: Greenshot-$(build_type)-$(APPVEYOR_BUILD_VERSION)
auth_token:
secure: 4sYcNGg7byBFtR7EkJHS8d3H3qP0u0LodlJWCV7g/4jEyv3vvVxqzh19zZ6Zgrf1
on:
build_type: RELEASE
notifications:
- provider: Email
to:
- robin@getgreenshot.org
on_build_success: false
on_build_failure: false
on_build_status_changed: true
- jens@getgreenshot.org
on_build_success: true
on_build_failure: true
on_build_status_changed: false

View file

@ -1,80 +0,0 @@
################################################################
# Greenshot DEPLOY script, written for the Windows Power Shell
# Assumes the installation of Microsoft .NET Framework 4.5
################################################################
# Greenshot - a free and open source screenshot tool
# Copyright (C) 2007-2014 Thomas Braun, Jens Klingen, Robin Krom
#
# For more information see: http://getgreenshot.org/
# The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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/>.
################################################################
# This script needs some environment variables to work:
# $env:sourceforge_host with the hostname
# $env:sourceforge_user with the username
# $env:sourceforge_password with the password for the username
# $env:sourceforge_hostkey with the hosts key (ssh-rsa 2048 xx:xx:xx:xx:...)
# $env:sourceforge_targetpath with the target location
try {
# Load WinSCP .NET assembly
Add-Type -Path "Greenshot\tools\WinSCP\WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.Protocol = [WinSCP.Protocol]::Sftp
$sessionOptions.HostName = $env:sourceforge_host
$sessionOptions.UserName = $env:sourceforge_user
$sessionOptions.Password = $env:sourceforge_password
$sessionOptions.SshHostKeyFingerprint = $env:sourceforge_hostkey
$session = New-Object WinSCP.Session
try {
# Connect
$session.Open($sessionOptions)
# Upload files
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
$artifactbase = "$(get-location)\Greenshot\releases"
# The list of all the artifacts that need to be uploaded
@(
"$artifactbase\Greenshot-INSTALLER*.exe",
"$artifactbase\Greenshot-NO-INSTALLER*.zip",
"$artifactbase\Greenshot_for_PortableApps*.exe",
"$artifactbase\additional_files\readme.txt"
) | foreach {
$transferResult = $session.PutFiles($_ , $env:sourceforge_targetpath, $False, $transferOptions)
# Throw on any error
$transferResult.Check()
# Print results
foreach ($transfer in $transferResult.Transfers) {
Write-Host ("Upload of {0} to {1} succeeded" -f $transfer.FileName, $env:sourceforge_host)
}
}
} finally {
# Disconnect, clean up
$session.Dispose()
}
} catch [Exception] {
Write-Host "Error: "$_.Exception
exit 1
}