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 values = new List(); + string rewrittenFormat = r.Replace(format, delegate(Match m) { + Group startGroup = m.Groups["start"]; + Group propertyGroup = m.Groups["property"]; + Group formatGroup = m.Groups["format"]; + Group endGroup = m.Groups["end"]; + + object value; + if (properties.TryGetValue(propertyGroup.Value, out value)) { + values.Add(value); + } else { + values.Add(source); + } + return new string('{', startGroup.Captures.Count) + (values.Count - 1) + formatGroup.Value + new string('}', endGroup.Captures.Count); + }); + + return string.Format(provider, rewrittenFormat, values.ToArray()); + } + + /// + /// 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; + } + + /// + /// Read "streamextensions" :) + /// + /// Stream + /// Stream + public static void CopyTo(this Stream input, Stream output) { + byte[] buffer = new byte[16 * 1024]; // Fairly arbitrary size + int bytesRead; + + while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0) { + output.Write(buffer, 0, bytesRead); + } + } + } +} diff --git a/GreenshotPlugin/Core/WmInputLangChangeRequestFilter.cs b/GreenshotPlugin/Core/WmInputLangChangeRequestFilter.cs new file mode 100644 index 000000000..f1caf717c --- /dev/null +++ b/GreenshotPlugin/Core/WmInputLangChangeRequestFilter.cs @@ -0,0 +1,65 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2015 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using GreenshotPlugin.UnmanagedHelpers; +using System.Windows.Forms; +using log4net; + +namespace GreenshotPlugin.Core +{ + /// + /// This IMessageFilter filters out all WM_INPUTLANGCHANGEREQUEST messages which go to a handle which is >32 bits. + /// The need for this is documented here: http://stackoverflow.com/a/32021586 + /// + public class WmInputLangChangeRequestFilter : IMessageFilter + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(WmInputLangChangeRequestFilter)); + + /// + /// This will do some filtering + /// + /// Message + /// true if the message should be filtered + public bool PreFilterMessage(ref Message m) + { + return PreFilterMessageExternal(ref m); + } + + /// + /// Also used in the MainForm WndProc + /// + /// Message + /// true if the message should be filtered + public static bool PreFilterMessageExternal(ref Message m) + { + WindowsMessages message = (WindowsMessages)m.Msg; + if (message == WindowsMessages.WM_INPUTLANGCHANGEREQUEST || message == WindowsMessages.WM_INPUTLANGCHANGE) + { + LOG.WarnFormat("Filtering: {0}, {1:X} - {2:X} - {3:X}", message, m.LParam.ToInt64(), m.WParam.ToInt64(), m.HWnd.ToInt64()); + // For now we always return true + return true; + // But it could look something like this: + //return (m.LParam.ToInt64() | 0x7FFFFFFF) != 0; + } + return false; + } + } +} \ No newline at end of file diff --git a/GreenshotPlugin/GreenshotPlugin.csproj b/GreenshotPlugin/GreenshotPlugin.csproj index bac1fe128..e9c313f6f 100644 --- a/GreenshotPlugin/GreenshotPlugin.csproj +++ b/GreenshotPlugin/GreenshotPlugin.csproj @@ -37,6 +37,7 @@ + @@ -155,7 +156,7 @@ - + diff --git a/GreenshotPlugin/IniFile/IniSection.cs b/GreenshotPlugin/IniFile/IniSection.cs index 0ff3b3279..40aba49f9 100644 --- a/GreenshotPlugin/IniFile/IniSection.cs +++ b/GreenshotPlugin/IniFile/IniSection.cs @@ -58,7 +58,10 @@ namespace Greenshot.IniFile { /// /// Flag to specify if values have been changed /// - public bool IsDirty = false; + public bool IsDirty { + get; + set; + } /// /// Supply values we can't put as defaults diff --git a/appveyor.yml b/appveyor.yml index dafbe09e0..2b4283e3e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,9 +1,11 @@ -version: 1.3.0.{build} +version: 1.2.7.{build} branches: only: - - master + - 1.2 +skip_tags: true configuration: Release platform: Any CPU +clone_depth: 1 assembly_info: patch: true file: '**\AssemblyInfo.*' @@ -11,32 +13,40 @@ assembly_info: assembly_file_version: '{version}' assembly_informational_version: '{version}-$(build_type)-$(APPVEYOR_REPO_COMMIT)' environment: - credentials_box_client_id: key - credentials_box_client_secret: secret - credentials_dropbox_consumer_key: key - credentials_dropbox_consumer_secret: secret - credentials_flickr_consumer_key: key - credentials_flickr_consumer_secret: secret - credentials_imgur_consumer_key: key - credentials_imgur_consumer_secret: secret - credentials_photobucket_consumer_key: key - credentials_photobucket_consumer_secret: secret - credentials_picasa_consumer_key: key - credentials_picasa_consumer_secret: secret - sourceforge_host: frs.sourceforge.net - sourceforge_user: user - sourceforge_password: password - sourceforge_hostkey: ssh-rsa 2048 b0:a8:eb:30:ce:1a:0e:6a:4d:7a:6b:3a:0a:c6:27:60 - sourceforge_targetpath: /home/frs/project/greenshot/Greenshot/Greenshot 1.3/ - build_type: UNSTABLE - rsakey: key + credentials_box_client_id: + secure: 8MKxTOowo2fat6cNXGbFfvn6typiEtmCKsrptrWiEFUEoKlT1DUn40iGNcIELRA1 + credentials_box_client_secret: + secure: hJhzDVJuGd/WMnoSXhosvOM/1PGcYlKbtQjA6xyrmnmZcqCTMzqIdA6JXlo/V2Br + credentials_dropbox_consumer_key: + secure: Da/6KY1cu9CUM3iOqSpcUw== + credentials_dropbox_consumer_secret: + secure: KkyKyUY+buT/MZagXDP4cw== + credentials_flickr_consumer_key: + secure: fY8s0OkOMYwCjSZoL/6yZcP8xeT6J2EJLjbUMI5lAW42S5QT2U2B41KrmeP2NpnQ + credentials_flickr_consumer_secret: + secure: 9TthlljPHXWPkDDeG3uiFVJ9YJwHZOV0ZsojaIBBuvw= + credentials_imgur_consumer_key: + secure: z8S4QZ3/InPe3dgCf0CNyS0VGKuRyjjP8WMAq+AkK5OZJxZcbIxwobjgelE5CWYL + credentials_imgur_consumer_secret: + secure: ovfXJRorkkKUzbMXuZ4m0U6KF4icngmS+nzSljXJGSKfhI+GNXbMNa//mKYfTCXI + credentials_photobucket_consumer_key: + secure: oo9GD1Y8dkrli6hMfnnYsw== + credentials_photobucket_consumer_secret: + secure: GiNPoe9klM/YkoHIA/YHqOYrIaYwSFK7Ph9m8jT9uPP1l6+Hd5K8dVMw5DNa50oG + credentials_picasa_consumer_key: + secure: bjKXhFZkDqaq98XBrz5oQKQfT8CLpuv2ZAiBIwkzloaAPUs97b5yx6h/xFaE4NLS + credentials_picasa_consumer_secret: + secure: yNptTpmJWypbu9alOQtetxU66drr2FKxoPflNgRJdag= + build_type: RELEASE + rsakey: + secure: GNomwdlwZOCyd8d7xEWTnMVs1lpOeHvF+tlnvcbXGovLRtwAp2Ufu0r7paGY7BHGGkIs2WE7xUfyQ9UauVB+58JZ6fwVega8ucUgVJhl4x0QQNN2d6sULUhHfhuEHmxw+FDO/FxKFE6Lmf+ZRY+OGiw0wKIl4qD7mGRHcDQTipNEsTbau8HzqRVCdu3dx7pODC61DlsbO71xLF7UlqnmuZE+91Zz3V6AgaqE246n1499d6bXBYw1AH+8opNnKDFLnTHf7hUVcZn9mj6tKZXeTCuVUOr/SVQcgHKxlBlqzhfaEkxCR5GPtzQRqwDMxEycmFvj2wNP/sie6UEGhQxE4YMCc2OgqNOkpc5BbP/fxLr/SLFOEf1XXzTWCFMhsgpHx7TZbgQH26sa0rK/xaBRacZlwAaNk7V2nFZT7TebYEFy6zWNr9Y+IyeXIofj42XQTNXv8d8hyh+UYLByVEFYRf2DnActQkZQyNdWjZ+CxDV50QSZZs8FT3IIqraHYKsj2ITAN5LrUtWCi7bpNJL0UGo0EJiB2i0bp++tEAAwyrCljxI8d4bbGl/flHk/xd+ysQPnomndijeObjguEzqT8pyXZluSZhF+lI50mIDhMdtdAfMi5yn5RW7P6NWOSlC8xgQQgMZylsuSvRflKbEd/gsoDyEOnakNcdH2jekt9OD6GnuYM7iHkbMC89LBZ0VaHNGvCC+BQXdGUG7O9R3NthZcDXE7q7xbtGRB5ncVQDRfKoT5HVfiV6bSDrcfRODiuR59mZgiSYtZG+3kQWYUKn2wagvZKckGukA0SlOuTRCKZhgLcVHhWeRWeGE3iJ8K6BeHf2EgB8Qr6ayTyTUjBcn+u4qqWKgkvG4qRavlvrBSdMrAXWIKE8vSq1od0A2ZzP6+HCsrkuUR+HFfpE2dpjeckoa5vATQgyn8j5x11iIOB9HnT3YKbZ0aTU4rQgYMJXA/fPcgKDGkAPdgtGbQLssy/mwSdsXBYtMgEcs7vI9laR8Ik+NK2dbFHGFPnxS43WToGyKBxojt8SZbgPJXm22WRrN1+9AZvvhI7/mpZiEE7HWgNRClZYuqbfCMpelLGvVq832OLjelrWMJ0XBVNHnOw0p8qZKI1UpqQJXX1nL8j3JttEVHsfryIanM03kNDL0dX1VAKECKUMCVQ6i6tG4VWsR0C2JccPJ3PSoPgo5KMJhuZNaBoiPjZ2eaMREV6vUYbBYzrvdDQzUcE2stacREl4eJzGJ4GP5h08GQmIirGF/SCyZV1CadAbKZVjqb70XpIbE6NT/+84O82LZR4ui5KgTAv87lTZgvNJ7LxM7rRg1awj/iBxQeARNJxuPMPlk1CVx8Z3091UdL1K1avPKa85lCRwCkDKLcJPO9tlqi4dVjCrwpoCJkQMm3fbTl/BgHn00/RsnFZ2qfl5m2DyF+XuaOPauzsRdLUFAC4h44qoUuzRb4Pv6RFhN5CI4fddRKafNBHU9f69UCkO080/hIjTdj0+bpr4oNY4UEi80huyJY/c0iUPE8o48qBB8F3cW30SwhPmuphn4/18lB8GEwEPqoatmli4QRaDFUCUf9Hj0DEUqEAya/OHOW7/PvWcw/l/ZaIMUpOZ6q0xvPDAXokFRJAWzZhG7hNbWNEzQ3f/BjlYlYsBtMY0JUU8mH6YxwIzIGbHiLTBC0OglH0rDd5W+3NaUG9FZ//o9MAP5j2QqwSuFWXppbigh4zk+h17eJn5zhld7dtvOr+YmgYULj6NFIDKBZHUJdqLYScVzdc1p812FCCBcLmmw4RnwuF+RldHixTdy4UZ17T/hD4OLpWCINl9lUAficC0OFeLJLHxFW6Em8SCbZ3aUtFDIQD8oTqzUHZhGWYF2ukrOc8Dzm4FQ8xy3BhqfntTod1gwoilIirsP/z+GGMnTltkqiqK+gCmkVOfICwNFmHltZeJrmDQ4YU5abR09Yr1TaAk3CzWjV1XGBaf/oek0+tFkMOtZNdFRdlzLLE90PsZZFFnZhFBoNoOhYnMB9K2VqgEpJs0nXvF6qBOllptcpBYUYMzMdb0Ggu6m1d/phxuBuOsm+Xtr0Zw8Xd0vxIOQNDGsskCDIEUYWYajw2i66MmRPRyFEennXfLA0WIPpztXvfsrKjf42rjE3RukBsRff1Sci68cel4fGfmvj2y7gW0Tt before_build: -- ps: iex (new-object Net.WebClient).DownloadString("https://bitbucket.org/greenshot/greenshot/raw/master/prebuild.ps1") +- ps: iex (new-object Net.WebClient).DownloadString("https://bitbucket.org/greenshot/greenshot/raw/1.2/prebuild.ps1") build: project: greenshot\greenshot.sln verbosity: normal after_build: -- ps: iex (new-object Net.WebClient).DownloadString("https://bitbucket.org/greenshot/greenshot/raw/master/build.ps1") +- ps: iex (new-object Net.WebClient).DownloadString("https://bitbucket.org/greenshot/greenshot/raw/1.2/build.ps1") test: off artifacts: - path: Greenshot\releases\Greenshot*INSTALLER*.exe @@ -49,12 +59,26 @@ artifacts: name: Readme - path: Greenshot\releases\Greenshot-DEBUGSYMBOLS*.zip name: DEBUGSYMBOLS -deploy_script: -- ps: iex (new-object Net.WebClient).DownloadString("https://bitbucket.org/greenshot/greenshot/raw/master/deploy.ps1") +deploy: +- provider: GitHub + tag: Greenshot-$(build_type)-$(APPVEYOR_BUILD_VERSION) + description: + auth_token: + secure: 4sYcNGg7byBFtR7EkJHS8d3H3qP0u0LodlJWCV7g/4jEyv3vvVxqzh19zZ6Zgrf1 + prerelease: true + on: + build_type: UNSTABLE +- provider: GitHub + tag: Greenshot-$(build_type)-$(APPVEYOR_BUILD_VERSION) + auth_token: + secure: 4sYcNGg7byBFtR7EkJHS8d3H3qP0u0LodlJWCV7g/4jEyv3vvVxqzh19zZ6Zgrf1 + on: + build_type: RELEASE notifications: - provider: Email to: - robin@getgreenshot.org - on_build_success: false - on_build_failure: false - on_build_status_changed: true \ No newline at end of file + - jens@getgreenshot.org + on_build_success: true + on_build_failure: true + on_build_status_changed: false \ No newline at end of file diff --git a/deploy.ps1 b/deploy.ps1 deleted file mode 100644 index 1e6d2695e..000000000 --- a/deploy.ps1 +++ /dev/null @@ -1,80 +0,0 @@ -################################################################ -# Greenshot DEPLOY script, written for the Windows Power Shell -# Assumes the installation of Microsoft .NET Framework 4.5 -################################################################ -# Greenshot - a free and open source screenshot tool -# Copyright (C) 2007-2014 Thomas Braun, Jens Klingen, Robin Krom -# -# For more information see: http://getgreenshot.org/ -# The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 1 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -################################################################ - -# This script needs some environment variables to work: -# $env:sourceforge_host with the hostname -# $env:sourceforge_user with the username -# $env:sourceforge_password with the password for the username -# $env:sourceforge_hostkey with the hosts key (ssh-rsa 2048 xx:xx:xx:xx:...) -# $env:sourceforge_targetpath with the target location - -try { - # Load WinSCP .NET assembly - Add-Type -Path "Greenshot\tools\WinSCP\WinSCPnet.dll" - - # Setup session options - $sessionOptions = New-Object WinSCP.SessionOptions - $sessionOptions.Protocol = [WinSCP.Protocol]::Sftp - $sessionOptions.HostName = $env:sourceforge_host - $sessionOptions.UserName = $env:sourceforge_user - $sessionOptions.Password = $env:sourceforge_password - $sessionOptions.SshHostKeyFingerprint = $env:sourceforge_hostkey - - $session = New-Object WinSCP.Session - - try { - # Connect - $session.Open($sessionOptions) - - # Upload files - $transferOptions = New-Object WinSCP.TransferOptions - $transferOptions.TransferMode = [WinSCP.TransferMode]::Binary - - $artifactbase = "$(get-location)\Greenshot\releases" - # The list of all the artifacts that need to be uploaded - @( - "$artifactbase\Greenshot-INSTALLER*.exe", - "$artifactbase\Greenshot-NO-INSTALLER*.zip", - "$artifactbase\Greenshot_for_PortableApps*.exe", - "$artifactbase\additional_files\readme.txt" - ) | foreach { - $transferResult = $session.PutFiles($_ , $env:sourceforge_targetpath, $False, $transferOptions) - - # Throw on any error - $transferResult.Check() - - # Print results - foreach ($transfer in $transferResult.Transfers) { - Write-Host ("Upload of {0} to {1} succeeded" -f $transfer.FileName, $env:sourceforge_host) - } - } - } finally { - # Disconnect, clean up - $session.Dispose() - } - -} catch [Exception] { - Write-Host "Error: "$_.Exception - exit 1 -} \ No newline at end of file