mirror of https://github.com/lidarr/Lidarr
New: Make Twitter NetStandard compatible
This commit is contained in:
parent
3ced1843bf
commit
b51b0ef68a
|
@ -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
|
||||
}
|
||||
}
|
|
@ -8,7 +8,6 @@
|
|||
<PackageReference Include="FluentValidation" Version="8.4.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="NLog" Version="4.6.7" />
|
||||
<PackageReference Include="OAuth" Version="1.0.3" />
|
||||
<PackageReference Include="RestSharp" Version="106.6.10" />
|
||||
<PackageReference Include="System.IO.Abstractions" Version="4.0.11" />
|
||||
<PackageReference Include="TagLibSharp-Lidarr" Version="2.2.0.19" />
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using System;
|
||||
using OAuth;
|
||||
using System.Net;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Web;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.OAuth;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Twitter
|
||||
{
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web.Script.Serialization;
|
||||
|
||||
namespace TinyTwitter
|
||||
{
|
||||
|
@ -60,61 +58,6 @@ namespace TinyTwitter
|
|||
.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 serializer = new JavaScriptSerializer();
|
||||
|
||||
var tweets = (object[])serializer.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
|
||||
|
||||
public class RequestBuilder
|
||||
|
|
Loading…
Reference in New Issue