using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Principal;
using System.Text;
using System.Xml;
using OAuth;
namespace Misuzilla.Applications.TwitterIrcGateway
{
public class TwitterIdentity : MarshalByRefObject, IIdentity
{
public String ScreenName { get; set; }
public Int32 UserId { get; set; }
public String Token { get; set; }
public String TokenSecret { get; set; }
#region IIdentity メンバ
public string AuthenticationType
{
get { return "OAuth"; }
}
public bool IsAuthenticated
{
get { return true; }
}
public string Name
{
get { return ScreenName; }
}
#endregion
}
///
/// TwitterへのOAuthアクセスを提供するクラスです。
///
public class TwitterOAuth : OAuthBase
{
public enum HttpMethod
{
GET, POST
}
private String _consumerKey;
private String _consumerSecret;
private static readonly Uri RequestTokenUrl = new Uri("https://api.twitter.com/oauth/request_token");
private static readonly Uri AuthorizeUrl = new Uri("https://api.twitter.com/oauth/authorize");
private static readonly Uri AccessTokenUrl = new Uri("https://api.twitter.com/oauth/access_token");
///
/// リクエストに利用するOAuthトークンを取得・設定します。
///
public String Token { get; set; }
///
/// リクエストに利用するOAuthシークレットトークンを取得・設定します。
///
public String TokenSecret { get; set; }
public TwitterOAuth(String consumerKey, String consumerSecret)
{
_consumerKey = consumerKey;
_consumerSecret = consumerSecret;
}
#region Step 1 (Request Unauthorized Token)
public String GetAuthorizeUrl()
{
return AuthorizeUrl + "?oauth_token=" + RequestUnauthorizedToken();
}
public String GetAuthorizeUrl(out String authToken)
{
authToken = RequestUnauthorizedToken();
return AuthorizeUrl + "?oauth_token=" + authToken;
}
public String RequestUnauthorizedToken()
{
String result = Request(RequestTokenUrl, HttpMethod.GET);
NameValueCollection returnValues = new NameValueCollection();
foreach (var keyValue in result.Split(new[] { '&', ';' }, StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Split(new[] { '=' }, 2))
.Where(p => p.Length == 2))
{
returnValues[keyValue[0]] = keyValue[1];
}
return returnValues["oauth_token"];
}
#endregion
#region Step 2 (Request Access Token & Setup TwitterOAuth Client)
public TwitterIdentity RequestAccessToken(String authToken, String verifier)
{
Verifier = verifier;
String result = ReadResponse(RequestInternal(AccessTokenUrl, HttpMethod.GET, authToken, String.Empty));
NameValueCollection returnValues = new NameValueCollection();
foreach (var keyValue in result.Split(new[] { '&', ';' }, StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Split(new[] { '=' }, 2))
.Where(p => p.Length == 2))
{
returnValues[keyValue[0]] = keyValue[1];
}
TwitterIdentity identity = new TwitterIdentity()
{
Token = returnValues["oauth_token"],
TokenSecret = returnValues["oauth_token_secret"],
ScreenName = returnValues["screen_name"],
UserId = Int32.Parse(returnValues["user_id"])
};
return identity;
}
public TwitterIdentity RequestAccessToken(String authToken, String verifier, Dictionary parameters)
{
UriBuilder newUri = new UriBuilder(AccessTokenUrl);
newUri.Query = ((newUri.Query.Length > 0) ? "&" : "") + String.Join("&", parameters.Select(kv => String.Concat(Utility.UrlEncode(kv.Key), "=", Utility.UrlEncode(kv.Value))).ToArray());
Verifier = verifier;
String result = ReadResponse(RequestInternal(newUri.Uri, HttpMethod.POST, authToken, String.Empty));
NameValueCollection returnValues = new NameValueCollection();
foreach (var keyValue in result.Split(new[] { '&', ';' }, StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Split(new[] { '=' }, 2))
.Where(p => p.Length == 2))
{
returnValues[keyValue[0]] = keyValue[1];
}
TwitterIdentity identity = new TwitterIdentity()
{
Token = returnValues["oauth_token"],
TokenSecret = returnValues["oauth_token_secret"],
ScreenName = returnValues["screen_name"],
UserId = Int32.Parse(returnValues["user_id"])
};
return identity;
}
#endregion
///
/// リソースへアクセスし、レスポンスボディを返します。
///
///
///
///
public String Request(Uri requestUrl, HttpMethod method)
{
return ReadResponse(RequestInternal(requestUrl, method, Token, TokenSecret));
}
///
/// パラメータを指定してリソースへアクセスし、レスポンスボディを返します。
///
///
///
///
///
public String Request(Uri requestUrl, HttpMethod method, Dictionary parameters)
{
return Request(requestUrl, method, String.Join("&", parameters.Select(kv => String.Concat(Utility.UrlEncode(kv.Key), "=", Utility.UrlEncode(kv.Value))).ToArray()));
}
///
/// パラメータを指定してリソースへアクセスし、レスポンスボディを返します。
///
///
///
///
///
public String Request(Uri requestUrl, HttpMethod method, String parameters)
{
UriBuilder newUri = new UriBuilder(requestUrl);
newUri.Query = ((newUri.Query.Length > 0) ? "&" : "") + parameters;
return ReadResponse(RequestInternal(newUri.Uri, method, Token, TokenSecret));
}
///
/// リソースへアクセス開始し、HttpWebRequestを返します。
///
///
///
///
public HttpWebRequest CreateRequest(Uri requestUrl, HttpMethod method)
{
return RequestInternal(requestUrl, method, Token, TokenSecret);
}
///
/// リソースへアクセス開始し、HttpWebRequestを返します。
///
///
///
///
///
public HttpWebRequest CreateRequest(Uri requestUrl, HttpMethod method, Dictionary parameters)
{
return CreateRequest(requestUrl, method, String.Join("&", parameters.Select(kv => String.Concat(Utility.UrlEncode(kv.Key), "=", Utility.UrlEncode(kv.Value))).ToArray()));
}
///
/// リソースへアクセス開始し、HttpWebRequestを返します。
///
///
///
///
///
public HttpWebRequest CreateRequest(Uri requestUrl, HttpMethod method, String parameters)
{
UriBuilder newUri = new UriBuilder(requestUrl);
newUri.Query = ((newUri.Query.Length > 0) ? "&" : "") + parameters;
return RequestInternal(newUri.Uri, method, Token, TokenSecret);
}
public static Boolean TryGetErrorMessageFromResponseXml(String xmlBody, out String errorMessage)
{
errorMessage = null;
if (xmlBody.IndexOf("") == -1)
return false;
try
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlBody);
XmlNode node = xmlDoc.SelectSingleNode("//hash/error");
if (node != null)
{
errorMessage = node.InnerText;
return true;
}
}
catch (XmlException)
{
}
return false;
}
public static String GetMessageFromException(Exception e)
{
if (e is WebException)
{
using (HttpWebResponse webResponse = (e as WebException).Response as HttpWebResponse)
{
if (webResponse != null &&
webResponse.StatusCode == HttpStatusCode.Unauthorized)
{
try
{
String body = new StreamReader(webResponse.GetResponseStream()).ReadToEnd();
String errorMessage;
if (TwitterOAuth.TryGetErrorMessageFromResponseXml(body, out errorMessage))
{
return "(OAuth) " + errorMessage;
}
else
{
return System.Text.RegularExpressions.Regex.Replace(body, "<[^>]+>", "");
}
}
catch (IOException)
{
}
}
}
}
return e.Message;
}
#region Internal Implementation
private HttpWebRequest RequestInternal(Uri requestUrl, HttpMethod method, String token, String tokenSecret)
{
String normalizedUrl, queryString;
String signature = GenerateSignature(requestUrl,
_consumerKey,
_consumerSecret,
token,
tokenSecret,
method.ToString(),
GenerateTimeStamp(),
GenerateNonce(),
out normalizedUrl,
out queryString);
queryString += "&oauth_signature=" + UrlEncode(signature);
if (method == HttpMethod.GET)
{
return RequestInternalGet(normalizedUrl + "?" + queryString);
}
else
{
return RequestInternalPost(normalizedUrl, queryString);
}
}
private String ReadResponse(HttpWebRequest webRequest)
{
using (var response = webRequest.GetResponse())
{
StreamReader reader = new StreamReader(response.GetResponseStream());
return reader.ReadToEnd();
}
}
private HttpWebRequest RequestInternalGet(String uri)
{
HttpWebRequest webRequest = WebRequest.Create(uri) as HttpWebRequest;
webRequest.ServicePoint.Expect100Continue = false;
webRequest.Timeout = 30 * 1000;
webRequest.Method = "GET";
return webRequest;
}
private HttpWebRequest RequestInternalPost(String uri, String postData)
{
HttpWebRequest webRequest = WebRequest.Create(uri) as HttpWebRequest;
webRequest.ServicePoint.Expect100Continue = false;
webRequest.Timeout = 30 * 1000;
webRequest.Method = "POST";
using (Stream stream = webRequest.GetRequestStream())
{
Byte[] bytes = new UTF8Encoding(false).GetBytes(postData);
stream.Write(bytes, 0, bytes.Length);
}
return webRequest;
}
#endregion
}
}