diff --git a/GreenshotImgurPlugin/ImgurUtils.cs b/GreenshotImgurPlugin/ImgurUtils.cs index afe8a240c..6df2f0daf 100644 --- a/GreenshotImgurPlugin/ImgurUtils.cs +++ b/GreenshotImgurPlugin/ImgurUtils.cs @@ -100,39 +100,34 @@ namespace GreenshotImgurPlugin { /// byte[] with image data /// ImgurResponse public static ImgurInfo UploadToImgur(byte[] imageData, int dataLength, string title, string filename) { - StringBuilder uploadRequest = new StringBuilder(); + IDictionary uploadParameters = new Dictionary(); // Add image - uploadRequest.Append("image="); - uploadRequest.Append(OAuthHelper.UrlEncode3986(System.Convert.ToBase64String(imageData, 0, dataLength))); + uploadParameters.Add("image", System.Convert.ToBase64String(imageData, 0, dataLength)); // add type - uploadRequest.Append("&type=base64"); + uploadParameters.Add("type", "base64"); // add title if (title != null) { - uploadRequest.Append("&title="); - uploadRequest.Append(OAuthHelper.UrlEncode3986(title)); + uploadParameters.Add("title", title); } // add filename if (filename != null) { - uploadRequest.Append("&name="); - uploadRequest.Append(OAuthHelper.UrlEncode3986(filename)); + uploadParameters.Add("name", filename); } - string url; - string responseString; + string responseString = null; if (config.AnonymousAccess) { // add key - uploadRequest.Append("&key="); - uploadRequest.Append(IMGUR_ANONYMOUS_API_KEY); - url = config.ImgurApiUrl + "/upload"; - HttpWebRequest webRequest = (HttpWebRequest)NetworkHelper.CreateWebRequest(url); + uploadParameters.Add("key", IMGUR_ANONYMOUS_API_KEY); + HttpWebRequest webRequest = (HttpWebRequest)NetworkHelper.CreateWebRequest(config.ImgurApiUrl + "/upload"); webRequest.Method = "POST"; webRequest.ContentType = "application/x-www-form-urlencoded"; webRequest.ServicePoint.Expect100Continue = false; using(StreamWriter streamWriter = new StreamWriter(webRequest.GetRequestStream())) { - streamWriter.Write(uploadRequest.ToString()); + string urloadText = NetworkHelper.GenerateQueryParameters(uploadParameters); + streamWriter.Write(urloadText); } using (WebResponse response = webRequest.GetResponse()) { LogCredits(response); @@ -142,37 +137,38 @@ namespace GreenshotImgurPlugin { } } else { - url = config.ImgurApiUrl + "/account/images"; - OAuthHelper oAuth = new OAuthHelper(); + OAuthSession oAuth = new OAuthSession(); oAuth.BrowserWidth = 650; oAuth.BrowserHeight = 500; oAuth.CallbackUrl = "http://getgreenshot.org"; oAuth.AccessTokenUrl = "http://api.imgur.com/oauth/access_token"; oAuth.AuthorizeUrl = "http://api.imgur.com/oauth/authorize"; oAuth.RequestTokenUrl = "http://api.imgur.com/oauth/request_token"; - oAuth.ConsumerKey = ImgurCredentials.CONSUMER_KEY; - oAuth.ConsumerSecret = ImgurCredentials.CONSUMER_SECRET; + oAuth.ConsumerKey = "907d4455b8c38144d68c4f72190af4c40504a0ac7"; + oAuth.ConsumerSecret = "d33902ef409fea163ab755454c15b3d0"; oAuth.UserAgent = "Greenshot"; - if (string.IsNullOrEmpty(config.ImgurToken)) { - LOG.Debug("Creating Imgur Token"); - oAuth.getRequestToken(); - if (string.IsNullOrEmpty(oAuth.authorizeToken("Imgur authorization"))) { - LOG.Debug("User didn't authenticate!"); - return null; + oAuth.LoginTitle = "Imgur authorization"; + //oAuth.UseHTTPHeadersForAuthorization = false; + oAuth.Token = config.ImgurToken; + oAuth.TokenSecret = config.ImgurTokenSecret; + try { + LOG.DebugFormat("Test: {0}", oAuth.oAuthWebRequest(HTTPMethod.GET, "http://api.imgur.com/2/account", null)); + responseString = oAuth.oAuthWebRequest(HTTPMethod.POST, "http://api.imgur.com/2/account/images.xml", uploadParameters); + } catch (Exception ex) { + LOG.Error("Upload to imgur gave an exeption: ", ex); + throw ex; + } finally { + if (oAuth.Token != null) { + config.ImgurToken = oAuth.Token; } - string accessToken = oAuth.getAccessToken(); - config.ImgurToken = oAuth.Token; - config.ImgurTokenSecret = oAuth.TokenSecret; - } else { - LOG.Debug("Using stored Imgur Token"); - oAuth.Token = config.ImgurToken; - oAuth.TokenSecret = config.ImgurTokenSecret; + if (oAuth.TokenSecret != null) { + config.ImgurTokenSecret = oAuth.TokenSecret; + } + IniConfig.Save(); } - responseString = oAuth.oAuthWebRequest(OAuthHelper.Method.POST, url, uploadRequest.ToString()); } LOG.Info(responseString); ImgurInfo imgurInfo = ImgurInfo.ParseResponse(responseString); - LOG.Debug("Upload to imgur was finished"); return imgurInfo; } diff --git a/GreenshotPhotobucketPlugin/PhotobucketUtils.cs b/GreenshotPhotobucketPlugin/PhotobucketUtils.cs index 106d9293d..15e5637ce 100644 --- a/GreenshotPhotobucketPlugin/PhotobucketUtils.cs +++ b/GreenshotPhotobucketPlugin/PhotobucketUtils.cs @@ -48,59 +48,39 @@ namespace GreenshotPhotobucketPlugin { /// byte[] with image data /// PhotobucketResponse public static PhotobucketInfo UploadToPhotobucket(byte[] imageData, int dataLength, string title, string filename) { - StringBuilder uploadRequest = new StringBuilder(); - uploadRequest.Append("identifier=greenshot"); - // add type - uploadRequest.Append("&type=base64"); - // Add image - uploadRequest.Append("&uploadfile="); - uploadRequest.Append(OAuthHelper.UrlEncode3986(System.Convert.ToBase64String(imageData, 0, dataLength))); - // add title - if (title != null) { - uploadRequest.Append("&title="); - uploadRequest.Append(OAuthHelper.UrlEncode3986(title)); - } - // add filename - if (filename != null) { - uploadRequest.Append("&filename="); - uploadRequest.Append(OAuthHelper.UrlEncode3986(filename)); - } - string url = "http://api.photobucket.com/album/greenshot/upload"; string responseString; - OAuthHelper oAuth = new OAuthHelper(); + OAuthSession oAuth = new OAuthSession(); // This url is configured in the Photobucket API settings in the Photobucket site!! oAuth.CallbackUrl = "http://getgreenshot.org"; oAuth.AccessTokenUrl = "http://api.photobucket.com/login/access"; oAuth.AuthorizeUrl = "http://photobucket.com/apilogin/login"; oAuth.RequestTokenUrl = "http://api.photobucket.com/login/request"; - oAuth.ConsumerKey = ; - oAuth.ConsumerSecret = ; + oAuth.ConsumerKey = "149833145"; + oAuth.ConsumerSecret = "ebd828180b11103c010c7e71c66f6bcb"; oAuth.UserAgent = "Greenshot"; oAuth.BrowserWidth = 1010; oAuth.BrowserHeight = 400; oAuth.CheckVerifier = false; - if (string.IsNullOrEmpty(config.PhotobucketToken)) { - LOG.Debug("Creating Photobucket Token"); - try { - oAuth.getRequestToken(); - } catch (Exception ex) { - LOG.Error(ex); - throw new NotSupportedException("Photobucket is not available: " + ex.Message); - } - LOG.Debug("Authorizing Photobucket"); - if (string.IsNullOrEmpty(oAuth.authorizeToken("Photobucket authorization"))) { - return null; - } - string accessToken = oAuth.getAccessToken(); - config.PhotobucketToken = oAuth.Token; - config.PhotobucketTokenSecret = oAuth.TokenSecret; - } else { - LOG.Debug("Using stored Photobucket Token"); - oAuth.Token = config.PhotobucketToken; - oAuth.TokenSecret = config.PhotobucketTokenSecret; + oAuth.LoginTitle = "Photobucket authorization"; + Dictionary parameters = new Dictionary(); + // add album + parameters.Add("identifier", "greenshot"); + // add type + parameters.Add("type", "base64"); + // Add image + parameters.Add("uploadfile", System.Convert.ToBase64String(imageData, 0, dataLength)); + // add title + if (title != null) { + parameters.Add("title", title); } - responseString = oAuth.oAuthWebRequest(OAuthHelper.Method.POST, url, uploadRequest.ToString()); + // add filename + if (filename != null) { + parameters.Add("filename", filename); + } + responseString = oAuth.oAuthWebRequest(HTTPMethod.POST, "http://api.photobucket.com/album/greenshot/upload", parameters, null, null); + oAuth.Token = config.PhotobucketToken; + oAuth.TokenSecret = config.PhotobucketTokenSecret; LOG.Info(responseString); PhotobucketInfo PhotobucketInfo = PhotobucketInfo.ParseResponse(responseString); LOG.Debug("Upload to Photobucket was finished"); diff --git a/GreenshotPlugin/Controls/OAuthLoginForm.cs b/GreenshotPlugin/Controls/OAuthLoginForm.cs index 2de8d2460..f1b2d786d 100644 --- a/GreenshotPlugin/Controls/OAuthLoginForm.cs +++ b/GreenshotPlugin/Controls/OAuthLoginForm.cs @@ -34,7 +34,7 @@ namespace GreenshotPlugin.Controls { /// public partial class OAuthLoginForm : Form { private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(OAuthLoginForm)); - private OAuthHelper _oauth; + private OAuthSession _oauthSession; private String _token; private String _verifier; private String _tokenSecret; @@ -57,18 +57,18 @@ namespace GreenshotPlugin.Controls { } } - public OAuthLoginForm(OAuthHelper o, string browserTitle, int width, int height) { - _oauth = o; + public OAuthLoginForm(OAuthSession o, string browserTitle, int width, int height) { + _oauthSession = o; _token = null; InitializeComponent(); this.ClientSize = new System.Drawing.Size(width, height); this.Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon(); this.Text = browserTitle; this.addressTextBox.Text = o.AuthorizationLink; - _token = _oauth.Token; - _tokenSecret = _oauth.TokenSecret; + _token = _oauthSession.Token; + _tokenSecret = _oauthSession.TokenSecret; browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(browser_DocumentCompleted); - browser.Navigate(new Uri(_oauth.AuthorizationLink)); + browser.Navigate(new Uri(_oauthSession.AuthorizationLink)); WindowDetails.ToForeground(this.Handle); } @@ -88,15 +88,15 @@ namespace GreenshotPlugin.Controls { } private void checkUrl() { - if (browser.Url.ToString().StartsWith(_oauth.CallbackUrl)) { + if (browser.Url.ToString().StartsWith(_oauthSession.CallbackUrl)) { string queryParams = browser.Url.Query; if (queryParams.Length > 0) { //Store the Token and Token Secret - NameValueCollection qs = NetworkHelper.ParseQueryString(queryParams); - if (qs["oauth_token"] != null) { + IDictionary qs = NetworkHelper.ParseQueryString(queryParams); + if (qs.ContainsKey("oauth_token") && qs["oauth_token"] != null) { _token = qs["oauth_token"]; } - if (qs["oauth_verifier"] != null) { + if (qs.ContainsKey("oauth_verifier") && qs["oauth_verifier"] != null) { _verifier = qs["oauth_verifier"]; } } diff --git a/GreenshotPlugin/Core/NetworkHelper.cs b/GreenshotPlugin/Core/NetworkHelper.cs index 3f34a63cb..bf2b3db00 100644 --- a/GreenshotPlugin/Core/NetworkHelper.cs +++ b/GreenshotPlugin/Core/NetworkHelper.cs @@ -23,11 +23,13 @@ using System.Drawing; using System.IO; using System.Net; using System.Text; +using System.Collections.Generic; using System.Collections.Specialized; using System.Text.RegularExpressions; using Greenshot.IniFile; using System.Security.Cryptography.X509Certificates; using System.Net.Security; +using System.Web; namespace GreenshotPlugin.Core { /// @@ -158,6 +160,26 @@ namespace GreenshotPlugin.Core { return null; } + /// + /// A wrapper around the EscapeDataString, as the limit is 32766 characters + /// See: http://msdn.microsoft.com/en-us/library/system.uri.escapedatastring%28v=vs.110%29.aspx + /// + /// + /// escaped data string + public static string EscapeDataString(string text) { + if (!string.IsNullOrEmpty(text)) { + StringBuilder result = new StringBuilder(); + int currentLocation = 0; + while (currentLocation < text.Length) { + string process = text.Substring(currentLocation, Math.Min(16384, text.Length - currentLocation)); + result.Append(Uri.EscapeDataString(process)); + currentLocation = currentLocation + 16384; + } + return result.ToString(); + } + return null; + } + /// /// UrlDecodes a string without requiring System.Web /// @@ -174,23 +196,50 @@ namespace GreenshotPlugin.Core { /// ParseQueryString without the requirement for System.Web /// /// - /// - public static NameValueCollection ParseQueryString(string s) { - NameValueCollection nvc = new NameValueCollection(); + /// Dictionary + public static IDictionary ParseQueryString(string s) { + IDictionary parameters = new SortedDictionary(); // remove anything other than query string from url if (s.Contains("?")) { s = s.Substring(s.IndexOf('?') + 1); } foreach (string vp in Regex.Split(s, "&")) { + if (string.IsNullOrEmpty(vp)) { + continue; + } string[] singlePair = Regex.Split(vp, "="); + if (parameters.ContainsKey(singlePair[0])) { + parameters.Remove(singlePair[0]); + } if (singlePair.Length == 2) { - nvc.Add(singlePair[0], singlePair[1]); + parameters.Add(singlePair[0], singlePair[1]); } else { // only one key with no value specified in query string - nvc.Add(singlePair[0], string.Empty); + parameters.Add(singlePair[0], string.Empty); } } - return nvc; + return parameters; } + + /// + /// Generate the query paramters + /// + /// the list of query parameters + /// a string with the query parameters + public static string GenerateQueryParameters(IDictionary queryParameters) { + if (queryParameters == null || queryParameters.Count == 0) { + return string.Empty; + } + + queryParameters = new SortedDictionary(queryParameters); + + StringBuilder sb = new StringBuilder(); + foreach(string key in queryParameters.Keys) { + sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0}={1}&", key, UrlEncode(queryParameters[key])); + } + sb.Remove(sb.Length-1,1); + + return sb.ToString(); + } } } diff --git a/GreenshotPlugin/Core/OAuthHelper.cs b/GreenshotPlugin/Core/OAuthHelper.cs index abf48a1dd..23d5e686c 100644 --- a/GreenshotPlugin/Core/OAuthHelper.cs +++ b/GreenshotPlugin/Core/OAuthHelper.cs @@ -29,88 +29,56 @@ using System.Text.RegularExpressions; using GreenshotPlugin.Controls; namespace GreenshotPlugin.Core { - public class OAuthHelper { - private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(OAuthHelper)); - /// - /// Provides a predefined set of algorithms that are supported officially by the protocol - /// - protected enum SignatureTypes { - HMACSHA1, - PLAINTEXT, - RSASHA1 - } + /// + /// Provides a predefined set of algorithms that are supported officially by the protocol + /// + public enum OAuthSignatureTypes { + HMACSHA1, + PLAINTEXT, + RSASHA1 + } + + public enum HTTPMethod { GET, POST, PUT, DELETE }; - /// - /// Provides an internal structure to sort the query parameter - /// - protected class QueryParameter { - private string name = null; - private string value = null; - - public QueryParameter(string name, string value) - { - this.name = name; - this.value = value; - } - - public string Name - { - get { return name; } - } - - public string Value - { - get { return value; } - } - } - - /// - /// Comparer class used to perform the sorting of the query parameters - /// - protected class QueryParameterComparer : IComparer { - - #region IComparer Members - - public int Compare(QueryParameter x, QueryParameter y) { - if (x.Name == y.Name) { - return string.Compare(x.Value, y.Value); - } else { - return string.Compare(x.Name, y.Name); - } - } - - #endregion - } - - protected const string OAuthVersion = "1.0"; - protected const string OAuthParameterPrefix = "oauth_"; + public class OAuthSession { + private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(OAuthSession)); + protected const string OAUTH_VERSION = "1.0"; + protected const string OAUTH_PARAMETER_PREFIX = "oauth_"; // // List of know and used oauth parameters' names // - protected const string OAuthConsumerKeyKey = "oauth_consumer_key"; - protected const string OAuthCallbackKey = "oauth_callback"; - protected const string OAuthVersionKey = "oauth_version"; - protected const string OAuthSignatureMethodKey = "oauth_signature_method"; - protected const string OAuthSignatureKey = "oauth_signature"; - protected const string OAuthTimestampKey = "oauth_timestamp"; - protected const string OAuthNonceKey = "oauth_nonce"; - protected const string OAuthTokenKey = "oauth_token"; - protected const string oAauthVerifierKey = "oauth_verifier"; - protected const string OAuthTokenSecretKey = "oauth_token_secret"; + protected const string OAUTH_CONSUMER_KEY_KEY = "oauth_consumer_key"; + protected const string OAUTH_CALLBACK_KEY = "oauth_callback"; + protected const string OAUTH_VERSION_KEY = "oauth_version"; + protected const string OAUTH_SIGNATURE_METHOD_KEY = "oauth_signature_method"; + protected const string OAUTH_TIMESTAMP_KEY = "oauth_timestamp"; + protected const string OAUTH_NONCE_KEY = "oauth_nonce"; + protected const string OAUTH_TOKEN_KEY = "oauth_token"; + protected const string OAUTH_VERIFIER_KEY = "oauth_verifier"; + protected const string OAUTH_TOKEN_SECRET_KEY = "oauth_token_secret"; + protected const string OAUTH_SIGNATURE_KEY = "oauth_signature"; protected const string HMACSHA1SignatureType = "HMAC-SHA1"; protected const string PlainTextSignatureType = "PLAINTEXT"; protected const string RSASHA1SignatureType = "RSA-SHA1"; - public enum Method { GET, POST, PUT, DELETE }; + + protected static Random random = new Random(); + + protected const string UNRESERVED_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; + private string _userAgent = "Greenshot"; private string _callbackUrl = "http://getgreenshot.org"; private bool checkVerifier = true; + private bool useHTTPHeadersForAuthorization = true; + private bool useAuthorization = true; + // default browser size private int _browserWidth = 864; private int _browserHeight = 587; - + private string loginTitle = "Authorize Greenshot access"; + #region PublicPropertiies public string ConsumerKey { get; set; } public string ConsumerSecret { get; set; } @@ -149,15 +117,36 @@ namespace GreenshotPlugin.Core { set; } public string TokenSecret { get; set; } + public string LoginTitle { + get { + return loginTitle; + } + set { + loginTitle = value; + } + } + public string Verifier { + get; + set; + } + public bool UseHTTPHeadersForAuthorization { + get { + return useHTTPHeadersForAuthorization; + } + set { + useHTTPHeadersForAuthorization = value; + } + } + public bool UseAuthorization { + get { + return useAuthorization; + } + set { + useAuthorization = value; + } + } + #endregion - - protected Random random = new Random(); - - private string oauth_verifier; - public string Verifier { get { return oauth_verifier; } set { oauth_verifier = value; } } - - - protected const string unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; /// /// Helper function to compute a hash value @@ -165,7 +154,7 @@ namespace GreenshotPlugin.Core { /// The hashing algorithm used. If that algorithm needs some initialization, like HMAC and its derivatives, they should be initialized prior to passing it to this function /// The data to hash /// a Base64 string of the hash value - private string ComputeHash(HashAlgorithm hashAlgorithm, string data) { + private static string ComputeHash(HashAlgorithm hashAlgorithm, string data) { if (hashAlgorithm == null) { throw new ArgumentNullException("hashAlgorithm"); } @@ -174,56 +163,31 @@ namespace GreenshotPlugin.Core { throw new ArgumentNullException("data"); } - byte[] dataBuffer = System.Text.Encoding.ASCII.GetBytes(data); + byte[] dataBuffer = System.Text.Encoding.UTF8.GetBytes(data); byte[] hashBytes = hashAlgorithm.ComputeHash(dataBuffer); return Convert.ToBase64String(hashBytes); } /// - /// Internal function to cut out all non oauth query string parameters (all parameters not begining with "oauth_") + /// Generate the normalized paramter string /// - /// The query string part of the Url - /// A list of QueryParameter each containing the parameter name and value - private List GetQueryParameters(string parameters) { - if (parameters.StartsWith("?")) { - parameters = parameters.Remove(0, 1); + /// the list of query parameters + /// a string with the normalized query parameters + private static string GenerateNormalizedParametersString(IDictionary queryParameters) { + if (queryParameters == null || queryParameters.Count == 0) { + return string.Empty; } - List result = new List(); + queryParameters = new SortedDictionary(queryParameters); - if (!string.IsNullOrEmpty(parameters)) { - string[] p = parameters.Split('&'); - foreach (string s in p) { - if (!string.IsNullOrEmpty(s) && !s.StartsWith(OAuthParameterPrefix)) { - if (s.IndexOf('=') > -1) { - string[] temp = s.Split('='); - result.Add(new QueryParameter(temp[0], temp[1])); - } else { - result.Add(new QueryParameter(s, string.Empty)); - } - } - } + StringBuilder sb = new StringBuilder(); + foreach (string key in queryParameters.Keys) { + sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0}={1}&", key, UrlEncode3986(queryParameters[key])); } + sb.Remove(sb.Length - 1, 1); - return result; - } - - /// - /// A wrapper around the EscapeDataString, as the limit is 32766 characters - /// See: http://msdn.microsoft.com/en-us/library/system.uri.escapedatastring%28v=vs.110%29.aspx - /// - /// - /// escaped data string - private static StringBuilder EscapeDataStringToStringBuilder(string dataString) { - StringBuilder result = new StringBuilder(); - int currentLocation = 0; - while (currentLocation < dataString.Length) { - string process = dataString.Substring(currentLocation, Math.Min(16384, dataString.Length - currentLocation)); - result.Append(Uri.EscapeDataString(process)); - currentLocation = currentLocation + 16384; - } - return result; + return sb.ToString(); } /// @@ -232,170 +196,26 @@ namespace GreenshotPlugin.Core { /// /// The value to Url encode /// Returns a Url encoded string - public static string UrlEncode3986(string text) { - string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" }; - LOG.DebugFormat("Text size {0}", text.Length); - StringBuilder escaped = EscapeDataStringToStringBuilder(text); + /// This will cause an ignorable CA1055 warning in code analysis. + private static string UrlEncode3986(string value) { + StringBuilder result = new StringBuilder(); - for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++) { - escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0])); - } - return escaped.ToString(); - } - /// - /// Normalizes the request parameters according to the spec - /// - /// The list of parameters already sorted - /// a string representing the normalized parameters - protected string NormalizeRequestParameters(IList parameters) { - StringBuilder sb = new StringBuilder(); - QueryParameter p = null; - for (int i = 0; i < parameters.Count; i++) { - p = parameters[i]; - sb.AppendFormat("{0}={1}", p.Name, p.Value); - - if (i < parameters.Count - 1) { - sb.Append("&"); + foreach (char symbol in value) { + if (UNRESERVED_CHARS.IndexOf(symbol) != -1) { + result.Append(symbol); + } else { + result.Append('%' + String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:X2}", (int)symbol)); } } - return sb.ToString(); - } - - /// - /// Generate the signature base that is used to produce the signature - /// - /// The full url that needs to be signed including its non OAuth url parameters - /// The consumer key - /// The token, if available. If not available pass null or an empty string - /// The token secret, if available. If not available pass null or an empty string - /// The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc) - /// The signature type. To use the default values use OAuthBase.SignatureTypes. - /// The signature base - protected string GenerateSignatureBase(Uri url, string consumerKey, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, string callback, string signatureType, out string normalizedUrl, out string normalizedRequestParameters) { - if (token == null) { - token = string.Empty; - } - - if (tokenSecret == null) { - tokenSecret = string.Empty; - } - - if (string.IsNullOrEmpty(consumerKey)) { - throw new ArgumentNullException("consumerKey"); - } - - if (string.IsNullOrEmpty(httpMethod)) { - throw new ArgumentNullException("httpMethod"); - } - - if (string.IsNullOrEmpty(signatureType)) { - throw new ArgumentNullException("signatureType"); - } - - normalizedUrl = null; - normalizedRequestParameters = null; - - List parameters = GetQueryParameters(url.Query); - parameters.Add(new QueryParameter(OAuthVersionKey, OAuthVersion)); - parameters.Add(new QueryParameter(OAuthNonceKey, nonce)); - parameters.Add(new QueryParameter(OAuthTimestampKey, timeStamp)); - parameters.Add(new QueryParameter(OAuthSignatureMethodKey, signatureType)); - parameters.Add(new QueryParameter(OAuthConsumerKeyKey, consumerKey)); - - //TODO: Make this less of a hack - if (!string.IsNullOrEmpty(callback)) { - parameters.Add(new QueryParameter(OAuthCallbackKey, UrlEncode3986(callback))); - } - - if (!string.IsNullOrEmpty(token)) { - parameters.Add(new QueryParameter(OAuthTokenKey, token)); - } - - if (!string.IsNullOrEmpty(oauth_verifier)) { - parameters.Add(new QueryParameter(oAauthVerifierKey, oauth_verifier)); - } - - - parameters.Sort(new QueryParameterComparer()); - - - normalizedUrl = string.Format("{0}://{1}", url.Scheme, url.Host); - if (!((url.Scheme == "http" && url.Port == 80) || (url.Scheme == "https" && url.Port == 443))) { - normalizedUrl += ":" + url.Port; - } - normalizedUrl += url.AbsolutePath; - normalizedRequestParameters = NormalizeRequestParameters(parameters); - - StringBuilder signatureBase = new StringBuilder(); - signatureBase.AppendFormat("{0}&", httpMethod.ToUpper()); - signatureBase.AppendFormat("{0}&", UrlEncode3986(normalizedUrl)); - signatureBase.AppendFormat("{0}", UrlEncode3986(normalizedRequestParameters)); - - return signatureBase.ToString(); - } - - /// - /// Generate the signature value based on the given signature base and hash algorithm - /// - /// The signature based as produced by the GenerateSignatureBase method or by any other means - /// The hash algorithm used to perform the hashing. If the hashing algorithm requires initialization or a key it should be set prior to calling this method - /// A base64 string of the hash value - protected string GenerateSignatureUsingHash(string signatureBase, HashAlgorithm hash) { - return ComputeHash(hash, signatureBase); - } - - /// - /// Generates a signature using the HMAC-SHA1 algorithm - /// - /// The full url that needs to be signed including its non OAuth url parameters - /// The consumer key - /// The consumer seceret - /// The token, if available. If not available pass null or an empty string - /// The token secret, if available. If not available pass null or an empty string - /// The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc) - /// A base64 string of the hash value - protected string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, string callback, out string normalizedUrl, out string normalizedRequestParameters) { - return GenerateSignature(url, consumerKey, consumerSecret, token, tokenSecret, httpMethod, timeStamp, nonce, callback, SignatureTypes.HMACSHA1, out normalizedUrl, out normalizedRequestParameters); - } - - /// - /// Generates a signature using the specified signatureType - /// - /// The full url that needs to be signed including its non OAuth url parameters - /// The consumer key - /// The consumer seceret - /// The token, if available. If not available pass null or an empty string - /// The token secret, if available. If not available pass null or an empty string - /// The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc) - /// The type of signature to use - /// A base64 string of the hash value - protected string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, string callback, SignatureTypes signatureType, out string normalizedUrl, out string normalizedRequestParameters) { - normalizedUrl = null; - normalizedRequestParameters = null; - - switch (signatureType) { - case SignatureTypes.PLAINTEXT: - return NetworkHelper.UrlEncode(string.Format("{0}&{1}", consumerSecret, tokenSecret)); - case SignatureTypes.HMACSHA1: - string signatureBase = GenerateSignatureBase(url, consumerKey, token, tokenSecret, httpMethod, timeStamp, nonce, callback, HMACSHA1SignatureType, out normalizedUrl, out normalizedRequestParameters); - HMACSHA1 hmacsha1 = new HMACSHA1(); - hmacsha1.Key = Encoding.ASCII.GetBytes(string.Format("{0}&{1}", UrlEncode3986(consumerSecret), string.IsNullOrEmpty(tokenSecret) ? "" : UrlEncode3986(tokenSecret))); - - return GenerateSignatureUsingHash(signatureBase, hmacsha1); - case SignatureTypes.RSASHA1: - throw new NotImplementedException(); - default: - throw new ArgumentException("Unknown signature type", "signatureType"); - } + return result.ToString(); } /// /// Generate the timestamp for the signature /// /// - - protected virtual string GenerateTimeStamp() { + public static string GenerateTimeStamp() { // Default implementation of UNIX time of the current UTC time TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalSeconds).ToString(); @@ -405,23 +225,22 @@ namespace GreenshotPlugin.Core { /// Generate a nonce /// /// - protected virtual string GenerateNonce() { + public static string GenerateNonce() { // Just a simple implementation of a random number between 123400 and 9999999 return random.Next(123400, 9999999).ToString(); } - /// /// Get the request token using the consumer key and secret. Also initializes tokensecret /// /// The request token. - public String getRequestToken() { + private String getRequestToken() { string ret = null; - string response = oAuthWebRequest(Method.POST, RequestTokenUrl, String.Empty); + string response = oAuthWebRequestNoCheck(HTTPMethod.POST, RequestTokenUrl, null); if (response.Length > 0) { - NameValueCollection qs = NetworkHelper.ParseQueryString(response); - if (qs[OAuthTokenKey] != null) { - this.Token = qs[OAuthTokenKey]; - this.TokenSecret = qs[OAuthTokenSecretKey]; + IDictionary qs = NetworkHelper.ParseQueryString(response); + if (qs.ContainsKey(OAUTH_TOKEN_KEY)) { + this.Token = qs[OAUTH_TOKEN_KEY]; + this.TokenSecret = qs[OAUTH_TOKEN_SECRET_KEY]; ret = this.Token; } } @@ -432,13 +251,13 @@ namespace GreenshotPlugin.Core { /// Authorize the token by showing the dialog /// /// The request token. - public String authorizeToken(string browserTitle) { + private String authorizeToken() { if (string.IsNullOrEmpty(Token)) { Exception e = new Exception("The request token is not set"); throw e; } LOG.DebugFormat("Opening AuthorizationLink: {0}", AuthorizationLink); - OAuthLoginForm oAuthLoginForm = new OAuthLoginForm(this, browserTitle, BrowserWidth, BrowserHeight); + OAuthLoginForm oAuthLoginForm = new OAuthLoginForm(this, LoginTitle, BrowserWidth, BrowserHeight); oAuthLoginForm.ShowDialog(); Token = oAuthLoginForm.Token; if (CheckVerifier) { @@ -457,163 +276,200 @@ namespace GreenshotPlugin.Core { /// Get the access token /// /// The access token. - public String getAccessToken() { + private String getAccessToken() { if (string.IsNullOrEmpty(Token) || (CheckVerifier && string.IsNullOrEmpty(Verifier))) { Exception e = new Exception("The request token and verifier were not set"); throw e; } - string response = oAuthWebRequest(Method.POST, AccessTokenUrl, string.Empty); + string response = oAuthWebRequestNoCheck(HTTPMethod.POST, AccessTokenUrl, null); if (response.Length > 0) { - NameValueCollection qs = NetworkHelper.ParseQueryString(response); - if (qs[OAuthTokenKey] != null) { - this.Token = qs[OAuthTokenKey]; + IDictionary qs = NetworkHelper.ParseQueryString(response); + if (qs.ContainsKey(OAUTH_TOKEN_KEY) && qs[OAUTH_TOKEN_KEY] != null) { + this.Token = qs[OAUTH_TOKEN_KEY]; } - if (qs[OAuthTokenSecretKey] != null) { - this.TokenSecret = qs[OAuthTokenSecretKey]; + if (qs.ContainsKey(OAUTH_TOKEN_SECRET_KEY) && qs[OAUTH_TOKEN_SECRET_KEY] != null) { + this.TokenSecret = qs[OAUTH_TOKEN_SECRET_KEY]; } } return Token; } + /// + /// This method goes through the whole authorize process, including a Authorization window. + /// + /// true if the process is completed + private bool authorize() { + LOG.Debug("Creating Token"); + try { + getRequestToken(); + } catch (Exception ex) { + LOG.Error(ex); + throw new NotSupportedException("Service is not available: " + ex.Message); + } + if (UseAuthorization && string.IsNullOrEmpty(authorizeToken())) { + LOG.Debug("User didn't authenticate!"); + return false; + } + return getAccessToken() != null; + } + /// /// Get the link to the authorization page for this application. /// /// The url with a valid request token, or a null string. public string AuthorizationLink { get { - return AuthorizeUrl + "?" + OAuthTokenKey + "=" + this.Token + "&" + OAuthCallbackKey + "=" + UrlEncode3986(CallbackUrl); + return AuthorizeUrl + "?" + OAUTH_TOKEN_KEY + "=" + this.Token + "&" + OAUTH_CALLBACK_KEY + "=" + UrlEncode3986(CallbackUrl); } } + /// + /// Wrapper + /// + /// + /// + /// + /// + public string oAuthWebRequest(HTTPMethod method, string requestURL, IDictionary parameters) { + return oAuthWebRequest(method, requestURL, parameters, null, null); + } /// /// Submit a web request using oAuth. /// /// GET or POST - /// The full url, including the querystring. + /// The full url, including the querystring. + /// Parameters for the request + /// contenttype for the postdata /// Data to post (MemoryStream) /// The web server response. - public string oAuthWebRequest(Method method, string url, MemoryStream postData) { - string outUrl = ""; - string querystring = ""; - - Uri uri = new Uri(url); - - string nonce = this.GenerateNonce(); - string timeStamp = this.GenerateTimeStamp(); - - string callback = ""; - if (url.ToString().Contains(RequestTokenUrl)) { - callback = CallbackUrl; + public string oAuthWebRequest(HTTPMethod method, string requestURL, IDictionary parameters, string contentType, MemoryStream postData) { + // If we are not trying to get a Authorization or Accestoken, and we don't have a token, create one + if (string.IsNullOrEmpty(Token)) { + if (!authorize()) { + throw new Exception("Not authorized"); + } } - - //Generate Signature - string sig = this.GenerateSignature(uri, - this.ConsumerKey, - this.ConsumerSecret, - this.Token, - this.TokenSecret, - method.ToString(), - timeStamp, - nonce, - callback, - out outUrl, - out querystring); - - if (querystring.Length > 0) { - querystring += "&"; - } - querystring += OAuthSignatureKey + "=" + NetworkHelper.UrlEncode(sig); - - if (querystring.Length > 0) { - outUrl += "?"; - } - - return WebRequest(method, outUrl + querystring, postData); + return oAuthWebRequestNoCheck(method, requestURL, parameters, contentType, postData); } - /// - /// Submit a web request using oAuth. - /// - /// GET or POST - /// The full url, including the querystring. - /// Data to post (querystring format) - /// The web server response. - public string oAuthWebRequest(Method method, string url, string postData) { - string outUrl = ""; - string querystring = ""; - string ret = ""; + public string oAuthWebRequestNoCheck(HTTPMethod method, string requestURL, IDictionary parameters) { + return oAuthWebRequestNoCheck(method, requestURL, parameters, null, null); + } - //Setup postData for signing. - //Add the postData to the querystring. - if (method == Method.POST) { - if (postData.Length > 0) { - //Decode the parameters and re-encode using the oAuth UrlEncode method. - NameValueCollection qs = NetworkHelper.ParseQueryString(postData); - postData = ""; - foreach (string key in qs.AllKeys) { - if (postData.Length > 0) { - postData += "&"; - } - qs[key] = NetworkHelper.UrlDecode(qs[key]); - qs[key] = UrlEncode3986(qs[key]); - postData += key + "=" + qs[key]; + private string oAuthWebRequestNoCheck(HTTPMethod method, string requestURL, IDictionary parameters, string contentType, MemoryStream postData) { + // Build the signature base + StringBuilder signatureBase = new StringBuilder(); + // Add Method to signature base + signatureBase.Append(method.ToString()).Append("&"); + + // Add normalized URL + Uri url = new Uri(requestURL); + string normalizedUrl = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}://{1}", url.Scheme, url.Host); + if (!((url.Scheme == "http" && url.Port == 80) || (url.Scheme == "https" && url.Port == 443))) { + normalizedUrl += ":" + url.Port; + } + normalizedUrl += url.AbsolutePath; + signatureBase.Append(UrlEncode3986(normalizedUrl)).Append("&"); + + // Add normalized parameters + if (parameters == null) { + parameters = new Dictionary(); + } + + parameters.Add(OAUTH_VERSION_KEY, OAUTH_VERSION); + parameters.Add(OAUTH_NONCE_KEY, GenerateNonce()); + parameters.Add(OAUTH_TIMESTAMP_KEY, GenerateTimeStamp()); + parameters.Add(OAUTH_SIGNATURE_METHOD_KEY, HMACSHA1SignatureType); + parameters.Add(OAUTH_CONSUMER_KEY_KEY, ConsumerKey); + if (CallbackUrl != null && RequestTokenUrl != null && requestURL.ToString().StartsWith(RequestTokenUrl)) { + parameters.Add(OAUTH_CALLBACK_KEY, CallbackUrl); + } + + if (!string.IsNullOrEmpty(Token)) { + parameters.Add(OAUTH_TOKEN_KEY, Token); + } + signatureBase.Append(UrlEncode3986(GenerateNormalizedParametersString(parameters))); + LOG.DebugFormat("Signature base: {0}", signatureBase); + // Generate Signature and add it to the parameters + HMACSHA1 hmacsha1 = new HMACSHA1(); + hmacsha1.Key = Encoding.UTF8.GetBytes(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}&{1}", UrlEncode3986(ConsumerSecret), string.IsNullOrEmpty(TokenSecret) ? string.Empty : UrlEncode3986(TokenSecret))); + string signature = ComputeHash(hmacsha1, signatureBase.ToString()); + parameters.Add(OAUTH_SIGNATURE_KEY, signature); + LOG.DebugFormat("Signature: {0}", signature); + + IDictionary requestParameters = null; + // Add oAuth values as HTTP headers, if this is allowed + StringBuilder authHeader = null; + if (HTTPMethod.POST == method && UseHTTPHeadersForAuthorization) { + authHeader = new StringBuilder(); + requestParameters = new Dictionary(); + foreach (string parameterKey in parameters.Keys) { + if (parameterKey.StartsWith(OAUTH_PARAMETER_PREFIX)) { + authHeader.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0}=\"{1}\", ", parameterKey, UrlEncode3986(parameters[parameterKey])); + } else if (!requestParameters.ContainsKey(parameterKey)) { + LOG.DebugFormat("Request parameter: {0}={1}", parameterKey, parameters[parameterKey]); + requestParameters.Add(parameterKey, parameters[parameterKey]); } - if (url.IndexOf("?") > 0) { - url += "&"; - } else { - url += "?"; + } + // Remove trailing comma and space and add it to the headers + if (authHeader.Length > 0) { + authHeader.Remove(authHeader.Length - 2, 2); + } + } else if (HTTPMethod.GET == method) { + if (parameters.Count > 0) { + // Add the parameters to the request + requestURL += "?" + NetworkHelper.GenerateQueryParameters(parameters); + } + } else { + requestParameters = parameters; + } + + // Create webrequest + HttpWebRequest webRequest = (HttpWebRequest)NetworkHelper.CreateWebRequest(requestURL); + webRequest.Method = method.ToString(); + webRequest.ServicePoint.Expect100Continue = false; + webRequest.UserAgent = _userAgent; + webRequest.Timeout = 20000; + + if (UseHTTPHeadersForAuthorization && authHeader != null) { + LOG.DebugFormat("Authorization: OAuth {0}", authHeader.ToString()); + webRequest.Headers.Add("Authorization: OAuth " + authHeader.ToString()); + } + + if (HTTPMethod.POST == method && postData == null && requestParameters != null && requestParameters.Count > 0) { + StringBuilder form = new StringBuilder(); + foreach (string parameterKey in requestParameters.Keys) { + form.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0}={1}&", UrlEncode3986(parameterKey), UrlEncode3986(parameters[parameterKey])); + } + // Remove trailing & + if (form.Length > 0) { + form.Remove(form.Length - 1, 1); + } + LOG.DebugFormat("Form data: {0}", form.ToString()); + webRequest.ContentType = "application/x-www-form-urlencoded"; + byte[] data = Encoding.UTF8.GetBytes(form.ToString()); + using (var requestStream = webRequest.GetRequestStream()) { + requestStream.Write(data, 0, data.Length); + } + } else { + webRequest.ContentType = contentType; + if (postData != null) { + using (var requestStream = webRequest.GetRequestStream()) { + requestStream.Write(postData.GetBuffer(), 0, (int)postData.Length); } - url += postData; } } - Uri uri = new Uri(url); + string responseData = WebResponseGet(webRequest); + LOG.DebugFormat("Response: {0}", responseData); - string nonce = this.GenerateNonce(); - string timeStamp = this.GenerateTimeStamp(); - - string callback = ""; - if (url.ToString().Contains(RequestTokenUrl)) { - callback = CallbackUrl; - } + webRequest = null; - //Generate Signature - string sig = this.GenerateSignature(uri, - this.ConsumerKey, - this.ConsumerSecret, - this.Token, - this.TokenSecret, - method.ToString(), - timeStamp, - nonce, - callback, - out outUrl, - out querystring); - - if (querystring.Length > 0) { - querystring += "&"; - } - querystring += OAuthSignatureKey + "=" + NetworkHelper.UrlEncode(sig); - - //Convert the querystring to postData - if (method == Method.POST) { - postData = querystring; - querystring = ""; - } - - if (querystring.Length > 0) { - outUrl += "?"; - } - - if (method == Method.POST || method == Method.GET) { - ret = WebRequest(method, outUrl + querystring, postData); - } - - return ret; + return responseData; } /// @@ -623,7 +479,7 @@ namespace GreenshotPlugin.Core { /// Full url to the web resource /// Data to post /// The web server response. - protected string WebRequest(Method method, string url, MemoryStream postData) { + protected string WebRequest(HTTPMethod method, string url, string contentType, MemoryStream postData) { HttpWebRequest webRequest = null; string responseData = ""; @@ -633,6 +489,10 @@ namespace GreenshotPlugin.Core { webRequest.UserAgent = _userAgent; webRequest.Timeout = 20000; webRequest.ContentLength = postData.Length; + if (method == HTTPMethod.POST) { + webRequest.ContentType = contentType; + } + using (var requestStream = webRequest.GetRequestStream()) { requestStream.Write(postData.GetBuffer(), 0, (int)postData.Length); } @@ -644,46 +504,6 @@ namespace GreenshotPlugin.Core { return responseData; } - /// - /// Web Request Wrapper - /// - /// Http Method - /// Full url to the web resource - /// Data to post in querystring format - /// The web server response. - protected string WebRequest(Method method, string url, string postData) { - HttpWebRequest webRequest = null; - StreamWriter requestWriter = null; - string responseData = ""; - - webRequest = (HttpWebRequest)NetworkHelper.CreateWebRequest(url); - webRequest.Method = method.ToString(); - webRequest.ServicePoint.Expect100Continue = false; - webRequest.UserAgent = _userAgent; - webRequest.Timeout = 20000; - - if (method == Method.POST) { - webRequest.ContentType = "application/x-www-form-urlencoded"; - - requestWriter = new StreamWriter(webRequest.GetRequestStream()); - try { - requestWriter.Write(postData); - } catch { - throw; - } finally { - requestWriter.Close(); - requestWriter = null; - } - } - - responseData = WebResponseGet(webRequest); - - webRequest = null; - - return responseData; - - } - /// /// Process the web response. /// @@ -707,4 +527,5 @@ namespace GreenshotPlugin.Core { return responseData; } } + }