New: Make Twitter NetStandard compatible
This commit is contained in:
parent
0878f514aa
commit
1d02208316
|
@ -0,0 +1,13 @@
|
||||||
|
OAuth (http://github.com/danielcrenna/oauth)
|
||||||
|
Written by Daniel Crenna
|
||||||
|
(http://danielcrenna.com)
|
||||||
|
|
||||||
|
This work is public domain.
|
||||||
|
"The person who associated a work with this document has
|
||||||
|
dedicated the work to the Commons by waiving all of his
|
||||||
|
or her rights to the work worldwide under copyright law
|
||||||
|
and all related or neighboring legal rights he or she
|
||||||
|
had in the work, to the extent allowable by law."
|
||||||
|
|
||||||
|
For more information, please visit:
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
|
@ -0,0 +1,508 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.OAuth
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A request wrapper for the OAuth 1.0a specification.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="http://oauth.net/"/>
|
||||||
|
public class OAuthRequest
|
||||||
|
{
|
||||||
|
public virtual OAuthSignatureMethod SignatureMethod { get; set; }
|
||||||
|
public virtual OAuthSignatureTreatment SignatureTreatment { get; set; }
|
||||||
|
public virtual OAuthRequestType Type { get; set; }
|
||||||
|
|
||||||
|
public virtual string Method { get; set; }
|
||||||
|
public virtual string Realm { get; set; }
|
||||||
|
public virtual string ConsumerKey { get; set; }
|
||||||
|
public virtual string ConsumerSecret { get; set; }
|
||||||
|
public virtual string Token { get; set; }
|
||||||
|
public virtual string TokenSecret { get; set; }
|
||||||
|
public virtual string Verifier { get; set; }
|
||||||
|
public virtual string ClientUsername { get; set; }
|
||||||
|
public virtual string ClientPassword { get; set; }
|
||||||
|
public virtual string CallbackUrl { get; set; }
|
||||||
|
public virtual string Version { get; set; }
|
||||||
|
public virtual string SessionHandle { get; set; }
|
||||||
|
|
||||||
|
/// <seealso cref="http://oauth.net/core/1.0#request_urls"/>
|
||||||
|
public virtual string RequestUrl { get; set; }
|
||||||
|
|
||||||
|
#region Authorization Header
|
||||||
|
|
||||||
|
#if !WINRT
|
||||||
|
public string GetAuthorizationHeader(NameValueCollection parameters)
|
||||||
|
{
|
||||||
|
var collection = new WebParameterCollection(parameters);
|
||||||
|
|
||||||
|
return GetAuthorizationHeader(collection);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public string GetAuthorizationHeader(IDictionary<string, string> parameters)
|
||||||
|
{
|
||||||
|
var collection = new WebParameterCollection(parameters);
|
||||||
|
|
||||||
|
return GetAuthorizationHeader(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetAuthorizationHeader()
|
||||||
|
{
|
||||||
|
var collection = new WebParameterCollection(0);
|
||||||
|
|
||||||
|
return GetAuthorizationHeader(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetAuthorizationHeader(WebParameterCollection parameters)
|
||||||
|
{
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case OAuthRequestType.RequestToken:
|
||||||
|
ValidateRequestState();
|
||||||
|
return GetSignatureAuthorizationHeader(parameters);
|
||||||
|
case OAuthRequestType.AccessToken:
|
||||||
|
ValidateAccessRequestState();
|
||||||
|
return GetSignatureAuthorizationHeader(parameters);
|
||||||
|
case OAuthRequestType.ProtectedResource:
|
||||||
|
ValidateProtectedResourceState();
|
||||||
|
return GetSignatureAuthorizationHeader(parameters);
|
||||||
|
case OAuthRequestType.ClientAuthentication:
|
||||||
|
ValidateClientAuthAccessRequestState();
|
||||||
|
return GetClientSignatureAuthorizationHeader(parameters);
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetSignatureAuthorizationHeader(WebParameterCollection parameters)
|
||||||
|
{
|
||||||
|
var signature = GetNewSignature(parameters);
|
||||||
|
|
||||||
|
parameters.Add("oauth_signature", signature);
|
||||||
|
|
||||||
|
return WriteAuthorizationHeader(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetClientSignatureAuthorizationHeader(WebParameterCollection parameters)
|
||||||
|
{
|
||||||
|
var signature = GetNewSignatureXAuth(parameters);
|
||||||
|
|
||||||
|
parameters.Add("oauth_signature", signature);
|
||||||
|
|
||||||
|
return WriteAuthorizationHeader(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string WriteAuthorizationHeader(WebParameterCollection parameters)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder("OAuth ");
|
||||||
|
|
||||||
|
if (!IsNullOrBlank(Realm))
|
||||||
|
{
|
||||||
|
sb.AppendFormat("realm=\"{0}\",", OAuthTools.UrlEncodeRelaxed(Realm));
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters.Sort((l, r) => l.Name.CompareTo(r.Name));
|
||||||
|
|
||||||
|
foreach (var parameter in parameters.Where(parameter =>
|
||||||
|
!IsNullOrBlank(parameter.Name) &&
|
||||||
|
!IsNullOrBlank(parameter.Value) &&
|
||||||
|
(parameter.Name.StartsWith("oauth_") || parameter.Name.StartsWith("x_auth_"))))
|
||||||
|
{
|
||||||
|
sb.AppendFormat("{0}=\"{1}\",", parameter.Name, parameter.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Remove(sb.Length - 1, 1);
|
||||||
|
|
||||||
|
var authorization = sb.ToString();
|
||||||
|
return authorization;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Authorization Query
|
||||||
|
|
||||||
|
#if !WINRT
|
||||||
|
public string GetAuthorizationQuery(NameValueCollection parameters)
|
||||||
|
{
|
||||||
|
var collection = new WebParameterCollection(parameters);
|
||||||
|
|
||||||
|
return GetAuthorizationQuery(collection);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public string GetAuthorizationQuery(IDictionary<string, string> parameters)
|
||||||
|
{
|
||||||
|
var collection = new WebParameterCollection(parameters);
|
||||||
|
|
||||||
|
return GetAuthorizationQuery(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetAuthorizationQuery()
|
||||||
|
{
|
||||||
|
var collection = new WebParameterCollection(0);
|
||||||
|
|
||||||
|
return GetAuthorizationQuery(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetAuthorizationQuery(WebParameterCollection parameters)
|
||||||
|
{
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case OAuthRequestType.RequestToken:
|
||||||
|
ValidateRequestState();
|
||||||
|
return GetSignatureAuthorizationQuery(parameters);
|
||||||
|
case OAuthRequestType.AccessToken:
|
||||||
|
ValidateAccessRequestState();
|
||||||
|
return GetSignatureAuthorizationQuery(parameters);
|
||||||
|
case OAuthRequestType.ProtectedResource:
|
||||||
|
ValidateProtectedResourceState();
|
||||||
|
return GetSignatureAuthorizationQuery(parameters);
|
||||||
|
case OAuthRequestType.ClientAuthentication:
|
||||||
|
ValidateClientAuthAccessRequestState();
|
||||||
|
return GetClientSignatureAuthorizationQuery(parameters);
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetSignatureAuthorizationQuery(WebParameterCollection parameters)
|
||||||
|
{
|
||||||
|
var signature = GetNewSignature(parameters);
|
||||||
|
|
||||||
|
parameters.Add("oauth_signature", signature);
|
||||||
|
|
||||||
|
return WriteAuthorizationQuery(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetClientSignatureAuthorizationQuery(WebParameterCollection parameters)
|
||||||
|
{
|
||||||
|
var signature = GetNewSignatureXAuth(parameters);
|
||||||
|
|
||||||
|
parameters.Add("oauth_signature", signature);
|
||||||
|
|
||||||
|
return WriteAuthorizationQuery(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string WriteAuthorizationQuery(WebParameterCollection parameters)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
parameters.Sort((l, r) => l.Name.CompareTo(r.Name));
|
||||||
|
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
foreach (var parameter in parameters.Where(parameter =>
|
||||||
|
!IsNullOrBlank(parameter.Name) &&
|
||||||
|
!IsNullOrBlank(parameter.Value) &&
|
||||||
|
(parameter.Name.StartsWith("oauth_") || parameter.Name.StartsWith("x_auth_"))))
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
var format = count < parameters.Count ? "{0}={1}&" : "{0}={1}";
|
||||||
|
sb.AppendFormat(format, parameter.Name, parameter.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var authorization = sb.ToString();
|
||||||
|
return authorization;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private string GetNewSignature(WebParameterCollection parameters)
|
||||||
|
{
|
||||||
|
var timestamp = OAuthTools.GetTimestamp();
|
||||||
|
|
||||||
|
var nonce = OAuthTools.GetNonce();
|
||||||
|
|
||||||
|
AddAuthParameters(parameters, timestamp, nonce);
|
||||||
|
|
||||||
|
var signatureBase = OAuthTools.ConcatenateRequestElements(Method.ToUpperInvariant(), RequestUrl, parameters);
|
||||||
|
|
||||||
|
var signature = OAuthTools.GetSignature(SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret, TokenSecret);
|
||||||
|
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetNewSignatureXAuth(WebParameterCollection parameters)
|
||||||
|
{
|
||||||
|
var timestamp = OAuthTools.GetTimestamp();
|
||||||
|
|
||||||
|
var nonce = OAuthTools.GetNonce();
|
||||||
|
|
||||||
|
AddXAuthParameters(parameters, timestamp, nonce);
|
||||||
|
|
||||||
|
var signatureBase = OAuthTools.ConcatenateRequestElements(Method.ToUpperInvariant(), RequestUrl, parameters);
|
||||||
|
|
||||||
|
var signature = OAuthTools.GetSignature(SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret, TokenSecret);
|
||||||
|
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Static Helpers
|
||||||
|
|
||||||
|
public static OAuthRequest ForRequestToken(string consumerKey, string consumerSecret)
|
||||||
|
{
|
||||||
|
var credentials = new OAuthRequest
|
||||||
|
{
|
||||||
|
Method = "GET",
|
||||||
|
Type = OAuthRequestType.RequestToken,
|
||||||
|
SignatureMethod = OAuthSignatureMethod.HmacSha1,
|
||||||
|
SignatureTreatment = OAuthSignatureTreatment.Escaped,
|
||||||
|
ConsumerKey = consumerKey,
|
||||||
|
ConsumerSecret = consumerSecret
|
||||||
|
};
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OAuthRequest ForRequestToken(string consumerKey, string consumerSecret, string callbackUrl)
|
||||||
|
{
|
||||||
|
var credentials = ForRequestToken(consumerKey, consumerSecret);
|
||||||
|
credentials.CallbackUrl = callbackUrl;
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OAuthRequest ForAccessToken(string consumerKey, string consumerSecret, string requestToken, string requestTokenSecret)
|
||||||
|
{
|
||||||
|
var credentials = new OAuthRequest
|
||||||
|
{
|
||||||
|
Method = "GET",
|
||||||
|
Type = OAuthRequestType.AccessToken,
|
||||||
|
SignatureMethod = OAuthSignatureMethod.HmacSha1,
|
||||||
|
SignatureTreatment = OAuthSignatureTreatment.Escaped,
|
||||||
|
ConsumerKey = consumerKey,
|
||||||
|
ConsumerSecret = consumerSecret,
|
||||||
|
Token = requestToken,
|
||||||
|
TokenSecret = requestTokenSecret
|
||||||
|
};
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OAuthRequest ForAccessToken(string consumerKey, string consumerSecret, string requestToken, string requestTokenSecret, string verifier)
|
||||||
|
{
|
||||||
|
var credentials = ForAccessToken(consumerKey, consumerSecret, requestToken, requestTokenSecret);
|
||||||
|
credentials.Verifier = verifier;
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OAuthRequest ForAccessTokenRefresh(string consumerKey, string consumerSecret, string accessToken, string accessTokenSecret, string sessionHandle)
|
||||||
|
{
|
||||||
|
var credentials = ForAccessToken(consumerKey, consumerSecret, accessToken, accessTokenSecret);
|
||||||
|
credentials.SessionHandle = sessionHandle;
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OAuthRequest ForAccessTokenRefresh(string consumerKey, string consumerSecret, string accessToken, string accessTokenSecret, string sessionHandle, string verifier)
|
||||||
|
{
|
||||||
|
var credentials = ForAccessToken(consumerKey, consumerSecret, accessToken, accessTokenSecret);
|
||||||
|
credentials.SessionHandle = sessionHandle;
|
||||||
|
credentials.Verifier = verifier;
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OAuthRequest ForClientAuthentication(string consumerKey, string consumerSecret, string username, string password)
|
||||||
|
{
|
||||||
|
var credentials = new OAuthRequest
|
||||||
|
{
|
||||||
|
Method = "GET",
|
||||||
|
Type = OAuthRequestType.ClientAuthentication,
|
||||||
|
SignatureMethod = OAuthSignatureMethod.HmacSha1,
|
||||||
|
SignatureTreatment = OAuthSignatureTreatment.Escaped,
|
||||||
|
ConsumerKey = consumerKey,
|
||||||
|
ConsumerSecret = consumerSecret,
|
||||||
|
ClientUsername = username,
|
||||||
|
ClientPassword = password
|
||||||
|
};
|
||||||
|
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OAuthRequest ForProtectedResource(string method, string consumerKey, string consumerSecret, string accessToken, string accessTokenSecret)
|
||||||
|
{
|
||||||
|
var credentials = new OAuthRequest
|
||||||
|
{
|
||||||
|
Method = method ?? "GET",
|
||||||
|
Type = OAuthRequestType.ProtectedResource,
|
||||||
|
SignatureMethod = OAuthSignatureMethod.HmacSha1,
|
||||||
|
SignatureTreatment = OAuthSignatureTreatment.Escaped,
|
||||||
|
ConsumerKey = consumerKey,
|
||||||
|
ConsumerSecret = consumerSecret,
|
||||||
|
Token = accessToken,
|
||||||
|
TokenSecret = accessTokenSecret
|
||||||
|
};
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private void ValidateRequestState()
|
||||||
|
{
|
||||||
|
if (IsNullOrBlank(Method))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify an HTTP method");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNullOrBlank(RequestUrl))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify a request token URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNullOrBlank(ConsumerKey))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify a consumer key");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNullOrBlank(ConsumerSecret))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify a consumer secret");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidateAccessRequestState()
|
||||||
|
{
|
||||||
|
if (IsNullOrBlank(Method))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify an HTTP method");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNullOrBlank(RequestUrl))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify an access token URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNullOrBlank(ConsumerKey))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify a consumer key");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNullOrBlank(ConsumerSecret))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify a consumer secret");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNullOrBlank(Token))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify a token");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidateClientAuthAccessRequestState()
|
||||||
|
{
|
||||||
|
if (IsNullOrBlank(Method))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify an HTTP method");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNullOrBlank(RequestUrl))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify an access token URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNullOrBlank(ConsumerKey))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify a consumer key");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNullOrBlank(ConsumerSecret))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify a consumer secret");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNullOrBlank(ClientUsername) || IsNullOrBlank(ClientPassword))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify user credentials");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidateProtectedResourceState()
|
||||||
|
{
|
||||||
|
if (IsNullOrBlank(Method))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify an HTTP method");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNullOrBlank(ConsumerKey))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify a consumer key");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNullOrBlank(ConsumerSecret))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("You must specify a consumer secret");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddAuthParameters(ICollection<WebParameter> parameters, string timestamp, string nonce)
|
||||||
|
{
|
||||||
|
var authParameters = new WebParameterCollection
|
||||||
|
{
|
||||||
|
new WebParameter("oauth_consumer_key", ConsumerKey),
|
||||||
|
new WebParameter("oauth_nonce", nonce),
|
||||||
|
new WebParameter("oauth_signature_method", ToRequestValue(SignatureMethod)),
|
||||||
|
new WebParameter("oauth_timestamp", timestamp),
|
||||||
|
new WebParameter("oauth_version", Version ?? "1.0")
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!IsNullOrBlank(Token))
|
||||||
|
{
|
||||||
|
authParameters.Add(new WebParameter("oauth_token", Token));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsNullOrBlank(CallbackUrl))
|
||||||
|
{
|
||||||
|
authParameters.Add(new WebParameter("oauth_callback", CallbackUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsNullOrBlank(Verifier))
|
||||||
|
{
|
||||||
|
authParameters.Add(new WebParameter("oauth_verifier", Verifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsNullOrBlank(SessionHandle))
|
||||||
|
{
|
||||||
|
authParameters.Add(new WebParameter("oauth_session_handle", SessionHandle));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var authParameter in authParameters)
|
||||||
|
{
|
||||||
|
parameters.Add(authParameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddXAuthParameters(ICollection<WebParameter> parameters, string timestamp, string nonce)
|
||||||
|
{
|
||||||
|
var authParameters = new WebParameterCollection
|
||||||
|
{
|
||||||
|
new WebParameter("x_auth_username", ClientUsername),
|
||||||
|
new WebParameter("x_auth_password", ClientPassword),
|
||||||
|
new WebParameter("x_auth_mode", "client_auth"),
|
||||||
|
new WebParameter("oauth_consumer_key", ConsumerKey),
|
||||||
|
new WebParameter("oauth_signature_method", ToRequestValue(SignatureMethod)),
|
||||||
|
new WebParameter("oauth_timestamp", timestamp),
|
||||||
|
new WebParameter("oauth_nonce", nonce),
|
||||||
|
new WebParameter("oauth_version", Version ?? "1.0")
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var authParameter in authParameters)
|
||||||
|
{
|
||||||
|
parameters.Add(authParameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToRequestValue(OAuthSignatureMethod signatureMethod)
|
||||||
|
{
|
||||||
|
var value = signatureMethod.ToString().ToUpper();
|
||||||
|
var shaIndex = value.IndexOf("SHA1");
|
||||||
|
return shaIndex > -1 ? value.Insert(shaIndex, "-") : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNullOrBlank(string value)
|
||||||
|
{
|
||||||
|
return String.IsNullOrEmpty(value) || (!String.IsNullOrEmpty(value) && value.Trim() == String.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
namespace NzbDrone.Common.OAuth
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The types of OAuth requests possible in a typical workflow.
|
||||||
|
/// Used for validation purposes and to build static helpers.
|
||||||
|
/// </summary>
|
||||||
|
public enum OAuthRequestType
|
||||||
|
{
|
||||||
|
RequestToken,
|
||||||
|
AccessToken,
|
||||||
|
ProtectedResource,
|
||||||
|
ClientAuthentication
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
namespace NzbDrone.Common.OAuth
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The encryption method to use when hashing a request signature.
|
||||||
|
/// </summary>
|
||||||
|
public enum OAuthSignatureMethod
|
||||||
|
{
|
||||||
|
HmacSha1,
|
||||||
|
PlainText,
|
||||||
|
RsaSha1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
namespace NzbDrone.Common.OAuth
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies whether the final signature value should be escaped during calculation.
|
||||||
|
/// This might be necessary for some OAuth implementations that do not obey the default
|
||||||
|
/// specification for signature escaping.
|
||||||
|
/// </summary>
|
||||||
|
public enum OAuthSignatureTreatment
|
||||||
|
{
|
||||||
|
Escaped,
|
||||||
|
Unescaped
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,409 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
#if !WINRT
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
#else
|
||||||
|
using Windows.Security.Cryptography;
|
||||||
|
using Windows.Security.Cryptography.Core;
|
||||||
|
using Windows.Storage.Streams;
|
||||||
|
using System.Globalization;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.OAuth
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A general purpose toolset for creating components of an OAuth request.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="http://oauth.net/"/>
|
||||||
|
public static class OAuthTools
|
||||||
|
{
|
||||||
|
private const string AlphaNumeric = Upper + Lower + Digit;
|
||||||
|
private const string Digit = "1234567890";
|
||||||
|
private const string Lower = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
private const string Unreserved = AlphaNumeric + "-._~";
|
||||||
|
private const string Upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
|
||||||
|
private static readonly Random _random;
|
||||||
|
private static readonly object _randomLock = new object();
|
||||||
|
|
||||||
|
#if !SILVERLIGHT && !WINRT
|
||||||
|
private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static OAuthTools()
|
||||||
|
{
|
||||||
|
#if !SILVERLIGHT && !WINRT
|
||||||
|
var bytes = new byte[4];
|
||||||
|
_rng.GetNonZeroBytes(bytes);
|
||||||
|
_random = new Random(BitConverter.ToInt32(bytes, 0));
|
||||||
|
#else
|
||||||
|
_random = new Random();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All text parameters are UTF-8 encoded (per section 5.1).
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="http://www.hueniverse.com/hueniverse/2008/10/beginners-gui-1.html"/>
|
||||||
|
#if !WINRT
|
||||||
|
private static readonly Encoding _encoding = Encoding.UTF8;
|
||||||
|
#else
|
||||||
|
private static readonly BinaryStringEncoding _encoding = BinaryStringEncoding.Utf8;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a random 16-byte lowercase alphanumeric string.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="http://oauth.net/core/1.0#nonce"/>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetNonce()
|
||||||
|
{
|
||||||
|
const string chars = (Lower + Digit);
|
||||||
|
|
||||||
|
var nonce = new char[16];
|
||||||
|
lock (_randomLock)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < nonce.Length; i++)
|
||||||
|
{
|
||||||
|
nonce[i] = chars[_random.Next(0, chars.Length)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new string(nonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a timestamp based on the current elapsed seconds since '01/01/1970 0000 GMT"
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="http://oauth.net/core/1.0#nonce"/>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetTimestamp()
|
||||||
|
{
|
||||||
|
return GetTimestamp(DateTime.UtcNow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a timestamp based on the elapsed seconds of a given time since '01/01/1970 0000 GMT"
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="http://oauth.net/core/1.0#nonce"/>
|
||||||
|
/// <param name="dateTime">A specified point in time.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetTimestamp(DateTime dateTime)
|
||||||
|
{
|
||||||
|
var timestamp = ToUnixTime(dateTime);
|
||||||
|
return timestamp.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long ToUnixTime(DateTime dateTime)
|
||||||
|
{
|
||||||
|
var timeSpan = (dateTime - new DateTime(1970, 1, 1));
|
||||||
|
var timestamp = (long)timeSpan.TotalSeconds;
|
||||||
|
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// URL encodes a string based on section 5.1 of the OAuth spec.
|
||||||
|
/// Namely, percent encoding with [RFC3986], avoiding unreserved characters,
|
||||||
|
/// upper-casing hexadecimal characters, and UTF-8 encoding for text value pairs.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <seealso href="http://oauth.net/core/1.0#encoding_parameters" />
|
||||||
|
public static string UrlEncodeRelaxed(string value)
|
||||||
|
{
|
||||||
|
var escaped = Uri.EscapeDataString(value);
|
||||||
|
|
||||||
|
// LinkedIn users have problems because it requires escaping brackets
|
||||||
|
escaped = escaped.Replace("(", PercentEncode("("))
|
||||||
|
.Replace(")", PercentEncode(")"));
|
||||||
|
|
||||||
|
return escaped;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string PercentEncode(string s)
|
||||||
|
{
|
||||||
|
var bytes = Encoding.UTF8.GetBytes(s);
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (var b in bytes)
|
||||||
|
{
|
||||||
|
// Supports proper encoding of special characters (\n\r\t\b)
|
||||||
|
if ((b > 7 && b < 11) || b == 13)
|
||||||
|
{
|
||||||
|
sb.Append(string.Format("%0{0:X}", b));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append(string.Format("%{0:X}", b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// URL encodes a string based on section 5.1 of the OAuth spec.
|
||||||
|
/// Namely, percent encoding with [RFC3986], avoiding unreserved characters,
|
||||||
|
/// upper-casing hexadecimal characters, and UTF-8 encoding for text value pairs.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <seealso href="http://oauth.net/core/1.0#encoding_parameters" />
|
||||||
|
public static string UrlEncodeStrict(string value)
|
||||||
|
{
|
||||||
|
// [JD]: We need to escape the apostrophe as well or the signature will fail
|
||||||
|
var original = value;
|
||||||
|
var ret = original.OfType<char>().Where(
|
||||||
|
c => !Unreserved.OfType<char>().Contains(c) && c != '%').Aggregate(
|
||||||
|
value, (current, c) => current.Replace(
|
||||||
|
c.ToString(), PercentEncode(c.ToString())
|
||||||
|
));
|
||||||
|
|
||||||
|
return ret.Replace("%%", "%25%"); // Revisit to encode actual %'s
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sorts a collection of key-value pairs by name, and then value if equal,
|
||||||
|
/// concatenating them into a single string. This string should be encoded
|
||||||
|
/// prior to, or after normalization is run.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="http://oauth.net/core/1.0#rfc.section.9.1.1"/>
|
||||||
|
/// <param name="parameters"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string NormalizeRequestParameters(WebParameterCollection parameters)
|
||||||
|
{
|
||||||
|
var copy = SortParametersExcludingSignature(parameters);
|
||||||
|
var concatenated = Concatenate(copy, "=", "&");
|
||||||
|
return concatenated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Concatenate(ICollection<WebParameter> collection, string separator, string spacer)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
var total = collection.Count;
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
foreach (var item in collection)
|
||||||
|
{
|
||||||
|
sb.Append(item.Name);
|
||||||
|
sb.Append(separator);
|
||||||
|
sb.Append(item.Value);
|
||||||
|
|
||||||
|
count++;
|
||||||
|
if (count < total)
|
||||||
|
{
|
||||||
|
sb.Append(spacer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sorts a <see cref="WebParameterCollection"/> by name, and then value if equal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters">A collection of parameters to sort</param>
|
||||||
|
/// <returns>A sorted parameter collection</returns>
|
||||||
|
public static WebParameterCollection SortParametersExcludingSignature(WebParameterCollection parameters)
|
||||||
|
{
|
||||||
|
var copy = new WebParameterCollection(parameters);
|
||||||
|
var exclusions = copy.Where(n => EqualsIgnoreCase(n.Name, "oauth_signature"));
|
||||||
|
|
||||||
|
copy.RemoveAll(exclusions);
|
||||||
|
|
||||||
|
foreach(var parameter in copy)
|
||||||
|
{
|
||||||
|
parameter.Value = UrlEncodeStrict(parameter.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
copy.Sort((x, y) => x.Name.Equals(y.Name) ? x.Value.CompareTo(y.Value) : x.Name.CompareTo(y.Name));
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool EqualsIgnoreCase(string left, string right)
|
||||||
|
{
|
||||||
|
#if WINRT
|
||||||
|
return CultureInfo.InvariantCulture.CompareInfo.Compare(left, right, CompareOptions.IgnoreCase) == 0;
|
||||||
|
#else
|
||||||
|
return String.Compare(left, right, StringComparison.InvariantCultureIgnoreCase) == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a request URL suitable for making OAuth requests.
|
||||||
|
/// Resulting URLs must exclude port 80 or port 443 when accompanied by HTTP and HTTPS, respectively.
|
||||||
|
/// Resulting URLs must be lower case.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="http://oauth.net/core/1.0#rfc.section.9.1.2"/>
|
||||||
|
/// <param name="url">The original request URL</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ConstructRequestUrl(Uri url)
|
||||||
|
{
|
||||||
|
if (url == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("url");
|
||||||
|
}
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
var requestUrl = string.Format("{0}://{1}", url.Scheme, url.Host);
|
||||||
|
var qualified = string.Format(":{0}", url.Port);
|
||||||
|
var basic = url.Scheme == "http" && url.Port == 80;
|
||||||
|
var secure = url.Scheme == "https" && url.Port == 443;
|
||||||
|
|
||||||
|
sb.Append(requestUrl);
|
||||||
|
sb.Append(!basic && !secure ? qualified : "");
|
||||||
|
sb.Append(url.AbsolutePath);
|
||||||
|
|
||||||
|
return sb.ToString(); //.ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a request elements concatentation value to send with a request.
|
||||||
|
/// This is also known as the signature base.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="http://oauth.net/core/1.0#rfc.section.9.1.3"/>
|
||||||
|
/// <seealso href="http://oauth.net/core/1.0#sig_base_example"/>
|
||||||
|
/// <param name="method">The request's HTTP method type</param>
|
||||||
|
/// <param name="url">The request URL</param>
|
||||||
|
/// <param name="parameters">The request's parameters</param>
|
||||||
|
/// <returns>A signature base string</returns>
|
||||||
|
public static string ConcatenateRequestElements(string method, string url, WebParameterCollection parameters)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
// Separating &'s are not URL encoded
|
||||||
|
var requestMethod = string.Concat(method.ToUpper(), "&");
|
||||||
|
var requestUrl = string.Concat(UrlEncodeRelaxed(ConstructRequestUrl(new Uri(url))), "&");
|
||||||
|
var requestParameters = UrlEncodeRelaxed(NormalizeRequestParameters(parameters));
|
||||||
|
|
||||||
|
sb.Append(requestMethod);
|
||||||
|
sb.Append(requestUrl);
|
||||||
|
sb.Append(requestParameters);
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a signature value given a signature base and the consumer secret.
|
||||||
|
/// This method is used when the token secret is currently unknown.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="http://oauth.net/core/1.0#rfc.section.9.2"/>
|
||||||
|
/// <param name="signatureMethod">The hashing method</param>
|
||||||
|
/// <param name="signatureBase">The signature base</param>
|
||||||
|
/// <param name="consumerSecret">The consumer key</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetSignature(OAuthSignatureMethod signatureMethod,
|
||||||
|
string signatureBase,
|
||||||
|
string consumerSecret)
|
||||||
|
{
|
||||||
|
return GetSignature(signatureMethod, OAuthSignatureTreatment.Escaped, signatureBase, consumerSecret, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a signature value given a signature base and the consumer secret.
|
||||||
|
/// This method is used when the token secret is currently unknown.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="http://oauth.net/core/1.0#rfc.section.9.2"/>
|
||||||
|
/// <param name="signatureMethod">The hashing method</param>
|
||||||
|
/// <param name="signatureTreatment">The treatment to use on a signature value</param>
|
||||||
|
/// <param name="signatureBase">The signature base</param>
|
||||||
|
/// <param name="consumerSecret">The consumer key</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetSignature(OAuthSignatureMethod signatureMethod,
|
||||||
|
OAuthSignatureTreatment signatureTreatment,
|
||||||
|
string signatureBase,
|
||||||
|
string consumerSecret)
|
||||||
|
{
|
||||||
|
return GetSignature(signatureMethod, signatureTreatment, signatureBase, consumerSecret, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a signature value given a signature base and the consumer secret and a known token secret.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="http://oauth.net/core/1.0#rfc.section.9.2"/>
|
||||||
|
/// <param name="signatureMethod">The hashing method</param>
|
||||||
|
/// <param name="signatureBase">The signature base</param>
|
||||||
|
/// <param name="consumerSecret">The consumer secret</param>
|
||||||
|
/// <param name="tokenSecret">The token secret</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetSignature(OAuthSignatureMethod signatureMethod,
|
||||||
|
string signatureBase,
|
||||||
|
string consumerSecret,
|
||||||
|
string tokenSecret)
|
||||||
|
{
|
||||||
|
return GetSignature(signatureMethod, OAuthSignatureTreatment.Escaped, consumerSecret, tokenSecret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a signature value given a signature base and the consumer secret and a known token secret.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso href="http://oauth.net/core/1.0#rfc.section.9.2"/>
|
||||||
|
/// <param name="signatureMethod">The hashing method</param>
|
||||||
|
/// <param name="signatureTreatment">The treatment to use on a signature value</param>
|
||||||
|
/// <param name="signatureBase">The signature base</param>
|
||||||
|
/// <param name="consumerSecret">The consumer secret</param>
|
||||||
|
/// <param name="tokenSecret">The token secret</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetSignature(OAuthSignatureMethod signatureMethod,
|
||||||
|
OAuthSignatureTreatment signatureTreatment,
|
||||||
|
string signatureBase,
|
||||||
|
string consumerSecret,
|
||||||
|
string tokenSecret)
|
||||||
|
{
|
||||||
|
if (IsNullOrBlank(tokenSecret))
|
||||||
|
{
|
||||||
|
tokenSecret = String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
consumerSecret = UrlEncodeRelaxed(consumerSecret);
|
||||||
|
tokenSecret = UrlEncodeRelaxed(tokenSecret);
|
||||||
|
|
||||||
|
string signature;
|
||||||
|
switch (signatureMethod)
|
||||||
|
{
|
||||||
|
case OAuthSignatureMethod.HmacSha1:
|
||||||
|
{
|
||||||
|
var key = string.Concat(consumerSecret, "&", tokenSecret);
|
||||||
|
#if WINRT
|
||||||
|
IBuffer keyMaterial = CryptographicBuffer.ConvertStringToBinary(key, _encoding);
|
||||||
|
MacAlgorithmProvider hmacSha1Provider = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha1);
|
||||||
|
CryptographicKey macKey = hmacSha1Provider.CreateKey(keyMaterial);
|
||||||
|
IBuffer dataToBeSigned = CryptographicBuffer.ConvertStringToBinary(signatureBase, _encoding);
|
||||||
|
IBuffer signatureBuffer = CryptographicEngine.Sign(macKey, dataToBeSigned);
|
||||||
|
signature = CryptographicBuffer.EncodeToBase64String(signatureBuffer);
|
||||||
|
#else
|
||||||
|
var crypto = new HMACSHA1();
|
||||||
|
|
||||||
|
crypto.Key = _encoding.GetBytes(key);
|
||||||
|
signature = HashWith(signatureBase, crypto);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException("Only HMAC-SHA1 is currently supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = signatureTreatment == OAuthSignatureTreatment.Escaped
|
||||||
|
? UrlEncodeRelaxed(signature)
|
||||||
|
: signature;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !WINRT
|
||||||
|
private static string HashWith(string input, HashAlgorithm algorithm)
|
||||||
|
{
|
||||||
|
var data = Encoding.UTF8.GetBytes(input);
|
||||||
|
var hash = algorithm.ComputeHash(data);
|
||||||
|
return Convert.ToBase64String(hash);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private static bool IsNullOrBlank(string value)
|
||||||
|
{
|
||||||
|
return String.IsNullOrEmpty(value) || (!String.IsNullOrEmpty(value) && value.Trim() == String.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
namespace NzbDrone.Common.OAuth
|
||||||
|
{
|
||||||
|
public class WebParameter
|
||||||
|
{
|
||||||
|
public WebParameter(string name, string value)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Value { get; set; }
|
||||||
|
public string Name { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,202 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.OAuth
|
||||||
|
{
|
||||||
|
public class WebParameterCollection : IList<WebParameter>
|
||||||
|
{
|
||||||
|
private IList<WebParameter> _parameters;
|
||||||
|
|
||||||
|
public virtual WebParameter this[string name]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var parameters = this.Where(p => p.Name.Equals(name));
|
||||||
|
|
||||||
|
if(parameters.Count() == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parameters.Count() == 1)
|
||||||
|
{
|
||||||
|
return parameters.Single();
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = string.Join(",", parameters.Select(p => p.Value).ToArray());
|
||||||
|
return new WebParameter(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IEnumerable<string> Names
|
||||||
|
{
|
||||||
|
get { return _parameters.Select(p => p.Name); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IEnumerable<string> Values
|
||||||
|
{
|
||||||
|
get { return _parameters.Select(p => p.Value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebParameterCollection(IEnumerable<WebParameter> parameters)
|
||||||
|
{
|
||||||
|
_parameters = new List<WebParameter>(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !WINRT
|
||||||
|
public WebParameterCollection(NameValueCollection collection) : this()
|
||||||
|
{
|
||||||
|
AddCollection(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void AddRange(NameValueCollection collection)
|
||||||
|
{
|
||||||
|
AddCollection(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddCollection(NameValueCollection collection)
|
||||||
|
{
|
||||||
|
var parameters = collection.AllKeys.Select(key => new WebParameter(key, collection[key]));
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
_parameters.Add(parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public WebParameterCollection(IDictionary<string, string> collection) : this()
|
||||||
|
{
|
||||||
|
AddCollection(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddCollection(IDictionary<string, string> collection)
|
||||||
|
{
|
||||||
|
foreach (var parameter in collection.Keys.Select(key => new WebParameter(key, collection[key])))
|
||||||
|
{
|
||||||
|
_parameters.Add(parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebParameterCollection()
|
||||||
|
{
|
||||||
|
_parameters = new List<WebParameter>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebParameterCollection(int capacity)
|
||||||
|
{
|
||||||
|
_parameters = new List<WebParameter>(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddCollection(IEnumerable<WebParameter> collection)
|
||||||
|
{
|
||||||
|
foreach (var pair in collection.Select(parameter => new WebParameter(parameter.Name, parameter.Value)))
|
||||||
|
{
|
||||||
|
_parameters.Add(pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void AddRange(WebParameterCollection collection)
|
||||||
|
{
|
||||||
|
AddCollection(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void AddRange(IEnumerable<WebParameter> collection)
|
||||||
|
{
|
||||||
|
AddCollection(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Sort(Comparison<WebParameter> comparison)
|
||||||
|
{
|
||||||
|
var sorted = new List<WebParameter>(_parameters);
|
||||||
|
sorted.Sort(comparison);
|
||||||
|
_parameters = sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool RemoveAll(IEnumerable<WebParameter> parameters)
|
||||||
|
{
|
||||||
|
var array = parameters.ToArray();
|
||||||
|
var success = array.Aggregate(true, (current, parameter) => current & _parameters.Remove(parameter));
|
||||||
|
return success && array.Length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Add(string name, string value)
|
||||||
|
{
|
||||||
|
var pair = new WebParameter(name, value);
|
||||||
|
_parameters.Add(pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IList<WebParameter> Members
|
||||||
|
|
||||||
|
public virtual IEnumerator<WebParameter> GetEnumerator()
|
||||||
|
{
|
||||||
|
return _parameters.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Add(WebParameter parameter)
|
||||||
|
{
|
||||||
|
|
||||||
|
_parameters.Add(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Clear()
|
||||||
|
{
|
||||||
|
_parameters.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool Contains(WebParameter parameter)
|
||||||
|
{
|
||||||
|
return _parameters.Contains(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void CopyTo(WebParameter[] parameters, int arrayIndex)
|
||||||
|
{
|
||||||
|
_parameters.CopyTo(parameters, arrayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool Remove(WebParameter parameter)
|
||||||
|
{
|
||||||
|
return _parameters.Remove(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual int Count
|
||||||
|
{
|
||||||
|
get { return _parameters.Count; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool IsReadOnly
|
||||||
|
{
|
||||||
|
get { return _parameters.IsReadOnly; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual int IndexOf(WebParameter parameter)
|
||||||
|
{
|
||||||
|
return _parameters.IndexOf(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Insert(int index, WebParameter parameter)
|
||||||
|
{
|
||||||
|
_parameters.Insert(index, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
_parameters.RemoveAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual WebParameter this[int index]
|
||||||
|
{
|
||||||
|
get { return _parameters[index]; }
|
||||||
|
set { _parameters[index] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
using NLog;
|
using NLog;
|
||||||
using System;
|
using System;
|
||||||
using OAuth;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Http;
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Common.OAuth;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Twitter
|
namespace NzbDrone.Core.Notifications.Twitter
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
@ -60,59 +59,6 @@ namespace TinyTwitter
|
||||||
.Execute();
|
.Execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Tweet> GetHomeTimeline(long? sinceId = null, long? maxId = null, int? count = 20)
|
|
||||||
{
|
|
||||||
return GetTimeline("https://api.twitter.com/1.1/statuses/home_timeline.json", sinceId, maxId, count, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Tweet> GetMentions(long? sinceId = null, long? maxId = null, int? count = 20)
|
|
||||||
{
|
|
||||||
return GetTimeline("https://api.twitter.com/1.1/statuses/mentions.json", sinceId, maxId, count, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Tweet> GetUserTimeline(long? sinceId = null, long? maxId = null, int? count = 20, string screenName = "")
|
|
||||||
{
|
|
||||||
return GetTimeline("https://api.twitter.com/1.1/statuses/user_timeline.json", sinceId, maxId, count, screenName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<Tweet> GetTimeline(string url, long? sinceId, long? maxId, int? count, string screenName)
|
|
||||||
{
|
|
||||||
var builder = new RequestBuilder(oauth, "GET", url);
|
|
||||||
|
|
||||||
if (sinceId.HasValue)
|
|
||||||
builder.AddParameter("since_id", sinceId.Value.ToString());
|
|
||||||
|
|
||||||
if (maxId.HasValue)
|
|
||||||
builder.AddParameter("max_id", maxId.Value.ToString());
|
|
||||||
|
|
||||||
if (count.HasValue)
|
|
||||||
builder.AddParameter("count", count.Value.ToString());
|
|
||||||
|
|
||||||
if (screenName != "")
|
|
||||||
builder.AddParameter("screen_name", screenName);
|
|
||||||
|
|
||||||
var responseContent = builder.Execute();
|
|
||||||
|
|
||||||
var tweets = (object[])JsonConvert.DeserializeObject(responseContent);
|
|
||||||
|
|
||||||
return tweets.Cast<Dictionary<string, object>>().Select(tweet =>
|
|
||||||
{
|
|
||||||
var user = ((Dictionary<string, object>)tweet["user"]);
|
|
||||||
var date = DateTime.ParseExact(tweet["created_at"].ToString(),
|
|
||||||
"ddd MMM dd HH:mm:ss zz00 yyyy",
|
|
||||||
CultureInfo.InvariantCulture).ToLocalTime();
|
|
||||||
|
|
||||||
return new Tweet
|
|
||||||
{
|
|
||||||
Id = (long)tweet["id"],
|
|
||||||
CreatedAt = date,
|
|
||||||
Text = (string)tweet["text"],
|
|
||||||
UserName = (string)user["name"],
|
|
||||||
ScreenName = (string)user["screen_name"]
|
|
||||||
};
|
|
||||||
}).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
#region RequestBuilder
|
#region RequestBuilder
|
||||||
|
|
||||||
public class RequestBuilder
|
public class RequestBuilder
|
||||||
|
|
Loading…
Reference in New Issue