• R/O
  • HTTP
  • SSH
  • HTTPS

提交

標籤
無標籤

Frequently used words (click to add to your profile)

javac++androidlinuxc#objective-cqtwindows誰得cocoapythonphprubygameguibathyscaphec翻訳omegat計画中(planning stage)frameworktwittertestdomvb.netdirectxbtronarduinopreviewerゲームエンジン

OpenTweenのfork


Commit MetaInfo

修訂58bcc8d19ee28f78c38a4cc63081a8bd79036322 (tree)
時間2012-02-22 19:47:44
作者spinor <spinor2109@gmai...>
CommiterKimura Youichi

Log Message

Port Connection/HttpConnectionOAuth.vb, Connection/HttpConnectionOAuthEcho.vb to C#

Change Summary

差異

--- a/Tween/Connection/HttpConnectionOAuth.vb
+++ /dev/null
@@ -1,568 +0,0 @@
1-' Tween - Client of Twitter
2-' Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3-' (c) 2008-2011 Moz (@syo68k)
4-' (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5-' (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6-' (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7-' All rights reserved.
8-'
9-' This file is part of Tween.
10-'
11-' This program is free software; you can redistribute it and/or modify it
12-' under the terms of the GNU General Public License as published by the Free
13-' Software Foundation; either version 3 of the License, or (at your option)
14-' any later version.
15-'
16-' This program is distributed in the hope that it will be useful, but
17-' WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18-' or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19-' for more details.
20-'
21-' You should have received a copy of the GNU General Public License along
22-' with this program. If not, see <http://www.gnu.org/licenses/>, or write to
23-' the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
24-' Boston, MA 02110-1301, USA.
25-
26-Imports System.Collections.Specialized
27-Imports System.Diagnostics
28-Imports System.IO
29-Imports System.Net
30-Imports System.Security
31-Imports System.Text
32-
33-'''<summary>
34-'''OAuth認証を使用するHTTP通信。HMAC-SHA1固定
35-'''</summary>
36-'''<remarks>
37-'''使用前に認証情報を設定する。認証確認を伴う場合はAuthenticate系のメソッドを、認証不要な場合はInitializeを呼ぶこと。
38-'''</remarks>
39-Public Class HttpConnectionOAuth
40- Inherits HttpConnection
41- Implements IHttpConnection
42-
43- '''<summary>
44- '''OAuth署名のoauth_timestamp算出用基準日付(1970/1/1 00:00:00)
45- '''</summary>
46- Private Shared ReadOnly UnixEpoch As New DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified)
47-
48- '''<summary>
49- '''OAuth署名のoauth_nonce算出用乱数クラス
50- '''</summary>
51- Private Shared ReadOnly NonceRandom As New Random
52-
53- '''<summary>
54- '''OAuthのアクセストークン。永続化可能(ユーザー取り消しの可能性はある)。
55- '''</summary>
56- Private token As String = ""
57-
58- '''<summary>
59- '''OAuthの署名作成用秘密アクセストークン。永続化可能(ユーザー取り消しの可能性はある)。
60- '''</summary>
61- Private tokenSecret As String = ""
62-
63- '''<summary>
64- '''OAuthのコンシューマー鍵
65- '''</summary>
66- Private consumerKey As String
67-
68- '''<summary>
69- '''OAuthの署名作成用秘密コンシューマーデータ
70- '''</summary>
71- Protected consumerSecret As String
72-
73- '''<summary>
74- '''認証成功時の応答でユーザー情報を取得する場合のキー。設定しない場合は、AuthUsernameもブランクのままとなる
75- '''</summary>
76- Private userIdentKey As String = ""
77-
78- '''<summary>
79- '''認証成功時の応答でユーザーID情報を取得する場合のキー。設定しない場合は、AuthUserIdもブランクのままとなる
80- '''</summary>
81- Private userIdIdentKey As String = ""
82-
83- '''<summary>
84- '''認証完了時の応答からuserIdentKey情報に基づいて取得するユーザー情報
85- '''</summary>
86- Private authorizedUsername As String = ""
87-
88- '''<summary>
89- '''認証完了時の応答からuserIdentKey情報に基づいて取得するユーザー情報
90- '''</summary>
91- Private authorizedUserId As Long
92-
93- '''<summary>
94- '''Stream用のHttpWebRequest
95- '''</summary>
96- Private streamReq As HttpWebRequest = Nothing
97-
98- '''<summary>
99- '''OAuth認証で指定のURLとHTTP通信を行い、結果を返す
100- '''</summary>
101- '''<param name="method">HTTP通信メソッド(GET/HEAD/POST/PUT/DELETE)</param>
102- '''<param name="requestUri">通信先URI</param>
103- '''<param name="param">GET時のクエリ、またはPOST時のエンティティボディ</param>
104- '''<param name="content">[OUT]HTTP応答のボディデータ</param>
105- '''<param name="headerInfo">[IN/OUT]HTTP応答のヘッダ情報。必要なヘッダ名を事前に設定しておくこと</param>
106- '''<param name="callback">処理終了直前に呼ばれるコールバック関数のデリゲート 不要な場合はNothingを渡すこと</param>
107- '''<returns>HTTP応答のステータスコード</returns>
108- Public Function GetContent(ByVal method As String, _
109- ByVal requestUri As Uri, _
110- ByVal param As Dictionary(Of String, String), _
111- ByRef content As String, _
112- ByVal headerInfo As Dictionary(Of String, String), _
113- ByVal callback As CallbackDelegate) As HttpStatusCode Implements IHttpConnection.GetContent
114- '認証済かチェック
115- If String.IsNullOrEmpty(token) Then Return HttpStatusCode.Unauthorized
116-
117- Dim webReq As HttpWebRequest = CreateRequest(method, _
118- requestUri, _
119- param, _
120- False)
121- 'OAuth認証ヘッダを付加
122- AppendOAuthInfo(webReq, param, token, tokenSecret)
123-
124- Dim code As HttpStatusCode
125- If content Is Nothing Then
126- code = GetResponse(webReq, headerInfo, False)
127- Else
128- code = GetResponse(webReq, content, headerInfo, False)
129- End If
130- If callback IsNot Nothing Then
131- Dim frame As New StackFrame(1)
132- callback(frame.GetMethod.Name, code, content)
133- End If
134- Return code
135- End Function
136-
137- '''<summary>
138- '''バイナリアップロード
139- '''</summary>
140- Public Function GetContent(ByVal method As String, _
141- ByVal requestUri As Uri, _
142- ByVal param As Dictionary(Of String, String), _
143- ByVal binary As List(Of KeyValuePair(Of String, FileInfo)), _
144- ByRef content As String, _
145- ByVal headerInfo As Dictionary(Of String, String), _
146- ByVal callback As CallbackDelegate) As HttpStatusCode Implements IHttpConnection.GetContent
147- '認証済かチェック
148- If String.IsNullOrEmpty(token) Then Return HttpStatusCode.Unauthorized
149-
150- Dim webReq As HttpWebRequest = CreateRequest(method, _
151- requestUri, _
152- param, _
153- binary, _
154- False)
155- 'OAuth認証ヘッダを付加
156- AppendOAuthInfo(webReq, Nothing, token, tokenSecret)
157-
158- Dim code As HttpStatusCode
159- If content Is Nothing Then
160- code = GetResponse(webReq, headerInfo, False)
161- Else
162- code = GetResponse(webReq, content, headerInfo, False)
163- End If
164- If callback IsNot Nothing Then
165- Dim frame As New StackFrame(1)
166- callback(frame.GetMethod.Name, code, content)
167- End If
168- Return code
169- End Function
170-
171- '''<summary>
172- '''OAuth認証で指定のURLとHTTP通信を行い、ストリームを返す
173- '''</summary>
174- '''<param name="method">HTTP通信メソッド(GET/HEAD/POST/PUT/DELETE)</param>
175- '''<param name="requestUri">通信先URI</param>
176- '''<param name="param">GET時のクエリ、またはPOST時のエンティティボディ</param>
177- '''<param name="content">[OUT]HTTP応答のボディストリーム</param>
178- '''<returns>HTTP応答のステータスコード</returns>
179- Public Function GetContent(ByVal method As String, _
180- ByVal requestUri As Uri, _
181- ByVal param As Dictionary(Of String, String), _
182- ByRef content As Stream,
183- ByVal userAgent As String) As HttpStatusCode Implements IHttpConnection.GetContent
184- '認証済かチェック
185- If String.IsNullOrEmpty(token) Then Return HttpStatusCode.Unauthorized
186-
187- Me.RequestAbort()
188- streamReq = CreateRequest(method, requestUri, param, False)
189- 'User-Agent指定がある場合は付加
190- If Not String.IsNullOrEmpty(userAgent) Then streamReq.UserAgent = userAgent
191-
192- 'OAuth認証ヘッダを付加
193- AppendOAuthInfo(streamReq, param, token, tokenSecret)
194-
195- Try
196- Dim webRes As HttpWebResponse = CType(streamReq.GetResponse(), HttpWebResponse)
197- content = webRes.GetResponseStream()
198- Return webRes.StatusCode
199- Catch ex As WebException
200- If ex.Status = WebExceptionStatus.ProtocolError Then
201- Dim res As HttpWebResponse = DirectCast(ex.Response, HttpWebResponse)
202- Return res.StatusCode
203- End If
204- Throw
205- End Try
206-
207- End Function
208-
209- Public Sub RequestAbort() Implements IHttpConnection.RequestAbort
210- Try
211- If streamReq IsNot Nothing Then
212- streamReq.Abort()
213- streamReq = Nothing
214- End If
215- Catch ex As Exception
216- End Try
217- End Sub
218-
219-
220-#Region "認証処理"
221- '''<summary>
222- '''OAuth認証の開始要求(リクエストトークン取得)。PIN入力用の前段
223- '''</summary>
224- '''<remarks>
225- '''呼び出し元では戻されたurlをブラウザで開き、認証完了後PIN入力を受け付けて、リクエストトークンと共にAuthenticatePinFlowを呼び出す
226- '''</remarks>
227- '''<param name="requestTokenUrl">リクエストトークンの取得先URL</param>
228- '''<param name="requestUri">ブラウザで開く認証用URLのベース</param>
229- '''<param name="requestToken">[OUT]認証要求で戻されるリクエストトークン。使い捨て</param>
230- '''<param name="authUri">[OUT]requestUriを元に生成された認証用URL。通常はリクエストトークンをクエリとして付加したUri</param>
231- '''<returns>取得結果真偽値</returns>
232- Public Function AuthenticatePinFlowRequest(ByVal requestTokenUrl As String, _
233- ByVal authorizeUrl As String, _
234- ByRef requestToken As String, _
235- ByRef authUri As Uri) As Boolean
236- 'PIN-based flow
237- authUri = GetAuthenticatePageUri(requestTokenUrl, authorizeUrl, requestToken)
238- If authUri Is Nothing Then Return False
239- Return True
240- End Function
241-
242- '''<summary>
243- '''OAuth認証のアクセストークン取得。PIN入力用の後段
244- '''</summary>
245- '''<remarks>
246- '''事前にAuthenticatePinFlowRequestを呼んで、ブラウザで認証後に表示されるPINを入力してもらい、その値とともに呼び出すこと
247- '''</remarks>
248- '''<param name="accessTokenUrl">アクセストークンの取得先URL</param>
249- '''<param name="requestUri">AuthenticatePinFlowRequestで取得したリクエストトークン</param>
250- '''<param name="pinCode">Webで認証後に表示されるPINコード</param>
251- '''<returns>取得結果真偽値</returns>
252- Public Function AuthenticatePinFlow(ByVal accessTokenUrl As String, _
253- ByVal requestToken As String, _
254- ByVal pinCode As String) As HttpStatusCode
255- 'PIN-based flow
256- If String.IsNullOrEmpty(requestToken) Then Throw New Exception("Sequence error.(requestToken is blank)")
257-
258- 'アクセストークン取得
259- Dim content As String = ""
260- Dim accessTokenData As NameValueCollection
261- Dim httpCode As HttpStatusCode = GetOAuthToken(New Uri(accessTokenUrl), pinCode, requestToken, Nothing, content)
262- If httpCode <> HttpStatusCode.OK Then Return httpCode
263- accessTokenData = ParseQueryString(content)
264-
265- If accessTokenData IsNot Nothing Then
266- token = accessTokenData.Item("oauth_token")
267- tokenSecret = accessTokenData.Item("oauth_token_secret")
268- 'サービスごとの独自拡張対応
269- If Me.userIdentKey <> "" Then
270- authorizedUsername = accessTokenData.Item(Me.userIdentKey)
271- Else
272- authorizedUsername = ""
273- End If
274- If Me.userIdIdentKey <> "" Then
275- Try
276- authorizedUserId = CLng(accessTokenData.Item(Me.userIdIdentKey))
277- Catch ex As Exception
278- authorizedUserId = 0
279- End Try
280- Else
281- authorizedUserId = 0
282- End If
283- If token = "" Then Throw New InvalidDataException("Token is null.")
284- Return HttpStatusCode.OK
285- Else
286- Throw New InvalidDataException("Return value is null.")
287- End If
288- End Function
289-
290- '''<summary>
291- '''OAuth認証のアクセストークン取得。xAuth方式
292- '''</summary>
293- '''<param name="accessTokenUrl">アクセストークンの取得先URL</param>
294- '''<param name="username">認証用ユーザー名</param>
295- '''<param name="password">認証用パスワード</param>
296- '''<returns>取得結果真偽値</returns>
297- Public Function AuthenticateXAuth(ByVal accessTokenUrl As Uri, ByVal username As String, ByVal password As String, ByRef content As String) As HttpStatusCode Implements IHttpConnection.Authenticate
298- 'ユーザー・パスワードチェック
299- If String.IsNullOrEmpty(username) OrElse String.IsNullOrEmpty(password) Then
300- Throw New Exception("Sequence error.(username or password is blank)")
301- End If
302- 'xAuthの拡張パラメータ設定
303- Dim parameter As New Dictionary(Of String, String)
304- parameter.Add("x_auth_mode", "client_auth")
305- parameter.Add("x_auth_username", username)
306- parameter.Add("x_auth_password", password)
307-
308- 'アクセストークン取得
309- Dim httpCode As HttpStatusCode = GetOAuthToken(accessTokenUrl, "", "", parameter, content)
310- If httpCode <> HttpStatusCode.OK Then Return httpCode
311- Dim accessTokenData As NameValueCollection = ParseQueryString(content)
312-
313- If accessTokenData IsNot Nothing Then
314- token = accessTokenData.Item("oauth_token")
315- tokenSecret = accessTokenData.Item("oauth_token_secret")
316- 'サービスごとの独自拡張対応
317- If Me.userIdentKey <> "" Then
318- authorizedUsername = accessTokenData.Item(Me.userIdentKey)
319- Else
320- authorizedUsername = ""
321- End If
322- If Me.userIdIdentKey <> "" Then
323- Try
324- authorizedUserId = CLng(accessTokenData.Item(Me.userIdIdentKey))
325- Catch ex As Exception
326- authorizedUserId = 0
327- End Try
328- Else
329- authorizedUserId = 0
330- End If
331- If token = "" Then Throw New InvalidDataException("Token is null.")
332- Return HttpStatusCode.OK
333- Else
334- Throw New InvalidDataException("Return value is null.")
335- End If
336- End Function
337-
338- '''<summary>
339- '''OAuth認証のリクエストトークン取得。リクエストトークンと組み合わせた認証用のUriも生成する
340- '''</summary>
341- '''<param name="accessTokenUrl">リクエストトークンの取得先URL</param>
342- '''<param name="authorizeUrl">ブラウザで開く認証用URLのベース</param>
343- '''<param name="requestToken">[OUT]取得したリクエストトークン</param>
344- '''<returns>取得結果真偽値</returns>
345- Private Function GetAuthenticatePageUri(ByVal requestTokenUrl As String, _
346- ByVal authorizeUrl As String, _
347- ByRef requestToken As String) As Uri
348- Const tokenKey As String = "oauth_token"
349-
350- 'リクエストトークン取得
351- Dim content As String = ""
352- Dim reqTokenData As NameValueCollection
353- If GetOAuthToken(New Uri(requestTokenUrl), "", "", Nothing, content) <> HttpStatusCode.OK Then Return Nothing
354- reqTokenData = ParseQueryString(content)
355-
356- If reqTokenData IsNot Nothing Then
357- requestToken = reqTokenData.Item(tokenKey)
358- 'Uri生成
359- Dim ub As New UriBuilder(authorizeUrl)
360- ub.Query = String.Format("{0}={1}", tokenKey, requestToken)
361- Return ub.Uri
362- Else
363- Return Nothing
364- End If
365- End Function
366-
367- '''<summary>
368- '''OAuth認証のトークン取得共通処理
369- '''</summary>
370- '''<param name="requestUri">各種トークンの取得先URL</param>
371- '''<param name="pinCode">PINフロー時のアクセストークン取得時に設定。それ以外は空文字列</param>
372- '''<param name="requestToken">PINフロー時のリクエストトークン取得時に設定。それ以外は空文字列</param>
373- '''<param name="parameter">追加パラメータ。xAuthで使用</param>
374- '''<returns>取得結果のデータ。正しく取得出来なかった場合はNothing</returns>
375- Private Function GetOAuthToken(ByVal requestUri As Uri, ByVal pinCode As String, ByVal requestToken As String, ByVal parameter As Dictionary(Of String, String), ByRef content As String) As HttpStatusCode
376- Dim webReq As HttpWebRequest = Nothing
377- 'HTTPリクエスト生成。PINコードもパラメータも未指定の場合はGETメソッドで通信。それ以外はPOST
378- If String.IsNullOrEmpty(pinCode) AndAlso parameter Is Nothing Then
379- webReq = CreateRequest("GET", requestUri, Nothing, False)
380- Else
381- webReq = CreateRequest("POST", requestUri, parameter, False) 'ボディに追加パラメータ書き込み
382- End If
383- 'OAuth関連パラメータ準備。追加パラメータがあれば追加
384- Dim query As New Dictionary(Of String, String)
385- If parameter IsNot Nothing Then
386- For Each kvp As KeyValuePair(Of String, String) In parameter
387- query.Add(kvp.Key, kvp.Value)
388- Next
389- End If
390- 'PINコードが指定されていればパラメータに追加
391- If Not String.IsNullOrEmpty(pinCode) Then query.Add("oauth_verifier", pinCode)
392- 'OAuth関連情報をHTTPリクエストに追加
393- AppendOAuthInfo(webReq, query, requestToken, "")
394- 'HTTP応答取得
395- Dim header As New Dictionary(Of String, String) From {{"Date", ""}}
396- Dim responseCode As HttpStatusCode = GetResponse(webReq, content, header, False)
397- If responseCode = HttpStatusCode.OK Then Return responseCode
398- If Not String.IsNullOrEmpty(header("Date")) Then content += Environment.NewLine + "Check the Date & Time of this computer." + Environment.NewLine + "Server:" + CDate(header("Date")).ToString + " PC:" + Now.ToString
399- Return responseCode
400- End Function
401-#End Region
402-
403-#Region "OAuth認証用ヘッダ作成・付加処理"
404- '''<summary>
405- '''HTTPリクエストにOAuth関連ヘッダを追加
406- '''</summary>
407- '''<param name="webRequest">追加対象のHTTPリクエスト</param>
408- '''<param name="query">OAuth追加情報+クエリ or POSTデータ</param>
409- '''<param name="token">アクセストークン、もしくはリクエストトークン。未取得なら空文字列</param>
410- '''<param name="tokenSecret">アクセストークンシークレット。認証処理では空文字列</param>
411- Protected Overridable Sub AppendOAuthInfo(ByVal webRequest As HttpWebRequest, _
412- ByVal query As Dictionary(Of String, String), _
413- ByVal token As String, _
414- ByVal tokenSecret As String)
415- 'OAuth共通情報取得
416- Dim parameter As Dictionary(Of String, String) = GetOAuthParameter(token)
417- 'OAuth共通情報にquery情報を追加
418- If query IsNot Nothing Then
419- For Each item As KeyValuePair(Of String, String) In query
420- parameter.Add(item.Key, item.Value)
421- Next
422- End If
423- '署名の作成・追加
424- parameter.Add("oauth_signature", CreateSignature(tokenSecret, webRequest.Method, webRequest.RequestUri, parameter))
425- 'HTTPリクエストのヘッダに追加
426- Dim sb As New StringBuilder("OAuth ")
427- For Each item As KeyValuePair(Of String, String) In parameter
428- '各種情報のうち、oauth_で始まる情報のみ、ヘッダに追加する。各情報はカンマ区切り、データはダブルクォーテーションで括る
429- If item.Key.StartsWith("oauth_") Then
430- sb.AppendFormat("{0}=""{1}"",", item.Key, UrlEncode(item.Value))
431- End If
432- Next
433- webRequest.Headers.Add(HttpRequestHeader.Authorization, sb.ToString)
434- End Sub
435-
436- '''<summary>
437- '''OAuthで使用する共通情報を取得する
438- '''</summary>
439- '''<param name="token">アクセストークン、もしくはリクエストトークン。未取得なら空文字列</param>
440- '''<returns>OAuth情報のディクショナリ</returns>
441- Protected Function GetOAuthParameter(ByVal token As String) As Dictionary(Of String, String)
442- Dim parameter As New Dictionary(Of String, String)
443- parameter.Add("oauth_consumer_key", consumerKey)
444- parameter.Add("oauth_signature_method", "HMAC-SHA1")
445- parameter.Add("oauth_timestamp", Convert.ToInt64((DateTime.UtcNow - UnixEpoch).TotalSeconds).ToString()) 'epoch秒
446- parameter.Add("oauth_nonce", NonceRandom.Next(123400, 9999999).ToString())
447- parameter.Add("oauth_version", "1.0")
448- If Not String.IsNullOrEmpty(token) Then parameter.Add("oauth_token", token) 'トークンがあれば追加
449- Return parameter
450- End Function
451-
452- '''<summary>
453- '''OAuth認証ヘッダの署名作成
454- '''</summary>
455- '''<param name="tokenSecret">アクセストークン秘密鍵</param>
456- '''<param name="method">HTTPメソッド文字列</param>
457- '''<param name="uri">アクセス先Uri</param>
458- '''<param name="parameter">クエリ、もしくはPOSTデータ</param>
459- '''<returns>署名文字列</returns>
460- Protected Overridable Function CreateSignature(ByVal tokenSecret As String, _
461- ByVal method As String, _
462- ByVal uri As Uri, _
463- ByVal parameter As Dictionary(Of String, String) _
464- ) As String
465- 'パラメタをソート済みディクショナリに詰替(OAuthの仕様)
466- Dim sorted As New SortedDictionary(Of String, String)(parameter)
467- 'URLエンコード済みのクエリ形式文字列に変換
468- Dim paramString As String = CreateQueryString(sorted)
469- 'アクセス先URLの整形
470- Dim url As String = String.Format("{0}://{1}{2}", uri.Scheme, uri.Host, uri.AbsolutePath)
471- '署名のベース文字列生成(&区切り)。クエリ形式文字列は再エンコードする
472- Dim signatureBase As String = String.Format("{0}&{1}&{2}", method, UrlEncode(url), UrlEncode(paramString))
473- '署名鍵の文字列をコンシューマー秘密鍵とアクセストークン秘密鍵から生成(&区切り。アクセストークン秘密鍵なくても&残すこと)
474- Dim key As String = UrlEncode(consumerSecret) + "&"
475- If Not String.IsNullOrEmpty(tokenSecret) Then key += UrlEncode(tokenSecret)
476- '鍵生成&署名生成
477- Using hmac As New Cryptography.HMACSHA1(Encoding.ASCII.GetBytes(key))
478- Dim hash As Byte() = hmac.ComputeHash(Encoding.ASCII.GetBytes(signatureBase))
479- Return Convert.ToBase64String(hash)
480- End Using
481- End Function
482-
483-#End Region
484-
485- '''<summary>
486- '''初期化。各種トークンの設定とユーザー識別情報設定
487- '''</summary>
488- '''<param name="consumerKey">コンシューマー鍵</param>
489- '''<param name="consumerSecret">コンシューマー秘密鍵</param>
490- '''<param name="accessToken">アクセストークン</param>
491- '''<param name="accessTokenSecret">アクセストークン秘密鍵</param>
492- '''<param name="userIdentifier">アクセストークン取得時に得られるユーザー識別情報。不要なら空文字列</param>
493- Public Sub Initialize(ByVal consumerKey As String, _
494- ByVal consumerSecret As String, _
495- ByVal accessToken As String, _
496- ByVal accessTokenSecret As String, _
497- ByVal userIdentifier As String,
498- ByVal userIdIdentifier As String)
499- Me.consumerKey = consumerKey
500- Me.consumerSecret = consumerSecret
501- Me.token = accessToken
502- Me.tokenSecret = accessTokenSecret
503- Me.userIdentKey = userIdentifier
504- Me.userIdIdentKey = userIdIdentifier
505- End Sub
506-
507- '''<summary>
508- '''初期化。各種トークンの設定とユーザー識別情報設定
509- '''</summary>
510- '''<param name="consumerKey">コンシューマー鍵</param>
511- '''<param name="consumerSecret">コンシューマー秘密鍵</param>
512- '''<param name="accessToken">アクセストークン</param>
513- '''<param name="accessTokenSecret">アクセストークン秘密鍵</param>
514- '''<param name="username">認証済みユーザー名</param>
515- '''<param name="userIdentifier">アクセストークン取得時に得られるユーザー識別情報。不要なら空文字列</param>
516- Public Sub Initialize(ByVal consumerKey As String, _
517- ByVal consumerSecret As String, _
518- ByVal accessToken As String, _
519- ByVal accessTokenSecret As String, _
520- ByVal username As String, _
521- ByVal userId As Long,
522- ByVal userIdentifier As String,
523- ByVal userIdIdentifier As String)
524- Initialize(consumerKey, consumerSecret, accessToken, accessTokenSecret, userIdentifier, userIdIdentifier)
525- authorizedUsername = username
526- authorizedUserId = userId
527- End Sub
528-
529- '''<summary>
530- '''アクセストークン
531- '''</summary>
532- Public ReadOnly Property AccessToken() As String
533- Get
534- Return token
535- End Get
536- End Property
537-
538- '''<summary>
539- '''アクセストークン秘密鍵
540- '''</summary>
541- Public ReadOnly Property AccessTokenSecret() As String
542- Get
543- Return tokenSecret
544- End Get
545- End Property
546-
547- '''<summary>
548- '''認証済みユーザー名
549- '''</summary>
550- Public ReadOnly Property AuthUsername() As String Implements IHttpConnection.AuthUsername
551- Get
552- Return authorizedUsername
553- End Get
554- End Property
555-
556- '''<summary>
557- '''認証済みユーザーId
558- '''</summary>
559- Public Property AuthUserId() As Long Implements IHttpConnection.AuthUserId
560- Get
561- Return authorizedUserId
562- End Get
563- Set(ByVal value As Long)
564- authorizedUserId = value
565- End Set
566- End Property
567-
568-End Class
--- a/Tween/Connection/HttpConnectionOAuthEcho.vb
+++ /dev/null
@@ -1,81 +0,0 @@
1-' Tween - Client of Twitter
2-' Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3-' (c) 2008-2011 Moz (@syo68k)
4-' (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5-' (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6-' (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7-' All rights reserved.
8-'
9-' This file is part of Tween.
10-'
11-' This program is free software; you can redistribute it and/or modify it
12-' under the terms of the GNU General Public License as published by the Free
13-' Software Foundation; either version 3 of the License, or (at your option)
14-' any later version.
15-'
16-' This program is distributed in the hope that it will be useful, but
17-' WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18-' or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19-' for more details.
20-'
21-' You should have received a copy of the GNU General Public License along
22-' with this program. If not, see <http://www.gnu.org/licenses/>, or write to
23-' the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
24-' Boston, MA 02110-1301, USA.
25-
26-Imports System.Net
27-Imports System.Text
28-
29-Public Class HttpConnectionOAuthEcho
30- Inherits HttpConnectionOAuth
31-
32- Private _realm As Uri
33- Private _serviceProvider As Uri
34- Private _token As String
35- Private _tokenSecret As String
36-
37- Public WriteOnly Property Realm As Uri
38- Set(ByVal value As Uri)
39- _realm = value
40- End Set
41- End Property
42-
43- Public WriteOnly Property ServiceProvider As Uri
44- Set(ByVal value As Uri)
45- _serviceProvider = value
46- End Set
47- End Property
48-
49- Protected Overrides Sub AppendOAuthInfo(ByVal webRequest As HttpWebRequest, _
50- ByVal query As Dictionary(Of String, String), _
51- ByVal token As String, _
52- ByVal tokenSecret As String)
53- 'OAuth共通情報取得
54- Dim parameter As Dictionary(Of String, String) = GetOAuthParameter(token)
55- 'OAuth共通情報にquery情報を追加
56- If query IsNot Nothing Then
57- For Each item As KeyValuePair(Of String, String) In query
58- parameter.Add(item.Key, item.Value)
59- Next
60- End If
61- '署名の作成・追加(GETメソッド固定。ServiceProvider呼び出し用の署名作成)
62- parameter.Add("oauth_signature", CreateSignature(tokenSecret, GetMethod, _serviceProvider, parameter))
63- 'HTTPリクエストのヘッダに追加
64- Dim sb As New StringBuilder("OAuth ")
65- sb.AppendFormat("realm=""{0}://{1}{2}"",", _realm.Scheme, _realm.Host, _realm.AbsolutePath)
66- For Each item As KeyValuePair(Of String, String) In parameter
67- '各種情報のうち、oauth_で始まる情報のみ、ヘッダに追加する。各情報はカンマ区切り、データはダブルクォーテーションで括る
68- If item.Key.StartsWith("oauth_") Then
69- sb.AppendFormat("{0}=""{1}"",", item.Key, UrlEncode(item.Value))
70- End If
71- Next
72- webRequest.Headers.Add("X-Verify-Credentials-Authorization", sb.ToString)
73- webRequest.Headers.Add("X-Auth-Service-Provider", String.Format("{0}://{1}{2}", _serviceProvider.Scheme, _serviceProvider.Host, _serviceProvider.AbsolutePath))
74- End Sub
75-
76-
77- Public Sub New(ByVal realm As Uri, ByVal serviceProvider As Uri)
78- _realm = realm
79- _serviceProvider = serviceProvider
80- End Sub
81-End Class
--- a/Tween/Tween.vbproj
+++ b/Tween/Tween.vbproj
@@ -134,9 +134,7 @@
134134 <Compile Include="AuthBrowser.vb">
135135 <SubType>Form</SubType>
136136 </Compile>
137- <Compile Include="Connection\HttpConnectionOAuthEcho.vb" />
138137 <Compile Include="Connection\HttpOAuthApiProxy.vb" />
139- <Compile Include="Connection\HttpConnectionOAuth.vb" />
140138 <Compile Include="Connection\HttpTwitter.vb" />
141139 <Compile Include="Connection\Plixi.vb" />
142140 <Compile Include="Connection\TwitPic.vb" />
--- /dev/null
+++ b/TweenCS/Connection/HttpConnectionOAuth.cs
@@ -0,0 +1,615 @@
1+// OpenTween - Client of Twitter
2+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3+// (c) 2008-2011 Moz (@syo68k)
4+// (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5+// (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6+// (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7+// (c) 2011 spinor (@tplantd) <http://d.hatena.ne.jp/spinor/>
8+// All rights reserved.
9+//
10+// This file is part of OpenTween.
11+//
12+// This program is free software; you can redistribute it and/or modify it
13+// under the terms of the GNU General Public License as published by the Free
14+// Software Foundation; either version 3 of the License, or (at your option)
15+// any later version.
16+//
17+// This program is distributed in the hope that it will be useful, but
18+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20+// for more details.
21+//
22+// You should have received a copy of the GNU General Public License along
23+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
24+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
25+// Boston, MA 02110-1301, USA.
26+
27+using HttpConnection = Tween.HttpConnection;
28+using IHttpConnection = Tween.IHttpConnection;
29+using DateTime = System.DateTime;
30+using DateTimeKind = System.DateTimeKind;
31+using Random = System.Random;
32+using HttpWebRequest = System.Net.HttpWebRequest;
33+using HttpStatusCode = System.Net.HttpStatusCode;
34+using Uri = System.Uri;
35+using System.Collections.Generic; // for Dictionary<TKey, TValue>, List<T>, KeyValuePair<TKey, TValue>, SortedDictionary<TKey, TValue>
36+using CallbackDelegate = Tween.CallbackDelegate;
37+using StackFrame = System.Diagnostics.StackFrame;
38+using FileInfo = System.IO.FileInfo;
39+using Stream = System.IO.Stream;
40+using HttpWebResponse = System.Net.HttpWebResponse;
41+using WebException = System.Net.WebException;
42+using WebExceptionStatus = System.Net.WebExceptionStatus;
43+using Exception = System.Exception;
44+using NameValueCollection = System.Collections.Specialized.NameValueCollection;
45+using Convert = System.Convert;
46+using InvalidDataException = System.IO.InvalidDataException;
47+using UriBuilder = System.UriBuilder;
48+using Environment = System.Environment;
49+using DateTime = System.DateTime;
50+using StringBuilder = System.Text.StringBuilder;
51+using HttpRequestHeader = System.Net.HttpRequestHeader;
52+using HMACSHA1 = System.Security.Cryptography.HMACSHA1;
53+using Encoding = System.Text.Encoding;
54+
55+namespace Tween
56+{
57+ /// <summary>
58+ /// OAuth認証を使用するHTTP通信。HMAC-SHA1固定
59+ /// </summary>
60+ /// <remarks>
61+ /// 使用前に認証情報を設定する。認証確認を伴う場合はAuthenticate系のメソッドを、認証不要な場合はInitializeを呼ぶこと。
62+ /// </remarks>
63+ public class HttpConnectionOAuth : HttpConnection, IHttpConnection
64+ {
65+ /// <summary>
66+ /// OAuth署名のoauth_timestamp算出用基準日付(1970/1/1 00:00:00)
67+ /// </summary>
68+ private static readonly DateTime UnixEpoch = new DateTime( 1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified );
69+
70+ /// <summary>
71+ /// OAuth署名のoauth_nonce算出用乱数クラス
72+ /// </summary>
73+ private static readonly Random NonceRandom = new Random();
74+
75+ /// <summary>
76+ /// OAuthのアクセストークン。永続化可能(ユーザー取り消しの可能性はある)。
77+ /// </summary>
78+ private string token = "";
79+
80+ /// <summary>
81+ /// OAuthの署名作成用秘密アクセストークン。永続化可能(ユーザー取り消しの可能性はある)。
82+ /// </summary>
83+ private string tokenSecret = "";
84+
85+ /// <summary>
86+ /// OAuthのコンシューマー鍵
87+ /// </summary>
88+ private string consumerKey;
89+
90+ /// <summary>
91+ /// OAuthの署名作成用秘密コンシューマーデータ
92+ /// </summary>
93+ private string consumerSecret;
94+
95+ /// <summary>
96+ /// 認証成功時の応答でユーザー情報を取得する場合のキー。設定しない場合は、AuthUsernameもブランクのままとなる
97+ /// </summary>
98+ private string userIdentKey = "";
99+
100+ /// <summary>
101+ /// 認証成功時の応答でユーザーID情報を取得する場合のキー。設定しない場合は、AuthUserIdもブランクのままとなる
102+ /// </summary>
103+ private string userIdIdentKey = "";
104+
105+ /// <summary>
106+ /// 認証完了時の応答からuserIdentKey情報に基づいて取得するユーザー情報
107+ /// </summary>
108+ private string authorizedUsername = "";
109+
110+ /// <summary>
111+ /// 認証完了時の応答からuserIdentKey情報に基づいて取得するユーザー情報
112+ /// </summary>
113+ private long authorizedUserId;
114+
115+ /// <summary>
116+ /// Stream用のHttpWebRequest
117+ /// </summary>
118+ private HttpWebRequest streamReq = null;
119+
120+ /// <summary>
121+ /// OAuth認証で指定のURLとHTTP通信を行い、結果を返す
122+ /// </summary>
123+ /// <param name="method">HTTP通信メソッド(GET/HEAD/POST/PUT/DELETE)</param>
124+ /// <param name="requestUri">通信先URI</param>
125+ /// <param name="param">GET時のクエリ、またはPOST時のエンティティボディ</param>
126+ /// <param name="content">[OUT]HTTP応答のボディデータ</param>
127+ /// <param name="headerInfo">[IN/OUT]HTTP応答のヘッダ情報。必要なヘッダ名を事前に設定しておくこと</param>
128+ /// <param name="callback">処理終了直前に呼ばれるコールバック関数のデリゲート 不要な場合はNothingを渡すこと</param>
129+ /// <returns>HTTP応答のステータスコード</returns>
130+ public HttpStatusCode GetContent( string method,
131+ Uri requestUri,
132+ Dictionary< string, string > param,
133+ ref string content,
134+ Dictionary< string, string > headerInfo,
135+ CallbackDelegate callback )
136+ {
137+ // 認証済かチェック
138+ if ( string.IsNullOrEmpty( token ) )
139+ return HttpStatusCode.Unauthorized;
140+
141+ HttpWebRequest webReq = this.CreateRequest( method, requestUri, param, false );
142+ // OAuth認証ヘッダを付加
143+ this.AppendOAuthInfo( webReq, param, token, tokenSecret );
144+
145+ HttpStatusCode code;
146+ if ( content == null )
147+ code = this.GetResponse( webReq, headerInfo, false );
148+ else
149+ code = this.GetResponse( webReq, ref content, headerInfo, false );
150+
151+ if ( callback != null )
152+ {
153+ StackFrame frame = new StackFrame( 1 );
154+ callback( frame.GetMethod().Name, ref code, ref content );
155+ }
156+ return code;
157+ }
158+
159+ /// <summary>
160+ /// バイナリアップロード
161+ /// </summary>
162+ public HttpStatusCode GetContent( string method,
163+ Uri requestUri,
164+ Dictionary< string, string > param,
165+ List< KeyValuePair< string, FileInfo > > binary,
166+ ref string content,
167+ Dictionary< string, string > headerInfo,
168+ CallbackDelegate callback )
169+ {
170+ // 認証済かチェック
171+ if ( string.IsNullOrEmpty( token ) )
172+ return HttpStatusCode.Unauthorized;
173+
174+ HttpWebRequest webReq = this.CreateRequest( method, requestUri, param, binary, false );
175+ // OAuth認証ヘッダを付加
176+ this.AppendOAuthInfo( webReq, null, token, tokenSecret );
177+
178+ HttpStatusCode code;
179+ if ( content == null )
180+ code = this.GetResponse( webReq, headerInfo, false );
181+ else
182+ code = this.GetResponse( webReq, ref content, headerInfo, false );
183+
184+ if ( callback != null )
185+ {
186+ StackFrame frame = new StackFrame( 1 );
187+ callback( frame.GetMethod().Name, ref code, ref content );
188+ }
189+ return code;
190+ }
191+
192+ /// <summary>
193+ /// OAuth認証で指定のURLとHTTP通信を行い、ストリームを返す
194+ /// </summary>
195+ /// <param name="method">HTTP通信メソッド(GET/HEAD/POST/PUT/DELETE)</param>
196+ /// <param name="requestUri">通信先URI</param>
197+ /// <param name="param">GET時のクエリ、またはPOST時のエンティティボディ</param>
198+ /// <param name="content">[OUT]HTTP応答のボディストリーム</param>
199+ /// <returns>HTTP応答のステータスコード</returns>
200+ public HttpStatusCode GetContent( string method,
201+ Uri requestUri,
202+ Dictionary< string, string > param,
203+ ref Stream content,
204+ string userAgent )
205+ {
206+ // 認証済かチェック
207+ if ( string.IsNullOrEmpty( token ) )
208+ return HttpStatusCode.Unauthorized;
209+
210+ this.RequestAbort();
211+ this.streamReq = this.CreateRequest( method, requestUri, param, false );
212+ // User-Agent指定がある場合は付加
213+ if ( !string.IsNullOrEmpty( userAgent ) )
214+ this.streamReq.UserAgent = userAgent;
215+
216+ // OAuth認証ヘッダを付加
217+ this.AppendOAuthInfo( this.streamReq, param, token, tokenSecret );
218+
219+ try
220+ {
221+ HttpWebResponse webRes = (HttpWebResponse)this.streamReq.GetResponse();
222+ content = webRes.GetResponseStream();
223+ return webRes.StatusCode;
224+ }
225+ catch ( WebException ex )
226+ {
227+ if ( ex.Status == WebExceptionStatus.ProtocolError )
228+ {
229+ HttpWebResponse res = (HttpWebResponse)ex.Response;
230+ return res.StatusCode;
231+ }
232+ throw;
233+ }
234+ }
235+
236+ public void RequestAbort()
237+ {
238+ try
239+ {
240+ if ( this.streamReq != null )
241+ {
242+ this.streamReq.Abort();
243+ this.streamReq = null;
244+ }
245+ }
246+ catch ( Exception ) {}
247+ }
248+
249+ #region "認証処理"
250+ /// <summary>
251+ /// OAuth認証の開始要求(リクエストトークン取得)。PIN入力用の前段
252+ /// </summary>
253+ /// <remarks>
254+ /// 呼び出し元では戻されたurlをブラウザで開き、認証完了後PIN入力を受け付けて、リクエストトークンと共にAuthenticatePinFlowを呼び出す
255+ /// </remarks>
256+ /// <param name="requestTokenUrl">リクエストトークンの取得先URL</param>
257+ /// <param name="requestUri">ブラウザで開く認証用URLのベース</param>
258+ /// <param name="requestToken">[OUT]認証要求で戻されるリクエストトークン。使い捨て</param>
259+ /// <param name="authUri">[OUT]requestUriを元に生成された認証用URL。通常はリクエストトークンをクエリとして付加したUri</param>
260+ /// <returns>取得結果真偽値</returns>
261+ public bool AuthenticatePinFlowRequest( string requestTokenUrl, string authorizeUrl, ref string requestToken, ref Uri authUri )
262+ {
263+ // PIN-based flow
264+ authUri = this.GetAuthenticatePageUri( requestTokenUrl, authorizeUrl, ref requestToken );
265+ if ( authUri == null )
266+ return false;
267+ return true;
268+ }
269+
270+ /// <summary>
271+ /// OAuth認証のアクセストークン取得。PIN入力用の後段
272+ /// </summary>
273+ /// <remarks>
274+ /// 事前にAuthenticatePinFlowRequestを呼んで、ブラウザで認証後に表示されるPINを入力してもらい、その値とともに呼び出すこと
275+ /// </remarks>
276+ /// <param name="accessTokenUrl">アクセストークンの取得先URL</param>
277+ /// <param name="requestUri">AuthenticatePinFlowRequestで取得したリクエストトークン</param>
278+ /// <param name="pinCode">Webで認証後に表示されるPINコード</param>
279+ /// <returns>取得結果真偽値</returns>
280+ HttpStatusCode AuthenticatePinFlow( string accessTokenUrl, string requestToken, string pinCode )
281+ {
282+ // PIN-based flow
283+ if ( string.IsNullOrEmpty( requestToken ) )
284+ throw new Exception( "Sequence error.(requestToken is blank)" );
285+
286+ // アクセストークン取得
287+ string content = "";
288+ NameValueCollection accessTokenData;
289+ HttpStatusCode httpCode = this.GetOAuthToken( new Uri( accessTokenUrl ), pinCode, requestToken, null, ref content );
290+ if ( httpCode != HttpStatusCode.OK )
291+ return httpCode;
292+ accessTokenData = base.ParseQueryString( content );
293+
294+ if ( accessTokenData != null )
295+ {
296+ this.token = accessTokenData[ "oauth_token" ];
297+ this.tokenSecret = accessTokenData[ "oauth_token_secret" ];
298+
299+ // サービスごとの独自拡張対応
300+ if ( this.userIdentKey != "" )
301+ this.authorizedUsername = accessTokenData[ this.userIdentKey ];
302+ else
303+ this.authorizedUsername = "";
304+
305+ if ( this.userIdIdentKey != "" )
306+ {
307+ try
308+ {
309+ this.authorizedUserId = Convert.ToInt64( accessTokenData[ this.userIdIdentKey ] );
310+ }
311+ catch ( Exception )
312+ {
313+ this.authorizedUserId = 0;
314+ }
315+ }
316+ else
317+ {
318+ this.authorizedUserId = 0;
319+ }
320+
321+ if ( token == "" )
322+ throw new InvalidDataException( "Token is null." );
323+ return HttpStatusCode.OK;
324+ }
325+ else
326+ {
327+ throw new InvalidDataException( "Return value is null." );
328+ }
329+ }
330+
331+ /// <summary>
332+ /// OAuth認証のアクセストークン取得。xAuth方式
333+ /// </summary>
334+ /// <param name="accessTokenUrl">アクセストークンの取得先URL</param>
335+ /// <param name="username">認証用ユーザー名</param>
336+ /// <param name="password">認証用パスワード</param>
337+ /// <returns>取得結果真偽値</returns>
338+ public HttpStatusCode AuthenticateXAuth( Uri accessTokenUrl, string username, string password, ref string content )
339+ {
340+ // ユーザー・パスワードチェック
341+ if ( string.IsNullOrEmpty( username ) || string.IsNullOrEmpty( password ) )
342+ throw new Exception( "Sequence error.(username or password is blank)" );
343+
344+ // xAuthの拡張パラメータ設定
345+ Dictionary< string, string > parameter = new Dictionary< string, string >();
346+ parameter.Add( "x_auth_mode", "client_auth" );
347+ parameter.Add( "x_auth_username", username );
348+ parameter.Add( "x_auth_password", password );
349+
350+ // アクセストークン取得
351+ HttpStatusCode httpCode = this.GetOAuthToken( accessTokenUrl, "", "", parameter, ref content );
352+ if ( httpCode != HttpStatusCode.OK )
353+ return httpCode;
354+ NameValueCollection accessTokenData = base.ParseQueryString( content );
355+
356+ if ( accessTokenData != null )
357+ {
358+ this.token = accessTokenData[ "oauth_token" ];
359+ this.tokenSecret = accessTokenData[ "oauth_token_secret" ];
360+
361+ // サービスごとの独自拡張対応
362+ if ( this.userIdentKey != "" )
363+ this.authorizedUsername = accessTokenData[ this.userIdentKey ];
364+ else
365+ this.authorizedUsername = "";
366+
367+ if ( this.userIdIdentKey != "" )
368+ {
369+ try
370+ {
371+ this.authorizedUserId = Convert.ToInt64( accessTokenData[ this.userIdIdentKey ] );
372+ }
373+ catch ( Exception )
374+ {
375+ this.authorizedUserId = 0;
376+ }
377+ }
378+ else
379+ {
380+ this.authorizedUserId = 0;
381+ }
382+
383+ if ( token == "" )
384+ throw new InvalidDataException( "Token is null." );
385+ return HttpStatusCode.OK;
386+ }
387+ else
388+ {
389+ throw new InvalidDataException( "Return value is null." );
390+ }
391+ }
392+
393+ /// <summary>
394+ /// OAuth認証のリクエストトークン取得。リクエストトークンと組み合わせた認証用のUriも生成する
395+ /// </summary>
396+ /// <param name="accessTokenUrl">リクエストトークンの取得先URL</param>
397+ /// <param name="authorizeUrl">ブラウザで開く認証用URLのベース</param>
398+ /// <param name="requestToken">[OUT]取得したリクエストトークン</param>
399+ /// <returns>取得結果真偽値</returns>
400+ private Uri GetAuthenticatePageUri( string requestTokenUrl, string authorizeUrl, ref string requestToken )
401+ {
402+ const string tokenKey = "oauth_token";
403+
404+ // リクエストトークン取得
405+ string content = "";
406+ NameValueCollection reqTokenData;
407+ if ( this.GetOAuthToken( new Uri( requestTokenUrl ), "", "", null, ref content) != HttpStatusCode.OK )
408+ return null;
409+ reqTokenData = base.ParseQueryString( content );
410+
411+ if ( reqTokenData != null )
412+ {
413+ requestToken = reqTokenData[ tokenKey ];
414+ // Uri生成
415+ UriBuilder ub = new UriBuilder( authorizeUrl );
416+ ub.Query = string.Format( "{0}={1}", tokenKey, requestToken );
417+ return ub.Uri;
418+ }
419+ else
420+ {
421+ return null;
422+ }
423+ }
424+
425+ /// <summary>
426+ /// OAuth認証のトークン取得共通処理
427+ /// </summary>
428+ /// <param name="requestUri">各種トークンの取得先URL</param>
429+ /// <param name="pinCode">PINフロー時のアクセストークン取得時に設定。それ以外は空文字列</param>
430+ /// <param name="requestToken">PINフロー時のリクエストトークン取得時に設定。それ以外は空文字列</param>
431+ /// <param name="parameter">追加パラメータ。xAuthで使用</param>
432+ /// <returns>取得結果のデータ。正しく取得出来なかった場合はNothing</returns>
433+ private HttpStatusCode GetOAuthToken( Uri requestUri, string pinCode, string requestToken, Dictionary< string , string > parameter, ref string content )
434+ {
435+ HttpWebRequest webReq = null;
436+ // HTTPリクエスト生成。PINコードもパラメータも未指定の場合はGETメソッドで通信。それ以外はPOST
437+ if ( string.IsNullOrEmpty( pinCode ) && parameter != null )
438+ webReq = this.CreateRequest( "GET", requestUri, null, false );
439+ else
440+ webReq = this.CreateRequest( "POST", requestUri, parameter, false ); // ボディに追加パラメータ書き込み
441+
442+ // OAuth関連パラメータ準備。追加パラメータがあれば追加
443+ Dictionary< string, string > query = new Dictionary< string, string >();
444+ if ( parameter != null )
445+ foreach ( KeyValuePair< string, string > kvp in parameter )
446+ query.Add( kvp.Key, kvp.Value );
447+
448+ // PINコードが指定されていればパラメータに追加
449+ if ( string.IsNullOrEmpty( pinCode ) )
450+ query.Add( "oauth_verifier", pinCode );
451+ // OAuth関連情報をHTTPリクエストに追加
452+ this.AppendOAuthInfo( webReq, query, requestToken, "" );
453+
454+ // HTTP応答取得
455+ Dictionary< string, string > header = new Dictionary< string, string >() { { "Date", "" } };
456+ HttpStatusCode responseCode = this.GetResponse( webReq, ref content, header, false );
457+ if ( responseCode == HttpStatusCode.OK )
458+ return responseCode;
459+
460+ if ( !string.IsNullOrEmpty( header[ "Date" ] ) )
461+ content += Environment.NewLine + "Check the Date & Time of this computer." + Environment.NewLine
462+ + "Server:" + DateTime.Parse( header[ "Date" ] ).ToString() + " PC:" + DateTime.Now.ToString();
463+ return responseCode;
464+ }
465+ #endregion // 認証処理
466+
467+ #region "OAuth認証用ヘッダ作成・付加処理"
468+ /// <summary>
469+ /// HTTPリクエストにOAuth関連ヘッダを追加
470+ /// </summary>
471+ /// <param name="webRequest">追加対象のHTTPリクエスト</param>
472+ /// <param name="query">OAuth追加情報+クエリ or POSTデータ</param>
473+ /// <param name="token">アクセストークン、もしくはリクエストトークン。未取得なら空文字列</param>
474+ /// <param name="tokenSecret">アクセストークンシークレット。認証処理では空文字列</param>
475+ protected override void AppendOAuthInfo( HttpWebRequest webRequest, Dictionary< string, string > query, string token, string tokenSecret )
476+ {
477+ // OAuth共通情報取得
478+ Dictionary< string, string > parameter = this.GetOAuthParameter( token );
479+ // OAuth共通情報にquery情報を追加
480+ if ( query != null )
481+ foreach ( KeyValuePair< string, string > item in query )
482+ parameter.Add( item.Key, item.Value );
483+ // 署名の作成・追加
484+ parameter.Add( "oauth_signature", this.CreateSignature( tokenSecret, webRequest.Method, webRequest.RequestUri, parameter ) );
485+ // HTTPリクエストのヘッダに追加
486+ StringBuilder sb = new StringBuilder( "OAuth " );
487+ foreach ( KeyValuePair< string, string > item in parameter )
488+ // 各種情報のうち、oauth_で始まる情報のみ、ヘッダに追加する。各情報はカンマ区切り、データはダブルクォーテーションで括る
489+ if ( item.Key.StartsWith("oauth_") )
490+ sb.AppendFormat( "{0}=\"{1}\",", item.Key, this.UrlEncode( item.Value ) );
491+ webRequest.Headers.Add( HttpRequestHeader.Authorization, sb.ToString() );
492+ }
493+
494+ /// <summary>
495+ /// OAuthで使用する共通情報を取得する
496+ /// </summary>
497+ /// <param name="token">アクセストークン、もしくはリクエストトークン。未取得なら空文字列</param>
498+ /// <returns>OAuth情報のディクショナリ</returns>
499+ protected Dictionary< string, string > GetOAuthParameter( string token )
500+ {
501+ Dictionary< string, string > parameter = new Dictionary< string, string >();
502+ parameter.Add( "oauth_consumer_key", this.consumerKey );
503+ parameter.Add( "oauth_signature_method", "HMAC-SHA1" );
504+ parameter.Add( "oauth_timestamp", Convert.ToInt64( ( DateTime.UtcNow - HttpConnectionOAuth.UnixEpoch ).TotalSeconds ).ToString() ); // epoch秒
505+ parameter.Add( "oauth_nonce", HttpConnectionOAuth.NonceRandom.Next( 123400, 9999999 ).ToString() );
506+ parameter.Add( "oauth_version", "1.0" );
507+ if ( !string.IsNullOrEmpty( token ) )
508+ parameter.Add( "oauth_token", token ); // トークンがあれば追加
509+ return parameter;
510+ }
511+
512+ /// <summary>
513+ /// OAuth認証ヘッダの署名作成
514+ /// </summary>
515+ /// <param name="tokenSecret">アクセストークン秘密鍵</param>
516+ /// <param name="method">HTTPメソッド文字列</param>
517+ /// <param name="uri">アクセス先Uri</param>
518+ /// <param name="parameter">クエリ、もしくはPOSTデータ</param>
519+ /// <returns>署名文字列</returns>
520+ protected override string CreateSignature( string tokenSecret, string method, Uri uri, Dictionary< string, string > parameter )
521+ {
522+ // パラメタをソート済みディクショナリに詰替(OAuthの仕様)
523+ SortedDictionary< string, string > sorted = new SortedDictionary< string, string >( parameter );
524+ // URLエンコード済みのクエリ形式文字列に変換
525+ string paramString = this.CreateQueryString( sorted );
526+ // アクセス先URLの整形
527+ string url = string.Format( "{0}://{1}{2}", uri.Scheme, uri.Host, uri.AbsolutePath );
528+ // 署名のベース文字列生成(&区切り)。クエリ形式文字列は再エンコードする
529+ string signatureBase = string.Format( "{0}&{1}&{2}", method, this.UrlEncode( url ), this.UrlEncode( paramString ) );
530+ // 署名鍵の文字列をコンシューマー秘密鍵とアクセストークン秘密鍵から生成(&区切り。アクセストークン秘密鍵なくても&残すこと)
531+ string key = this.UrlEncode( this.consumerSecret ) + "&";
532+ if ( !string.IsNullOrEmpty( tokenSecret ) )
533+ key += this.UrlEncode( tokenSecret );
534+ // 鍵生成&署名生成
535+ using ( HMACSHA1 hmac = new HMACSHA1( Encoding.ASCII.GetBytes( key ) ) )
536+ {
537+ byte[] hash = hmac.ComputeHash( Encoding.ASCII.GetBytes( signatureBase ) );
538+ return Convert.ToBase64String( hash );
539+ }
540+ }
541+ #endregion // OAuth認証用ヘッダ作成・付加処理
542+
543+ /// <summary>
544+ /// 初期化。各種トークンの設定とユーザー識別情報設定
545+ /// </summary>
546+ /// <param name="consumerKey">コンシューマー鍵</param>
547+ /// <param name="consumerSecret">コンシューマー秘密鍵</param>
548+ /// <param name="accessToken">アクセストークン</param>
549+ /// <param name="accessTokenSecret">アクセストークン秘密鍵</param>
550+ /// <param name="userIdentifier">アクセストークン取得時に得られるユーザー識別情報。不要なら空文字列</param>
551+ public void Initialize( string consumerKey, string consumerSecret,
552+ string accessToken, string accessTokenSecret,
553+ string userIdentifier, string userIdIdentifier )
554+ {
555+ this.consumerKey = consumerKey;
556+ this.consumerSecret = consumerSecret;
557+ this.token = accessToken;
558+ this.tokenSecret = accessTokenSecret;
559+ this.userIdentKey = userIdentifier;
560+ this.userIdIdentKey = userIdIdentifier;
561+ }
562+
563+ /// <summary>
564+ /// 初期化。各種トークンの設定とユーザー識別情報設定
565+ /// </summary>
566+ /// <param name="consumerKey">コンシューマー鍵</param>
567+ /// <param name="consumerSecret">コンシューマー秘密鍵</param>
568+ /// <param name="accessToken">アクセストークン</param>
569+ /// <param name="accessTokenSecret">アクセストークン秘密鍵</param>
570+ /// <param name="username">認証済みユーザー名</param>
571+ /// <param name="userIdentifier">アクセストークン取得時に得られるユーザー識別情報。不要なら空文字列</param>
572+ public void Initialize( string consumerKey, string consumerSecret,
573+ string accessToken, string accessTokenSecret,
574+ string username, long userId,
575+ string userIdentifier, string userIdIdentifier )
576+ {
577+ this.Initialize( consumerKey, consumerSecret, accessToken, accessTokenSecret, userIdentifier, userIdIdentifier );
578+ this.authorizedUsername = username;
579+ this.authorizedUserId = userId;
580+ }
581+
582+ /// <summary>
583+ /// アクセストークン
584+ /// </summary>
585+ public string AccessToken
586+ {
587+ get { return this.token; }
588+ }
589+
590+ /// <summary>
591+ /// アクセストークン秘密鍵
592+ /// </summary>
593+ public string AccessTokenSecret
594+ {
595+ get { return this.tokenSecret; }
596+ }
597+
598+ /// <summary>
599+ /// 認証済みユーザー名
600+ /// </summary>
601+ public string AuthUsername
602+ {
603+ get { return this.authorizedUsername; }
604+ }
605+
606+ /// <summary>
607+ /// 認証済みユーザーId
608+ /// </summary>
609+ public long AuthUserId
610+ {
611+ get { return this.authorizedUserId; }
612+ set { this.authorizedUserId = value; }
613+ }
614+ }
615+}
--- /dev/null
+++ b/TweenCS/Connection/HttpConnectionOAuthEcho.cs
@@ -0,0 +1,82 @@
1+// OpenTween - Client of Twitter
2+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3+// (c) 2008-2011 Moz (@syo68k)
4+// (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5+// (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6+// (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7+// (c) 2011 spinor (@tplantd) <http://d.hatena.ne.jp/spinor/>
8+// All rights reserved.
9+//
10+// This file is part of OpenTween.
11+//
12+// This program is free software; you can redistribute it and/or modify it
13+// under the terms of the GNU General Public License as published by the Free
14+// Software Foundation; either version 3 of the License, or (at your option)
15+// any later version.
16+//
17+// This program is distributed in the hope that it will be useful, but
18+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20+// for more details.
21+//
22+// You should have received a copy of the GNU General Public License along
23+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
24+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
25+// Boston, MA 02110-1301, USA.
26+
27+using HttpConnectionOAuth = Tween.HttpConnectionOAuth;
28+using Uri = System.Uri;
29+using HttpWebRequest = System.Net.HttpWebRequest;
30+using System.Collections.Generic; // for Dictionary<TKey, TValue>, KeyValuePair<TKey, TValue>
31+using HttpConnection = Tween.HttpConnection;
32+using StringBuilder = System.Text.StringBuilder;
33+
34+namespace Tween
35+{
36+ public class HttpConnectionOAuthEcho : HttpConnectionOAuth
37+ {
38+ private Uri _realm;
39+
40+ private Uri _serviceProvider;
41+
42+ private string _token;
43+
44+ private string _tokenSecret;
45+
46+ public Uri Realm
47+ {
48+ set { this._realm = value; }
49+ }
50+
51+ public Uri ServiceProvider
52+ {
53+ set { this._serviceProvider = value; }
54+ }
55+
56+ protected override void AppendOAuthInfo( HttpWebRequest webRequest, Dictionary< string, string > query, string token, string tokenSecret )
57+ {
58+ // OAuth共通情報取得
59+ Dictionary< string, string > parameter = this.GetOAuthParameter( token );
60+ // OAuth共通情報にquery情報を追加
61+ if ( query != null )
62+ foreach ( KeyValuePair< string, string > item in query )
63+ parameter.Add( item.Key, item.Value );
64+ // 署名の作成・追加(GETメソッド固定。ServiceProvider呼び出し用の署名作成)
65+ parameter.Add( "oauth_signature", this.CreateSignature( tokenSecret, HttpConnection.GetMethod, this._serviceProvider, parameter ) );
66+ // HTTPリクエストのヘッダに追加
67+ StringBuilder sb = new StringBuilder( "OAuth " );
68+ sb.AppendFormat( "realm=\"{0}://{1}{2}\",", this._realm.Scheme, this._realm.Host, this._realm.AbsolutePath);
69+ foreach ( KeyValuePair< string, string > item in parameter )
70+ if ( item.Key.StartsWith( "oauth_" ) )
71+ sb.AppendFormat( "{0}=\"{1}\",", item.Key, this.UrlEncode( item.Value ) );
72+ webRequest.Headers.Add( "X-Verify-Credentials-Authorization", sb.ToString() );
73+ webRequest.Headers.Add( "X-Auth-Service-Provider", string.Format("{0}://{1}{2}", this._serviceProvider.Scheme, this._serviceProvider.Host, this._serviceProvider.AbsolutePath));
74+ }
75+
76+ public HttpConnectionOAuthEcho( Uri realm, Uri serviceProvider )
77+ {
78+ this._realm = realm;
79+ this._serviceProvider = serviceProvider;
80+ }
81+ }
82+}
--- a/TweenCS/TweenCS.csproj
+++ b/TweenCS/TweenCS.csproj
@@ -51,6 +51,8 @@
5151 <Compile Include="ApiInformation.cs" />
5252 <Compile Include="Bing.cs" />
5353 <Compile Include="Connection\HttpVarious.cs" />
54+ <Compile Include="Connection\HttpConnectionOAuth.cs" />
55+ <Compile Include="Connection\HttpConnectionOAuthEcho.cs" />
5456 <Compile Include="Connection\IHttpConnection.cs" />
5557 <Compile Include="Connection\IMultimediaShareService.cs" />
5658 <Compile Include="DataModel.cs" />