diff --git a/Greenshot/Controls/FontFamilyComboBox.cs b/Greenshot/Controls/FontFamilyComboBox.cs
index c4df132c3..410c87402 100644
--- a/Greenshot/Controls/FontFamilyComboBox.cs
+++ b/Greenshot/Controls/FontFamilyComboBox.cs
@@ -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 ();
}
+ ///
+ /// Helper method to draw the string
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ 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"));
diff --git a/Greenshot/Destinations/FileDestination.cs b/Greenshot/Destinations/FileDestination.cs
index 3841ac302..24af9bd1d 100644
--- a/Greenshot/Destinations/FileDestination.cs
+++ b/Greenshot/Destinations/FileDestination.cs
@@ -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);
diff --git a/Greenshot/Forms/AboutForm.cs b/Greenshot/Forms/AboutForm.cs
index 1fef03d31..64934dea0 100644
--- a/Greenshot/Forms/AboutForm.cs
+++ b/Greenshot/Forms/AboutForm.cs
@@ -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;
diff --git a/Greenshot/Forms/BugReportForm.cs b/Greenshot/Forms/BugReportForm.cs
index 8850a4ea8..2f6fb0247 100644
--- a/Greenshot/Forms/BugReportForm.cs
+++ b/Greenshot/Forms/BugReportForm.cs
@@ -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() {
diff --git a/Greenshot/Forms/CaptureForm.Designer.cs b/Greenshot/Forms/CaptureForm.Designer.cs
index 2722f29b0..72deb4ffd 100644
--- a/Greenshot/Forms/CaptureForm.Designer.cs
+++ b/Greenshot/Forms/CaptureForm.Designer.cs
@@ -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);
diff --git a/Greenshot/Forms/CaptureForm.cs b/Greenshot/Forms/CaptureForm.cs
index e16291510..528cd6515 100644
--- a/Greenshot/Forms/CaptureForm.cs
+++ b/Greenshot/Forms/CaptureForm.cs
@@ -123,6 +123,7 @@ namespace Greenshot.Forms {
LOG.Debug("Closing captureform");
WindowDetails.UnregisterIgnoreHandle(Handle);
}
+
///
/// This creates the capture form
///
@@ -130,7 +131,7 @@ namespace Greenshot.Forms {
///
public CaptureForm(ICapture capture, List 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;
}
diff --git a/Greenshot/Forms/DropShadowSettingsForm.cs b/Greenshot/Forms/DropShadowSettingsForm.cs
index 2eee65565..1952c6058 100644
--- a/Greenshot/Forms/DropShadowSettingsForm.cs
+++ b/Greenshot/Forms/DropShadowSettingsForm.cs
@@ -31,7 +31,6 @@ namespace Greenshot.Forms {
public DropShadowSettingsForm(DropShadowEffect effect) {
this.effect = effect;
InitializeComponent();
- this.Icon = GreenshotResources.getGreenshotIcon();
ShowSettings();
}
diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs
index 1562d642d..c5cc2ddba 100644
--- a/Greenshot/Forms/ImageEditorForm.cs
+++ b/Greenshot/Forms/ImageEditorForm.cs
@@ -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;
diff --git a/Greenshot/Forms/MainForm.cs b/Greenshot/Forms/MainForm.cs
index 81e7e9165..e58af6640 100644
--- a/Greenshot/Forms/MainForm.cs
+++ b/Greenshot/Forms/MainForm.cs
@@ -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
+
///
/// Helper method to cleanly register a hotkey
///
@@ -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();
}
diff --git a/Greenshot/Forms/PrintOptionsDialog.cs b/Greenshot/Forms/PrintOptionsDialog.cs
index 87e6fc7bb..1175bf1dc 100644
--- a/Greenshot/Forms/PrintOptionsDialog.cs
+++ b/Greenshot/Forms/PrintOptionsDialog.cs
@@ -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;
}
diff --git a/Greenshot/Forms/ResizeSettingsForm.cs b/Greenshot/Forms/ResizeSettingsForm.cs
index c3441be33..db8cac328 100644
--- a/Greenshot/Forms/ResizeSettingsForm.cs
+++ b/Greenshot/Forms/ResizeSettingsForm.cs
@@ -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);
diff --git a/Greenshot/Forms/SettingsForm.cs b/Greenshot/Forms/SettingsForm.cs
index 819c3d573..b64fdfaf0 100644
--- a/Greenshot/Forms/SettingsForm.cs
+++ b/Greenshot/Forms/SettingsForm.cs
@@ -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) {
diff --git a/Greenshot/Forms/TornEdgeSettingsForm.cs b/Greenshot/Forms/TornEdgeSettingsForm.cs
index edd57a266..63bae471a 100644
--- a/Greenshot/Forms/TornEdgeSettingsForm.cs
+++ b/Greenshot/Forms/TornEdgeSettingsForm.cs
@@ -30,7 +30,6 @@ namespace Greenshot.Forms {
public TornEdgeSettingsForm(TornEdgeEffect effect) {
this.effect = effect;
InitializeComponent();
- this.Icon = GreenshotResources.getGreenshotIcon();
ShowSettings();
}
diff --git a/Greenshot/Help/HelpFileLoader.cs b/Greenshot/Help/HelpFileLoader.cs
index fe79de7b4..93759a850 100644
--- a/Greenshot/Help/HelpFileLoader.cs
+++ b/Greenshot/Help/HelpFileLoader.cs
@@ -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 .
*/
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
}
/// 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)
- 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
///
/// URL for which the HTTP status is to be checked
/// An HTTP status code, or null if there is none (probably indicating that there is no internet connection available
- 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) {
diff --git a/Greenshot/Helpers/CaptureHelper.cs b/Greenshot/Helpers/CaptureHelper.cs
index 943a00d26..301b97c14 100644
--- a/Greenshot/Helpers/CaptureHelper.cs
+++ b/Greenshot/Helpers/CaptureHelper.cs
@@ -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;
diff --git a/Greenshot/Helpers/EnvironmentInfo.cs b/Greenshot/Helpers/EnvironmentInfo.cs
index 941f1eba0..443fe3321 100644
--- a/Greenshot/Helpers/EnvironmentInfo.cs
+++ b/Greenshot/Helpers/EnvironmentInfo.cs
@@ -18,30 +18,33 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+
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
+{
///
/// Description of EnvironmentInfo.
///
- 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().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/
///
- static public class OSInfo {
+ static public class OSInfo
+ {
#region BITS
///
/// Determines if the current application is 32 or 64-bit.
///
- static public int Bits {
- get {
+ static public int Bits
+ {
+ get
+ {
return IntPtr.Size * 8;
}
}
@@ -190,9 +235,12 @@ namespace Greenshot.Helpers {
///
/// Gets the edition of the operating system running on this computer.
///
- 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 {
///
/// Gets the name of the operating system running on this computer.
///
- 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 {
///
/// Gets the service pack information of the operating system running on this computer.
///
- 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 {
///
/// Gets the build version number of the operating system running on this computer.
///
- public static int BuildVersion {
- get {
+ public static int BuildVersion
+ {
+ get
+ {
return Environment.OSVersion.Version.Build;
}
}
@@ -662,8 +776,10 @@ namespace Greenshot.Helpers {
///
/// Gets the full version string of the operating system running on this computer.
///
- 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 {
///
/// Gets the full version of the operating system running on this computer.
///
- static public Version Version {
- get {
+ static public Version Version
+ {
+ get
+ {
return Environment.OSVersion.Version;
}
}
@@ -685,8 +803,10 @@ namespace Greenshot.Helpers {
///
/// Gets the major version number of the operating system running on this computer.
///
- static public int MajorVersion {
- get {
+ static public int MajorVersion
+ {
+ get
+ {
return Environment.OSVersion.Version.Major;
}
}
@@ -696,8 +816,10 @@ namespace Greenshot.Helpers {
///
/// Gets the minor version number of the operating system running on this computer.
///
- static public int MinorVersion {
- get {
+ static public int MinorVersion
+ {
+ get
+ {
return Environment.OSVersion.Version.Minor;
}
}
@@ -707,12 +829,14 @@ namespace Greenshot.Helpers {
///
/// Gets the revision version number of the operating system running on this computer.
///
- static public int RevisionVersion {
- get {
+ static public int RevisionVersion
+ {
+ get
+ {
return Environment.OSVersion.Version.Revision;
}
}
#endregion REVISION
#endregion VERSION
}
-}
+}
\ No newline at end of file
diff --git a/Greenshot/greenshot.manifest b/Greenshot/greenshot.manifest
index 58a12a1a1..919fcb895 100644
--- a/Greenshot/greenshot.manifest
+++ b/Greenshot/greenshot.manifest
@@ -9,14 +9,16 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/Greenshot/releases/additional_files/readme.txt.template b/Greenshot/releases/additional_files/readme.txt.template
index 4bdafc6ab..6ec082c85 100644
--- a/Greenshot/releases/additional_files/readme.txt.template
+++ b/Greenshot/releases/additional_files/readme.txt.template
@@ -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.
diff --git a/GreenshotBoxPlugin/BoxConfiguration.cs b/GreenshotBoxPlugin/BoxConfiguration.cs
index e82773acf..b75a3c2ae 100644
--- a/GreenshotBoxPlugin/BoxConfiguration.cs
+++ b/GreenshotBoxPlugin/BoxConfiguration.cs
@@ -18,9 +18,11 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+
using System.Windows.Forms;
using Greenshot.IniFile;
using GreenshotPlugin.Core;
+using System;
namespace GreenshotBoxPlugin {
///
@@ -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;
+ }
+
+ ///
+ /// Not stored
+ ///
+ public string AccessToken {
+ get;
+ set;
+ }
+
+ ///
+ /// Not stored
+ ///
+ public DateTimeOffset AccessTokenExpires {
+ get;
+ set;
+ }
///
/// A form for token
diff --git a/GreenshotBoxPlugin/BoxUtils.cs b/GreenshotBoxPlugin/BoxUtils.cs
index bca46b0bf..e0e1566d5 100644
--- a/GreenshotBoxPlugin/BoxUtils.cs
+++ b/GreenshotBoxPlugin/BoxUtils.cs
@@ -18,16 +18,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-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();
- 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(authorizationResponse);
-
- Config.BoxToken = authorization.AccessToken;
- IniConfig.Save();
- return true;
- }
-
///
- /// Download a url response as string
- ///
- /// An Uri to specify the download location
- ///
- /// string with the file content
- 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);
- }
-
- ///
- /// Upload parameters by post
- ///
- ///
- ///
- /// response
- public static string HttpPost(string url, IDictionary 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);
- }
-
- ///
- /// Upload file by PUT
+ /// Put string
///
///
///
+ /// OAuth2Settings
/// response
- 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);
- }
-
-
- ///
- /// Get REST request
- ///
- ///
- /// response
- 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);
}
///
@@ -143,45 +64,52 @@ namespace GreenshotBoxPlugin {
/// Filename of box upload
/// url to uploaded image
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 parameters = new Dictionary();
- 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(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(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();
}
}
}
diff --git a/GreenshotBoxPlugin/Forms/SettingsForm.cs b/GreenshotBoxPlugin/Forms/SettingsForm.cs
index 2e860a665..07b32373d 100644
--- a/GreenshotBoxPlugin/Forms/SettingsForm.cs
+++ b/GreenshotBoxPlugin/Forms/SettingsForm.cs
@@ -39,8 +39,6 @@ namespace GreenshotBoxPlugin {
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
- this.Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
-
}
}
}
diff --git a/GreenshotDropboxPlugin/Forms/SettingsForm.cs b/GreenshotDropboxPlugin/Forms/SettingsForm.cs
index 99d6738b8..280ded7f1 100644
--- a/GreenshotDropboxPlugin/Forms/SettingsForm.cs
+++ b/GreenshotDropboxPlugin/Forms/SettingsForm.cs
@@ -18,6 +18,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+
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();
}
}
diff --git a/GreenshotExternalCommandPlugin/ExternalCommandDestination.cs b/GreenshotExternalCommandPlugin/ExternalCommandDestination.cs
index d6784855a..ee13f6eee 100644
--- a/GreenshotExternalCommandPlugin/ExternalCommandDestination.cs
+++ b/GreenshotExternalCommandPlugin/ExternalCommandDestination.cs
@@ -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);
}
}
diff --git a/GreenshotExternalCommandPlugin/SettingsForm.cs b/GreenshotExternalCommandPlugin/SettingsForm.cs
index 025cdb68e..247e38b59 100644
--- a/GreenshotExternalCommandPlugin/SettingsForm.cs
+++ b/GreenshotExternalCommandPlugin/SettingsForm.cs
@@ -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();
diff --git a/GreenshotExternalCommandPlugin/SettingsFormDetail.cs b/GreenshotExternalCommandPlugin/SettingsFormDetail.cs
index 1fff7fc62..ae9d4b030 100644
--- a/GreenshotExternalCommandPlugin/SettingsFormDetail.cs
+++ b/GreenshotExternalCommandPlugin/SettingsFormDetail.cs
@@ -38,7 +38,6 @@ namespace ExternalCommand {
public SettingsFormDetail(string commando) {
InitializeComponent();
- this.Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
AcceptButton = buttonOk;
CancelButton = buttonCancel;
this.commando = commando;
diff --git a/GreenshotFlickrPlugin/Forms/FlickrForm.cs b/GreenshotFlickrPlugin/Forms/FlickrForm.cs
index 5aaf18262..59358436e 100644
--- a/GreenshotFlickrPlugin/Forms/FlickrForm.cs
+++ b/GreenshotFlickrPlugin/Forms/FlickrForm.cs
@@ -18,6 +18,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+
using System;
using GreenshotPlugin.Controls;
diff --git a/GreenshotFlickrPlugin/Forms/SettingsForm.cs b/GreenshotFlickrPlugin/Forms/SettingsForm.cs
index 92bd39374..0835e2ea5 100644
--- a/GreenshotFlickrPlugin/Forms/SettingsForm.cs
+++ b/GreenshotFlickrPlugin/Forms/SettingsForm.cs
@@ -37,7 +37,6 @@ namespace GreenshotFlickrPlugin {
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
- Icon = GreenshotResources.getGreenshotIcon();
CancelButton = buttonCancel;
AcceptButton = buttonOK;
}
diff --git a/GreenshotImgurPlugin/Forms/ImgurForm.cs b/GreenshotImgurPlugin/Forms/ImgurForm.cs
index 15de31281..d476f9313 100644
--- a/GreenshotImgurPlugin/Forms/ImgurForm.cs
+++ b/GreenshotImgurPlugin/Forms/ImgurForm.cs
@@ -18,13 +18,15 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+
using System;
+using GreenshotPlugin.Controls;
namespace GreenshotImgurPlugin {
///
/// This class is needed for design-time resolving of the language files
///
- public class ImgurForm : GreenshotPlugin.Controls.GreenshotForm {
+ public class ImgurForm : GreenshotForm {
public ImgurForm() : base() {
}
}
diff --git a/GreenshotImgurPlugin/Forms/ImgurHistory.Designer.cs b/GreenshotImgurPlugin/Forms/ImgurHistory.Designer.cs
index 2fb75eb8d..f3d3d221f 100644
--- a/GreenshotImgurPlugin/Forms/ImgurHistory.Designer.cs
+++ b/GreenshotImgurPlugin/Forms/ImgurHistory.Designer.cs
@@ -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
//
diff --git a/GreenshotImgurPlugin/Forms/ImgurHistory.cs b/GreenshotImgurPlugin/Forms/ImgurHistory.cs
index 25c501a1e..064fcc21f 100644
--- a/GreenshotImgurPlugin/Forms/ImgurHistory.cs
+++ b/GreenshotImgurPlugin/Forms/ImgurHistory.cs
@@ -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++) {
diff --git a/GreenshotImgurPlugin/Forms/SettingsForm.cs b/GreenshotImgurPlugin/Forms/SettingsForm.cs
index bda6270c6..8a35fc06e 100644
--- a/GreenshotImgurPlugin/Forms/SettingsForm.cs
+++ b/GreenshotImgurPlugin/Forms/SettingsForm.cs
@@ -35,7 +35,6 @@ namespace GreenshotImgurPlugin {
InitializeComponent();
CancelButton = buttonCancel;
AcceptButton = buttonOK;
- this.Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
ImgurUtils.LoadHistory();
diff --git a/GreenshotImgurPlugin/ImgurConfiguration.cs b/GreenshotImgurPlugin/ImgurConfiguration.cs
index 9fdb3950c..4dfb5acb3 100644
--- a/GreenshotImgurPlugin/ImgurConfiguration.cs
+++ b/GreenshotImgurPlugin/ImgurConfiguration.cs
@@ -30,9 +30,11 @@ namespace GreenshotImgurPlugin {
///
[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")]
diff --git a/GreenshotImgurPlugin/ImgurInfo.cs b/GreenshotImgurPlugin/ImgurInfo.cs
index 52daacf6d..fd8b61d20 100644
--- a/GreenshotImgurPlugin/ImgurInfo.cs
+++ b/GreenshotImgurPlugin/ImgurInfo.cs
@@ -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;
diff --git a/GreenshotImgurPlugin/ImgurUtils.cs b/GreenshotImgurPlugin/ImgurUtils.cs
index efa6de7cc..7e4829a64 100644
--- a/GreenshotImgurPlugin/ImgurUtils.cs
+++ b/GreenshotImgurPlugin/ImgurUtils.cs
@@ -31,14 +31,15 @@ namespace GreenshotImgurPlugin {
///
/// Description of ImgurUtils.
///
- 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();
- private ImgurUtils() {
- }
-
+ ///
+ /// Load the complete history of the imgur uploads, with the corresponding information
+ ///
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 {
}
}
+ ///
+ /// Use this to make sure Imgur knows from where the upload comes.
+ ///
+ ///
+ private static void SetClientId(HttpWebRequest webRequest) {
+ webRequest.Headers.Add("Authorization", "Client-ID " + IMGUR_ANONYMOUS_API_KEY);
+ }
+
///
/// 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);
}
+ ///
+ /// Retrieve the thumbnail of an imgur image
+ ///
+ ///
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;
}
+ ///
+ /// Retrieve information on an imgur image
+ ///
+ ///
+ ///
+ /// ImgurInfo
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;
}
+ ///
+ /// Delete an imgur image, this is done by specifying the delete hash
+ ///
+ ///
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;
+
+ ///
+ /// Helper for logging
+ ///
+ ///
+ ///
+ private static void LogHeader(IDictionary nameValues, string key) {
+ if (nameValues.ContainsKey(key)) {
+ LOG.InfoFormat("key={0}", nameValues[key]);
+ }
+ }
+
+ ///
+ /// Log the current rate-limit information
+ ///
+ ///
+ private static void LogRateLimitInfo(WebResponse response) {
+ IDictionary nameValues = new Dictionary();
+ 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;
+ }
}
}
}
diff --git a/GreenshotJiraPlugin/Forms/SettingsForm.cs b/GreenshotJiraPlugin/Forms/SettingsForm.cs
index 383ec7fbf..1ecaecfbf 100644
--- a/GreenshotJiraPlugin/Forms/SettingsForm.cs
+++ b/GreenshotJiraPlugin/Forms/SettingsForm.cs
@@ -34,7 +34,6 @@ namespace GreenshotJiraPlugin {
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
- Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
}
}
}
diff --git a/GreenshotOCRPlugin/OCRForm.cs b/GreenshotOCRPlugin/OCRForm.cs
index e7051dccc..f6fe326a2 100644
--- a/GreenshotOCRPlugin/OCRForm.cs
+++ b/GreenshotOCRPlugin/OCRForm.cs
@@ -19,11 +19,13 @@
* along with this program. If not, see .
*/
+using GreenshotPlugin.Controls;
+
namespace GreenshotOCR {
///
/// This class is needed for design-time resolving of the language files
///
- public class OCRForm : GreenshotPlugin.Controls.GreenshotForm {
+ public class OCRForm : GreenshotForm {
public OCRForm() : base() {
}
}
diff --git a/GreenshotOCRPlugin/SettingsForm.cs b/GreenshotOCRPlugin/SettingsForm.cs
index 9d34347c3..88b1d029a 100644
--- a/GreenshotOCRPlugin/SettingsForm.cs
+++ b/GreenshotOCRPlugin/SettingsForm.cs
@@ -37,7 +37,6 @@ namespace GreenshotOCR {
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
- this.Icon = GreenshotResources.getGreenshotIcon();
comboBox_languages.Items.Clear();
int index=0;
diff --git a/GreenshotPhotobucketPlugin/Forms/SettingsForm.cs b/GreenshotPhotobucketPlugin/Forms/SettingsForm.cs
index 83298e841..560a29b39 100644
--- a/GreenshotPhotobucketPlugin/Forms/SettingsForm.cs
+++ b/GreenshotPhotobucketPlugin/Forms/SettingsForm.cs
@@ -35,7 +35,6 @@ namespace GreenshotPhotobucketPlugin {
InitializeComponent();
AcceptButton = buttonOK;
CancelButton = buttonCancel;
- Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
}
}
}
diff --git a/GreenshotPicasaPlugin/Forms/SettingsForm.cs b/GreenshotPicasaPlugin/Forms/SettingsForm.cs
index 2916a5630..edb7597f0 100644
--- a/GreenshotPicasaPlugin/Forms/SettingsForm.cs
+++ b/GreenshotPicasaPlugin/Forms/SettingsForm.cs
@@ -38,7 +38,6 @@ namespace GreenshotPicasaPlugin {
InitializeComponent();
CancelButton = buttonCancel;
AcceptButton = buttonOK;
- Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon();
}
}
diff --git a/GreenshotPicasaPlugin/PicasaConfiguration.cs b/GreenshotPicasaPlugin/PicasaConfiguration.cs
index 849539ae7..c7b81e3c2 100644
--- a/GreenshotPicasaPlugin/PicasaConfiguration.cs
+++ b/GreenshotPicasaPlugin/PicasaConfiguration.cs
@@ -20,6 +20,7 @@
using System.Windows.Forms;
using Greenshot.IniFile;
using GreenshotPlugin.Core;
+using System;
namespace GreenshotPicasaPlugin {
///
@@ -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;
+ }
+
+ ///
+ /// Not stored
+ ///
+ public string AccessToken {
+ get;
+ set;
+ }
+
+ ///
+ /// Not stored
+ ///
+ public DateTimeOffset AccessTokenExpires {
+ get;
+ set;
+ }
///
/// A form for token
diff --git a/GreenshotPicasaPlugin/PicasaCredentials.cs b/GreenshotPicasaPlugin/PicasaCredentials.cs
index 046d188f9..83c6059b6 100644
--- a/GreenshotPicasaPlugin/PicasaCredentials.cs
+++ b/GreenshotPicasaPlugin/PicasaCredentials.cs
@@ -25,7 +25,7 @@ namespace GreenshotPicasaPlugin {
/// You can set your own values here
///
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@";
}
}
diff --git a/GreenshotPicasaPlugin/PicasaUtils.cs b/GreenshotPicasaPlugin/PicasaUtils.cs
index e8c106a7e..14c8307d1 100644
--- a/GreenshotPicasaPlugin/PicasaUtils.cs
+++ b/GreenshotPicasaPlugin/PicasaUtils.cs
@@ -17,25 +17,25 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Xml;
+
using Greenshot.IniFile;
using Greenshot.Plugin;
using GreenshotPlugin.Core;
+using System;
+using System.Net;
+using System.Xml;
namespace GreenshotPicasaPlugin {
///
/// Description of PicasaUtils.
///
- 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();
-
- 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}";
///
/// Do the actual upload to Picasa
@@ -46,46 +46,46 @@ namespace GreenshotPicasaPlugin {
///
/// PicasaResponse
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 headers = new Dictionary();
- 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();
}
}
+ ///
+ /// Parse the upload URL from the response
+ ///
+ ///
+ ///
public static string ParseResponse(string response) {
if (response == null) {
return null;
diff --git a/GreenshotPlugin/Controls/AnimatingForm.cs b/GreenshotPlugin/Controls/AnimatingForm.cs
index 17897a5c2..17f8baa38 100644
--- a/GreenshotPlugin/Controls/AnimatingForm.cs
+++ b/GreenshotPlugin/Controls/AnimatingForm.cs
@@ -18,17 +18,20 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+
using System;
using System.Windows.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.UnmanagedHelpers;
using Greenshot.IniFile;
+using log4net;
namespace GreenshotPlugin.Controls {
///
/// Extend this Form to have the possibility for animations on your form
///
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 {
///
///
void timer_Tick(object sender, EventArgs e) {
- Animate();
+ try {
+ Animate();
+ } catch (Exception ex) {
+ LOG.Warn("An exception occured while animating:", ex);
+ }
}
///
diff --git a/GreenshotPlugin/Controls/GreenshotForm.cs b/GreenshotPlugin/Controls/GreenshotForm.cs
index 62c01bb9f..ab151675a 100644
--- a/GreenshotPlugin/Controls/GreenshotForm.cs
+++ b/GreenshotPlugin/Controls/GreenshotForm.cs
@@ -38,11 +38,11 @@ namespace GreenshotPlugin.Controls {
protected static CoreConfiguration coreConfiguration;
private static IDictionary reflectionCache = new Dictionary();
private IComponentChangeService m_changeService;
- private bool isDesignModeLanguageSet = false;
- private bool applyLanguageManually = false;
- private bool storeFieldsManually = false;
- private IDictionary designTimeControls;
- private IDictionary designTimeToolStripItems;
+ private bool _isDesignModeLanguageSet = false;
+ private bool _applyLanguageManually = false;
+ private bool _storeFieldsManually = false;
+ private IDictionary _designTimeControls;
+ private IDictionary _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;
}
}
+ ///
+ /// When this is set, the form will be brought to the foreground as soon as it is shown.
+ ///
+ protected bool ToFront {
+ get;
+ set;
+ }
+
///
/// Code to initialize the language etc during design time
///
protected void InitializeForDesigner() {
if (DesignMode) {
- designTimeControls = new Dictionary();
- designTimeToolStripItems = new Dictionary();
+ _designTimeControls = new Dictionary();
+ _designTimeToolStripItems = new Dictionary();
try {
ITypeResolutionService typeResService = GetService(typeof(ITypeResolutionService)) as ITypeResolutionService;
@@ -119,8 +127,8 @@ namespace GreenshotPlugin.Controls {
///
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 {
}
}
+ ///
+ /// Make sure the form is visible, if this is wanted
+ ///
+ /// EventArgs
+ protected override void OnShown(EventArgs e) {
+ base.OnShown(e);
+ if (ToFront) {
+ WindowDetails.ToForeground(Handle);
+ }
+ }
+
///
/// check if the form was closed with an OK, if so store the values in the GreenshotControls
///
///
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;
}
diff --git a/GreenshotPlugin/Controls/OAuthLoginForm.Designer.cs b/GreenshotPlugin/Controls/OAuthLoginForm.Designer.cs
index d81bb942e..f7d47f4c5 100644
--- a/GreenshotPlugin/Controls/OAuthLoginForm.Designer.cs
+++ b/GreenshotPlugin/Controls/OAuthLoginForm.Designer.cs
@@ -44,37 +44,37 @@ namespace GreenshotPlugin.Controls {
/// the contents of this method with the code editor.
///
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;
}
}
diff --git a/GreenshotPlugin/Controls/OAuthLoginForm.cs b/GreenshotPlugin/Controls/OAuthLoginForm.cs
index 59e8f4088..e562b1185 100644
--- a/GreenshotPlugin/Controls/OAuthLoginForm.cs
+++ b/GreenshotPlugin/Controls/OAuthLoginForm.cs
@@ -35,62 +35,74 @@ namespace GreenshotPlugin.Controls {
///
public partial class OAuthLoginForm : Form {
private static readonly ILog LOG = LogManager.GetLogger(typeof(OAuthLoginForm));
- private string callbackUrl = null;
- private IDictionary callbackParameters = null;
+ private string _callbackUrl = null;
+ private IDictionary _callbackParameters = null;
public IDictionary 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));
+ }
+ ///
+ /// Make sure the form is visible
+ ///
+ /// EventArgs
+ 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;
}
diff --git a/GreenshotPlugin/Controls/QualityDialog.cs b/GreenshotPlugin/Controls/QualityDialog.cs
index 338a396d1..c601c8e88 100644
--- a/GreenshotPlugin/Controls/QualityDialog.cs
+++ b/GreenshotPlugin/Controls/QualityDialog.cs
@@ -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) {
diff --git a/GreenshotPlugin/Core/CoreConfiguration.cs b/GreenshotPlugin/Core/CoreConfiguration.cs
index dff34dda4..b5bff9abb 100644
--- a/GreenshotPlugin/Core/CoreConfiguration.cs
+++ b/GreenshotPlugin/Core/CoreConfiguration.cs
@@ -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);
}
+ ///
+ /// This method will be called before writing the configuration
+ ///
+ 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 {
+ }
+ }
+
///
/// This method will be called after reading the configuration, so eventually some corrections can be made
///
@@ -498,8 +509,8 @@ namespace GreenshotPlugin.Core {
OutputFileReduceColorsTo = 256;
}
- if (WebRequestTimeout < 1) {
- WebRequestTimeout = 10;
+ if (WebRequestTimeout <= 10) {
+ WebRequestTimeout = 100;
}
if (WebRequestReadWriteTimeout < 1) {
WebRequestReadWriteTimeout = 100;
diff --git a/GreenshotPlugin/Core/NetworkHelper.cs b/GreenshotPlugin/Core/NetworkHelper.cs
index 6fcfb6854..0acf63bf3 100644
--- a/GreenshotPlugin/Core/NetworkHelper.cs
+++ b/GreenshotPlugin/Core/NetworkHelper.cs
@@ -33,6 +33,17 @@ using System.Text;
using System.Text.RegularExpressions;
namespace GreenshotPlugin.Core {
+ ///
+ /// HTTP Method to make sure we have the correct method
+ ///
+ public enum HTTPMethod {
+ GET,
+ POST,
+ PUT,
+ DELETE,
+ HEAD
+ };
+
///
/// Description of NetworkHelper.
///
@@ -54,11 +65,7 @@ namespace GreenshotPlugin.Core {
/// An Uri to specify the download location
/// string with the file content
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));
}
///
@@ -86,6 +93,26 @@ namespace GreenshotPlugin.Core {
return null;
}
+ ///
+ /// Download the uri into a memorystream, without catching exceptions
+ ///
+ /// Of an image
+ /// MemoryStream which is already seeked to 0
+ 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;
+ }
+ }
+
///
/// Download the uri to Bitmap
///
@@ -93,15 +120,27 @@ namespace GreenshotPlugin.Core {
/// Bitmap
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 {
}
///
- /// Helper method to create a web request, eventually with proxy
+ /// Helper method to create a web request with a lot of default settings
///
/// string with uri to connect to
/// WebRequest
public static HttpWebRequest CreateWebRequest(string uri) {
return CreateWebRequest(new Uri(uri));
}
+
+ ///
+ /// Helper method to create a web request with a lot of default settings
+ ///
+ /// string with uri to connect to
+ /// /// Method to use
+ /// WebRequest
+ public static HttpWebRequest CreateWebRequest(string uri, HTTPMethod method) {
+ return CreateWebRequest(new Uri(uri), method);
+ }
+ ///
+ /// Helper method to create a web request with a lot of default settings
+ ///
+ /// Uri with uri to connect to
+ /// Method to use
+ /// WebRequest
+ public static HttpWebRequest CreateWebRequest(Uri uri, HTTPMethod method) {
+ HttpWebRequest webRequest = CreateWebRequest(uri);
+ webRequest.Method = method.ToString();
+ return webRequest;
+ }
+
///
/// Helper method to create a web request, eventually with proxy
///
@@ -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));
}
-
+
+ ///
+ /// Post the parameters "x-www-form-urlencoded"
+ ///
+ ///
+ public static void UploadFormUrlEncoded(HttpWebRequest webRequest, IDictionary 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);
+ }
+ }
+
+ ///
+ /// Log the headers of the WebResponse, if IsDebugEnabled
+ ///
+ /// WebResponse
+ 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]);
+ }
+ }
+
///
/// Process the web response.
///
/// The request object.
/// The response data.
/// TODO: This method should handle the StatusCode better!
- public static string GetResponse(HttpWebRequest webRequest) {
+ public static string GetResponseAsString(HttpWebRequest webRequest) {
+ return GetResponseAsString(webRequest, false);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ 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;
diff --git a/GreenshotPlugin/Core/OAuthHelper.cs b/GreenshotPlugin/Core/OAuthHelper.cs
index ba44650f7..94b73dc8c 100644
--- a/GreenshotPlugin/Core/OAuthHelper.cs
+++ b/GreenshotPlugin/Core/OAuthHelper.cs
@@ -19,30 +19,198 @@
* along with this program. If not, see .
*/
+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 {
///
- /// 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
///
public enum OAuthSignatureTypes {
HMACSHA1,
PLAINTEXT,
RSASHA1
}
-
- public enum HTTPMethod { GET, POST, PUT, DELETE };
+
+ ///
+ /// Specify the autorize mode that is used to get the token from the cloud service.
+ ///
+ 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
+ }
+ ///
+ /// Settings for the OAuth 2 protocol
+ ///
+ public class OAuth2Settings {
+ public OAuth2Settings() {
+ AdditionalAttributes = new Dictionary();
+ // Create a default state
+ State = Guid.NewGuid().ToString();
+ AuthorizeMode = OAuth2AuthorizeMode.Unknown;
+ }
+
+ public OAuth2AuthorizeMode AuthorizeMode {
+ get;
+ set;
+ }
+
+ ///
+ /// Specify the name of the cloud service, so it can be used in window titles, logs etc
+ ///
+ public string CloudServiceName {
+ get;
+ set;
+ }
+
+ ///
+ /// Specify the size of the embedded Browser, if using this
+ ///
+ public Size BrowserSize {
+ get;
+ set;
+ }
+
+ ///
+ /// The OAuth 2 client id
+ ///
+ public string ClientId {
+ get;
+ set;
+ }
+
+ ///
+ /// The OAuth 2 client secret
+ ///
+ public string ClientSecret {
+ get;
+ set;
+ }
+
+ ///
+ /// 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
+ ///
+ public string State {
+ get;
+ set;
+ }
+
+ ///
+ /// The autorization URL where the values of this class can be "injected"
+ ///
+ public string AuthUrlPattern {
+ get;
+ set;
+ }
+
+ ///
+ /// Get formatted Auth url (this will call a FormatWith(this) on the AuthUrlPattern
+ ///
+ public string FormattedAuthUrl {
+ get {
+ return AuthUrlPattern.FormatWith(this);
+ }
+ }
+
+ ///
+ /// The URL to get a Token
+ ///
+ public string TokenUrl {
+ get;
+ set;
+ }
+
+ ///
+ /// 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
+ ///
+ public string RedirectUrl {
+ get;
+ set;
+ }
+
+ ///
+ /// Bearer token for accessing OAuth 2 services
+ ///
+ public string AccessToken {
+ get;
+ set;
+ }
+
+ ///
+ /// Expire time for the AccessToken, this this time (-60 seconds) is passed a new AccessToken needs to be generated with the RefreshToken
+ ///
+ public DateTimeOffset AccessTokenExpires {
+ get;
+ set;
+ }
+
+ ///
+ /// Return true if the access token is expired.
+ /// Important "side-effect": if true is returned the AccessToken will be set to null!
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Token used to get a new Access Token
+ ///
+ public string RefreshToken {
+ get;
+ set;
+ }
+
+ ///
+ /// 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"
+ ///
+ public IDictionary AdditionalAttributes {
+ get;
+ set;
+ }
+
+ ///
+ /// 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.
+ ///
+ public string Code {
+ get;
+ set;
+ }
+ }
+
+ ///
+ /// An OAuth 1 session object
+ ///
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;
}
}
+
+ ///
+ /// OAuth 2.0 verification code receiver that runs a local server on a free port
+ /// and waits for a call with the authorization verification code.
+ ///
+ public class LocalServerCodeReceiver {
+ private static readonly ILog LOG = LogManager.GetLogger(typeof(LocalServerCodeReceiver));
+ private readonly ManualResetEvent _ready = new ManualResetEvent(true);
+
+ private string _loopbackCallback = "http://localhost:{0}/authorize/";
+ ///
+ /// The call back format. Expects one port parameter.
+ /// Default: http://localhost:{0}/authorize/
+ ///
+ public string LoopbackCallbackUrl {
+ get {
+ return _loopbackCallback;
+ }
+ set {
+ _loopbackCallback = value;
+ }
+ }
+
+ private string _closePageResponse =
+@"
+OAuth 2.0 Authentication CloudServiceName
+
+Greenshot received information from CloudServiceName. You can close this browser / tab if it is not closed itself...
+
+
+";
+
+ ///
+ /// 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
+ ///
+ public string ClosePageResponse {
+ get {
+ return _closePageResponse;
+ }
+ set {
+ _closePageResponse = value;
+ }
+ }
+
+ private string _redirectUri;
+ ///
+ /// The URL to redirect to
+ ///
+ protected string RedirectUri {
+ get {
+ if (!string.IsNullOrEmpty(_redirectUri)) {
+ return _redirectUri;
+ }
+
+ return _redirectUri = string.Format(_loopbackCallback, GetRandomUnusedPort());
+ }
+ }
+
+ private string _cloudServiceName;
+
+ private IDictionary _returnValues = new Dictionary();
+
+
+ ///
+ /// The OAuth code receiver
+ ///
+ ///
+ /// Dictionary with values
+ public IDictionary 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;
+ }
+
+ ///
+ /// Handle a connection async, this allows us to break the waiting
+ ///
+ /// IAsyncResult
+ private void ListenerCallback(IAsyncResult result) {
+ HttpListener listener = (HttpListener)result.AsyncState;
+
+ //If not listening return immediately as this method is called one last time after Close()
+ if (!listener.IsListening) {
+ return;
+ }
+
+ // Use EndGetContext to complete the asynchronous operation.
+ HttpListenerContext context = listener.EndGetContext(result);
+
+
+ // Handle request
+ HttpListenerRequest request = context.Request;
+ 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();
+ }
+
+ ///
+ /// Returns a random, unused port.
+ ///
+ /// port to use
+ private static int GetRandomUnusedPort() {
+ var listener = new TcpListener(IPAddress.Loopback, 0);
+ try {
+ listener.Start();
+ return ((IPEndPoint)listener.LocalEndpoint).Port;
+ } finally {
+ listener.Stop();
+ }
+ }
+ }
+
+ ///
+ /// Code to simplify OAuth 2
+ ///
+ 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";
+
+ ///
+ /// Generate an OAuth 2 Token by using the supplied code
+ ///
+ /// Code to get the RefreshToken
+ /// OAuth2Settings to update with the information that was retrieved
+ public static void GenerateRefreshToken(OAuth2Settings settings) {
+ IDictionary data = new Dictionary();
+ // 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 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;
+ }
+
+ ///
+ /// 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
+ ///
+ ///
+ public static void GenerateAccessToken(OAuth2Settings settings) {
+ IDictionary data = new Dictionary();
+ 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 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;
+ }
+ }
+
+ ///
+ /// Authenticate by using the mode specified in the settings
+ ///
+ /// OAuth2Settings
+ /// false if it was canceled, true if it worked, exception if not
+ 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;
+ }
+
+ ///
+ /// Authenticate via an embedded browser
+ /// If this works, return the code
+ ///
+ /// OAuth2Settings with the Auth / Token url etc
+ /// true if completed, false if canceled
+ 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;
+ }
+
+ ///
+ /// Authenticate via a local server by using the LocalServerCodeReceiver
+ /// If this works, return the code
+ ///
+ /// OAuth2Settings with the Auth / Token url etc
+ /// true if completed
+ private static bool AuthenticateViaLocalServer(OAuth2Settings settings) {
+ var codeReceiver = new LocalServerCodeReceiver();
+ IDictionary 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;
+ }
+
+ ///
+ /// Simple helper to add the Authorization Bearer header
+ ///
+ /// WebRequest
+ /// OAuth2Settings
+ public static void AddOAuth2Credentials(HttpWebRequest webRequest, OAuth2Settings settings) {
+ if (!string.IsNullOrEmpty(settings.AccessToken)) {
+ webRequest.Headers.Add("Authorization", "Bearer " + settings.AccessToken);
+ }
+ }
+
+ ///
+ /// Check and authenticate or refresh tokens
+ ///
+ /// OAuth2Settings
+ 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");
+ }
+ }
+
+ ///
+ /// CreateWebRequest ready for OAuth 2 access
+ ///
+ /// HTTPMethod
+ ///
+ /// OAuth2Settings
+ /// HttpWebRequest
+ public static HttpWebRequest CreateOAuth2WebRequest(HTTPMethod method, string url, OAuth2Settings settings) {
+ CheckAndAuthenticateOrRefresh(settings);
+
+ HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, method);
+ AddOAuth2Credentials(webRequest, settings);
+ return webRequest;
+ }
+ }
}
diff --git a/GreenshotPlugin/Core/SourceForgeHelper.cs b/GreenshotPlugin/Core/SourceForgeHelper.cs
index 80928600e..c54c27ff5 100644
--- a/GreenshotPlugin/Core/SourceForgeHelper.cs
+++ b/GreenshotPlugin/Core/SourceForgeHelper.cs
@@ -18,6 +18,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+
using System;
using System.Collections.Generic;
using System.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;
}
}
///
@@ -113,10 +114,9 @@ namespace GreenshotPlugin.Core {
///
/// Dictionary> with files and their RssFile "description"
public static Dictionary> 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
diff --git a/GreenshotPlugin/Core/EncryptionHelper.cs b/GreenshotPlugin/Core/StringExtensions.cs
similarity index 52%
rename from GreenshotPlugin/Core/EncryptionHelper.cs
rename to GreenshotPlugin/Core/StringExtensions.cs
index bd76e53a7..9cbec97b4 100644
--- a/GreenshotPlugin/Core/EncryptionHelper.cs
+++ b/GreenshotPlugin/Core/StringExtensions.cs
@@ -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 .
- */
-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";
-
- ///
- /// A simply rijndael aes encryption, can be used to store passwords
- ///
- /// the string to call upon
- /// an encryped string in base64 form
- 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;
- }
-
- ///
- /// A simply rijndael aes decryption, can be used to store passwords
- ///
- /// a base64 encoded rijndael encrypted string
- /// Decrypeted text
- 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 .
+ */
+
+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";
+
+ ///
+ /// Format a string with the specified object
+ ///
+ /// String with formatting, like {name}
+ /// Object used for the formatting
+ /// Formatted string
+ public static string FormatWith(this string format, object source) {
+ return FormatWith(format, null, source);
+ }
+
+ ///
+ /// Format the string "format" with the source
+ ///
+ ///
+ ///
+ /// object with properties, if a property has the type IDictionary string,string it can used these parameters too
+ /// Formatted string
+ public static string FormatWith(this string format, IFormatProvider provider, object source) {
+ if (format == null) {
+ throw new ArgumentNullException("format");
+ }
+
+ IDictionary properties = new Dictionary();
+ foreach(var propertyInfo in source.GetType().GetProperties()) {
+ if (propertyInfo.CanRead && propertyInfo.CanWrite) {
+ object value = propertyInfo.GetValue(source, null);
+ if (propertyInfo.PropertyType != typeof(IDictionary)) {
+ properties.Add(propertyInfo.Name, value);
+ } else {
+ IDictionary dictionary = (IDictionary)value;
+ foreach (var propertyKey in dictionary.Keys) {
+ properties.Add(propertyKey, dictionary[propertyKey]);
+ }
+ }
+ }
+ }
+
+ Regex r = new Regex(@"(?\{)+(?[\w\.\[\]]+)(?:[^}]+)?(?\})+", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
+
+ List