開発に使用するリポジトリ
Revision | 1d8a7c757656863a48bfdaf7b0be6c84568f8d6f (tree) |
---|---|
Time | 2022-01-22 23:34:20 |
Author | upsilon <kim.upsilon@bucy...> |
Commiter | GitHub |
Merge pull request #89 from opentween/encrypt-api-keys
アプリケーションに埋め込むAPIキーの暗号化に対応
@@ -38,7 +38,7 @@ namespace OpenTween.Api | ||
38 | 38 | { |
39 | 39 | using var mockHandler = new HttpMessageHandlerMock(); |
40 | 40 | using var http = new HttpClient(mockHandler); |
41 | - var bitly = new BitlyApi(http); | |
41 | + var bitly = new BitlyApi(ApiKey.Create("fake_client_id"), ApiKey.Create("fake_client_secret"), http); | |
42 | 42 | |
43 | 43 | mockHandler.Enqueue(x => |
44 | 44 | { |
@@ -72,7 +72,7 @@ namespace OpenTween.Api | ||
72 | 72 | { |
73 | 73 | using var mockHandler = new HttpMessageHandlerMock(); |
74 | 74 | using var http = new HttpClient(mockHandler); |
75 | - var bitly = new BitlyApi(http); | |
75 | + var bitly = new BitlyApi(ApiKey.Create("fake_client_id"), ApiKey.Create("fake_client_secret"), http); | |
76 | 76 | |
77 | 77 | mockHandler.Enqueue(x => |
78 | 78 | { |
@@ -108,7 +108,7 @@ namespace OpenTween.Api | ||
108 | 108 | { |
109 | 109 | using var mockHandler = new HttpMessageHandlerMock(); |
110 | 110 | using var http = new HttpClient(mockHandler); |
111 | - var bitly = new BitlyApi(http); | |
111 | + var bitly = new BitlyApi(ApiKey.Create("fake_client_id"), ApiKey.Create("fake_client_secret"), http); | |
112 | 112 | |
113 | 113 | mockHandler.Enqueue(async x => |
114 | 114 | { |
@@ -117,8 +117,10 @@ namespace OpenTween.Api | ||
117 | 117 | x.RequestUri.GetLeftPart(UriPartial.Path)); |
118 | 118 | |
119 | 119 | Assert.Equal("Basic", x.Headers.Authorization.Scheme); |
120 | - Assert.Equal(ApplicationSettings.BitlyClientId + ":" + ApplicationSettings.BitlyClientSecret, | |
121 | - Encoding.UTF8.GetString(Convert.FromBase64String(x.Headers.Authorization.Parameter))); | |
120 | + Assert.Equal( | |
121 | + Convert.ToBase64String(Encoding.UTF8.GetBytes("fake_client_id:fake_client_secret")), | |
122 | + x.Headers.Authorization.Parameter | |
123 | + ); | |
122 | 124 | |
123 | 125 | var body = await x.Content.ReadAsStringAsync() |
124 | 126 | .ConfigureAwait(false); |
@@ -146,7 +148,7 @@ namespace OpenTween.Api | ||
146 | 148 | { |
147 | 149 | using var mockHandler = new HttpMessageHandlerMock(); |
148 | 150 | using var http = new HttpClient(mockHandler); |
149 | - var bitly = new BitlyApi(http); | |
151 | + var bitly = new BitlyApi(ApiKey.Create("fake_client_id"), ApiKey.Create("fake_client_secret"), http); | |
150 | 152 | |
151 | 153 | mockHandler.Enqueue(x => |
152 | 154 | { |
@@ -161,5 +163,17 @@ namespace OpenTween.Api | ||
161 | 163 | |
162 | 164 | Assert.Equal(0, mockHandler.QueueCount); |
163 | 165 | } |
166 | + | |
167 | + [Fact] | |
168 | + public async Task GetAccessTokenAsync_ApiKeyErrorTest() | |
169 | + { | |
170 | + using var mockHandler = new HttpMessageHandlerMock(); | |
171 | + using var http = new HttpClient(mockHandler); | |
172 | + var bitly = new BitlyApi(ApiKey.Create("%e%INVALID_API_KEY"), ApiKey.Create("%e%INVALID_API_KEY"), http); | |
173 | + | |
174 | + await Assert.ThrowsAsync<WebApiException>( | |
175 | + () => bitly.GetAccessTokenAsync("hogehoge", "tetete") | |
176 | + ); | |
177 | + } | |
164 | 178 | } |
165 | 179 | } |
@@ -82,7 +82,7 @@ namespace OpenTween.Api | ||
82 | 82 | }; |
83 | 83 | }); |
84 | 84 | |
85 | - var imgurApi = new ImgurApi("fake_api_key", http); | |
85 | + var imgurApi = new ImgurApi(ApiKey.Create("fake_api_key"), http); | |
86 | 86 | using var mediaItem = TestUtils.CreateDummyMediaItem(); |
87 | 87 | var uploadedUrl = await imgurApi.UploadFileAsync(mediaItem, "てすと") |
88 | 88 | .ConfigureAwait(false); |
@@ -113,7 +113,7 @@ namespace OpenTween.Api | ||
113 | 113 | }; |
114 | 114 | }); |
115 | 115 | |
116 | - var imgurApi = new ImgurApi("fake_api_key", http); | |
116 | + var imgurApi = new ImgurApi(ApiKey.Create("fake_api_key"), http); | |
117 | 117 | using var mediaItem = TestUtils.CreateDummyMediaItem(); |
118 | 118 | await Assert.ThrowsAsync<WebApiException>( |
119 | 119 | () => imgurApi.UploadFileAsync(mediaItem, "てすと") |
@@ -139,7 +139,22 @@ namespace OpenTween.Api | ||
139 | 139 | }; |
140 | 140 | }); |
141 | 141 | |
142 | - var imgurApi = new ImgurApi("fake_api_key", http); | |
142 | + var imgurApi = new ImgurApi(ApiKey.Create("fake_api_key"), http); | |
143 | + using var mediaItem = TestUtils.CreateDummyMediaItem(); | |
144 | + await Assert.ThrowsAsync<WebApiException>( | |
145 | + () => imgurApi.UploadFileAsync(mediaItem, "てすと") | |
146 | + ); | |
147 | + | |
148 | + Assert.Equal(0, mockHandler.QueueCount); | |
149 | + } | |
150 | + | |
151 | + [Fact] | |
152 | + public async Task UploadFileAsync_ApiKeyErrorTest() | |
153 | + { | |
154 | + using var mockHandler = new HttpMessageHandlerMock(); | |
155 | + using var http = new HttpClient(mockHandler); | |
156 | + | |
157 | + var imgurApi = new ImgurApi(ApiKey.Create("%e%INVALID_API_KEY"), http); | |
143 | 158 | using var mediaItem = TestUtils.CreateDummyMediaItem(); |
144 | 159 | await Assert.ThrowsAsync<WebApiException>( |
145 | 160 | () => imgurApi.UploadFileAsync(mediaItem, "てすと") |
@@ -42,7 +42,7 @@ namespace OpenTween.Api | ||
42 | 42 | using var mockHandler = new HttpMessageHandlerMock(); |
43 | 43 | using var http = new HttpClient(mockHandler); |
44 | 44 | |
45 | - var mock = new Mock<MicrosoftTranslatorApi>(http); | |
45 | + var mock = new Mock<MicrosoftTranslatorApi>(ApiKey.Create("fake_api_key"), http); | |
46 | 46 | mock.Setup(x => x.GetAccessTokenAsync()) |
47 | 47 | .ReturnsAsync(("1234abcd", TimeSpan.FromSeconds(1000))); |
48 | 48 |
@@ -95,9 +95,26 @@ namespace OpenTween.Api | ||
95 | 95 | } |
96 | 96 | |
97 | 97 | [Fact] |
98 | + public async Task TranslateAsync_ApiKeyErrorTest() | |
99 | + { | |
100 | + using var mockHandler = new HttpMessageHandlerMock(); | |
101 | + using var http = new HttpClient(mockHandler); | |
102 | + | |
103 | + var mock = new Mock<MicrosoftTranslatorApi>(ApiKey.Create("%e%INVALID_API_KEY"), http); | |
104 | + var translateApi = mock.Object; | |
105 | + | |
106 | + await Assert.ThrowsAsync<WebApiException>( | |
107 | + () => translateApi.TranslateAsync("hogehoge", langTo: "ja", langFrom: "en") | |
108 | + ); | |
109 | + | |
110 | + mock.Verify(x => x.GetAccessTokenAsync(), Times.Never()); | |
111 | + Assert.Equal(0, mockHandler.QueueCount); | |
112 | + } | |
113 | + | |
114 | + [Fact] | |
98 | 115 | public async Task UpdateAccessTokenIfExpired_FirstCallTest() |
99 | 116 | { |
100 | - var mock = new Mock<MicrosoftTranslatorApi>(); | |
117 | + var mock = new Mock<MicrosoftTranslatorApi>(ApiKey.Create("fake_api_key"), null); | |
101 | 118 | mock.Setup(x => x.GetAccessTokenAsync()) |
102 | 119 | .ReturnsAsync(("1234abcd", TimeSpan.FromSeconds(1000))); |
103 | 120 |
@@ -116,7 +133,7 @@ namespace OpenTween.Api | ||
116 | 133 | [Fact] |
117 | 134 | public async Task UpdateAccessTokenIfExpired_NotExpiredTest() |
118 | 135 | { |
119 | - var mock = new Mock<MicrosoftTranslatorApi>(); | |
136 | + var mock = new Mock<MicrosoftTranslatorApi>(ApiKey.Create("fake_api_key"), null); | |
120 | 137 | |
121 | 138 | var translateApi = mock.Object; |
122 | 139 | translateApi.AccessToken = "1234abcd"; |
@@ -132,7 +149,7 @@ namespace OpenTween.Api | ||
132 | 149 | [Fact] |
133 | 150 | public async Task UpdateAccessTokenIfExpired_ExpiredTest() |
134 | 151 | { |
135 | - var mock = new Mock<MicrosoftTranslatorApi>(); | |
152 | + var mock = new Mock<MicrosoftTranslatorApi>(ApiKey.Create("fake_api_key"), null); | |
136 | 153 | mock.Setup(x => x.GetAccessTokenAsync()) |
137 | 154 | .ReturnsAsync(("5678efgh", TimeSpan.FromSeconds(1000))); |
138 | 155 |
@@ -155,7 +172,7 @@ namespace OpenTween.Api | ||
155 | 172 | { |
156 | 173 | using var mockHandler = new HttpMessageHandlerMock(); |
157 | 174 | using var http = new HttpClient(mockHandler); |
158 | - var translateApi = new MicrosoftTranslatorApi(http); | |
175 | + var translateApi = new MicrosoftTranslatorApi(ApiKey.Create("fake_api_key"), http); | |
159 | 176 | |
160 | 177 | mockHandler.Enqueue(x => |
161 | 178 | { |
@@ -163,7 +180,7 @@ namespace OpenTween.Api | ||
163 | 180 | Assert.Equal(MicrosoftTranslatorApi.IssueTokenEndpoint, x.RequestUri); |
164 | 181 | |
165 | 182 | var keyHeader = x.Headers.First(y => y.Key == "Ocp-Apim-Subscription-Key"); |
166 | - Assert.Equal(ApplicationSettings.TranslatorSubscriptionKey, keyHeader.Value.Single()); | |
183 | + Assert.Equal("fake_api_key", keyHeader.Value.Single()); | |
167 | 184 | |
168 | 185 | return new HttpResponseMessage(HttpStatusCode.OK) |
169 | 186 | { |
@@ -30,7 +30,7 @@ using Xunit; | ||
30 | 30 | |
31 | 31 | namespace OpenTween.Api |
32 | 32 | { |
33 | - public class MobypictureApiTest | |
33 | + public class MobypictureApiText | |
34 | 34 | { |
35 | 35 | [Fact] |
36 | 36 | public async Task UploadFileAsync_Test() |
@@ -54,7 +54,7 @@ namespace OpenTween.Api | ||
54 | 54 | }; |
55 | 55 | }); |
56 | 56 | |
57 | - var mobypictureApi = new MobypictureApi("fake_api_key", http); | |
57 | + var mobypictureApi = new MobypictureApi(ApiKey.Create("fake_api_key"), http); | |
58 | 58 | using var mediaItem = TestUtils.CreateDummyMediaItem(); |
59 | 59 | var uploadedUrl = await mobypictureApi.UploadFileAsync(mediaItem, "てすと") |
60 | 60 | .ConfigureAwait(false); |
@@ -80,7 +80,22 @@ namespace OpenTween.Api | ||
80 | 80 | }; |
81 | 81 | }); |
82 | 82 | |
83 | - var mobypictureApi = new MobypictureApi("fake_api_key", http); | |
83 | + var mobypictureApi = new MobypictureApi(ApiKey.Create("fake_api_key"), http); | |
84 | + using var mediaItem = TestUtils.CreateDummyMediaItem(); | |
85 | + await Assert.ThrowsAsync<WebApiException>( | |
86 | + () => mobypictureApi.UploadFileAsync(mediaItem, "てすと") | |
87 | + ); | |
88 | + | |
89 | + Assert.Equal(0, mockHandler.QueueCount); | |
90 | + } | |
91 | + | |
92 | + [Fact] | |
93 | + public async Task UploadFileAsync_ApiKeyErrorTest() | |
94 | + { | |
95 | + using var mockHandler = new HttpMessageHandlerMock(); | |
96 | + using var http = new HttpClient(mockHandler); | |
97 | + | |
98 | + var mobypictureApi = new MobypictureApi(ApiKey.Create("%e%INVALID_API_KEY"), http); | |
84 | 99 | using var mediaItem = TestUtils.CreateDummyMediaItem(); |
85 | 100 | await Assert.ThrowsAsync<WebApiException>( |
86 | 101 | () => mobypictureApi.UploadFileAsync(mediaItem, "てすと") |
@@ -51,7 +51,7 @@ namespace OpenTween.Api | ||
51 | 51 | [Fact] |
52 | 52 | public void Initialize_Test() |
53 | 53 | { |
54 | - using var twitterApi = new TwitterApi(); | |
54 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
55 | 55 | Assert.Null(twitterApi.apiConnection); |
56 | 56 | |
57 | 57 | twitterApi.Initialize("*** AccessToken ***", "*** AccessSecret ***", userId: 100L, screenName: "hogehoge"); |
@@ -100,7 +100,7 @@ namespace OpenTween.Api | ||
100 | 100 | ) |
101 | 101 | .ReturnsAsync(Array.Empty<TwitterStatus>()); |
102 | 102 | |
103 | - using var twitterApi = new TwitterApi(); | |
103 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
104 | 104 | twitterApi.apiConnection = mock.Object; |
105 | 105 | |
106 | 106 | await twitterApi.StatusesHomeTimeline(200, maxId: 900L, sinceId: 100L) |
@@ -128,7 +128,7 @@ namespace OpenTween.Api | ||
128 | 128 | ) |
129 | 129 | .ReturnsAsync(Array.Empty<TwitterStatus>()); |
130 | 130 | |
131 | - using var twitterApi = new TwitterApi(); | |
131 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
132 | 132 | twitterApi.apiConnection = mock.Object; |
133 | 133 | |
134 | 134 | await twitterApi.StatusesMentionsTimeline(200, maxId: 900L, sinceId: 100L) |
@@ -158,7 +158,7 @@ namespace OpenTween.Api | ||
158 | 158 | ) |
159 | 159 | .ReturnsAsync(Array.Empty<TwitterStatus>()); |
160 | 160 | |
161 | - using var twitterApi = new TwitterApi(); | |
161 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
162 | 162 | twitterApi.apiConnection = mock.Object; |
163 | 163 | |
164 | 164 | await twitterApi.StatusesUserTimeline("twitterapi", count: 200, maxId: 900L, sinceId: 100L) |
@@ -184,7 +184,7 @@ namespace OpenTween.Api | ||
184 | 184 | ) |
185 | 185 | .ReturnsAsync(new TwitterStatus { Id = 100L }); |
186 | 186 | |
187 | - using var twitterApi = new TwitterApi(); | |
187 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
188 | 188 | twitterApi.apiConnection = mock.Object; |
189 | 189 | |
190 | 190 | await twitterApi.StatusesShow(statusId: 100L) |
@@ -214,7 +214,7 @@ namespace OpenTween.Api | ||
214 | 214 | ) |
215 | 215 | .ReturnsAsync(LazyJson.Create(new TwitterStatus())); |
216 | 216 | |
217 | - using var twitterApi = new TwitterApi(); | |
217 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
218 | 218 | twitterApi.apiConnection = mock.Object; |
219 | 219 | |
220 | 220 | await twitterApi.StatusesUpdate("hogehoge", replyToId: 100L, mediaIds: new[] { 10L, 20L }, |
@@ -243,7 +243,7 @@ namespace OpenTween.Api | ||
243 | 243 | ) |
244 | 244 | .ReturnsAsync(LazyJson.Create(new TwitterStatus())); |
245 | 245 | |
246 | - using var twitterApi = new TwitterApi(); | |
246 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
247 | 247 | twitterApi.apiConnection = mock.Object; |
248 | 248 | |
249 | 249 | await twitterApi.StatusesUpdate("hogehoge", replyToId: null, mediaIds: null, excludeReplyUserIds: Array.Empty<long>()) |
@@ -264,7 +264,7 @@ namespace OpenTween.Api | ||
264 | 264 | ) |
265 | 265 | .ReturnsAsync(LazyJson.Create(new TwitterStatus { Id = 100L })); |
266 | 266 | |
267 | - using var twitterApi = new TwitterApi(); | |
267 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
268 | 268 | twitterApi.apiConnection = mock.Object; |
269 | 269 | |
270 | 270 | await twitterApi.StatusesDestroy(statusId: 100L) |
@@ -290,7 +290,7 @@ namespace OpenTween.Api | ||
290 | 290 | ) |
291 | 291 | .ReturnsAsync(LazyJson.Create(new TwitterStatus())); |
292 | 292 | |
293 | - using var twitterApi = new TwitterApi(); | |
293 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
294 | 294 | twitterApi.apiConnection = mock.Object; |
295 | 295 | |
296 | 296 | await twitterApi.StatusesRetweet(100L) |
@@ -322,7 +322,7 @@ namespace OpenTween.Api | ||
322 | 322 | ) |
323 | 323 | .ReturnsAsync(new TwitterSearchResult()); |
324 | 324 | |
325 | - using var twitterApi = new TwitterApi(); | |
325 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
326 | 326 | twitterApi.apiConnection = mock.Object; |
327 | 327 | |
328 | 328 | await twitterApi.SearchTweets("from:twitterapi", "en", count: 200, maxId: 900L, sinceId: 100L) |
@@ -347,7 +347,7 @@ namespace OpenTween.Api | ||
347 | 347 | ) |
348 | 348 | .ReturnsAsync(new TwitterLists()); |
349 | 349 | |
350 | - using var twitterApi = new TwitterApi(); | |
350 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
351 | 351 | twitterApi.apiConnection = mock.Object; |
352 | 352 | |
353 | 353 | await twitterApi.ListsOwnerships("twitterapi", cursor: -1L, count: 100) |
@@ -372,7 +372,7 @@ namespace OpenTween.Api | ||
372 | 372 | ) |
373 | 373 | .ReturnsAsync(new TwitterLists()); |
374 | 374 | |
375 | - using var twitterApi = new TwitterApi(); | |
375 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
376 | 376 | twitterApi.apiConnection = mock.Object; |
377 | 377 | |
378 | 378 | await twitterApi.ListsSubscriptions("twitterapi", cursor: -1L, count: 100) |
@@ -398,7 +398,7 @@ namespace OpenTween.Api | ||
398 | 398 | ) |
399 | 399 | .ReturnsAsync(new TwitterLists()); |
400 | 400 | |
401 | - using var twitterApi = new TwitterApi(); | |
401 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
402 | 402 | twitterApi.apiConnection = mock.Object; |
403 | 403 | |
404 | 404 | await twitterApi.ListsMemberships("twitterapi", cursor: -1L, count: 100, filterToOwnedLists: true) |
@@ -422,7 +422,7 @@ namespace OpenTween.Api | ||
422 | 422 | ) |
423 | 423 | .ReturnsAsync(LazyJson.Create(new TwitterList())); |
424 | 424 | |
425 | - using var twitterApi = new TwitterApi(); | |
425 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
426 | 426 | twitterApi.apiConnection = mock.Object; |
427 | 427 | |
428 | 428 | await twitterApi.ListsCreate("hogehoge", description: "aaaa", @private: true) |
@@ -448,7 +448,7 @@ namespace OpenTween.Api | ||
448 | 448 | ) |
449 | 449 | .ReturnsAsync(LazyJson.Create(new TwitterList())); |
450 | 450 | |
451 | - using var twitterApi = new TwitterApi(); | |
451 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
452 | 452 | twitterApi.apiConnection = mock.Object; |
453 | 453 | |
454 | 454 | await twitterApi.ListsUpdate(12345L, name: "hogehoge", description: "aaaa", @private: true) |
@@ -471,7 +471,7 @@ namespace OpenTween.Api | ||
471 | 471 | ) |
472 | 472 | .ReturnsAsync(LazyJson.Create(new TwitterList())); |
473 | 473 | |
474 | - using var twitterApi = new TwitterApi(); | |
474 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
475 | 475 | twitterApi.apiConnection = mock.Object; |
476 | 476 | |
477 | 477 | await twitterApi.ListsDestroy(12345L) |
@@ -502,7 +502,7 @@ namespace OpenTween.Api | ||
502 | 502 | ) |
503 | 503 | .ReturnsAsync(Array.Empty<TwitterStatus>()); |
504 | 504 | |
505 | - using var twitterApi = new TwitterApi(); | |
505 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
506 | 506 | twitterApi.apiConnection = mock.Object; |
507 | 507 | |
508 | 508 | await twitterApi.ListsStatuses(12345L, count: 200, maxId: 900L, sinceId: 100L, includeRTs: true) |
@@ -529,7 +529,7 @@ namespace OpenTween.Api | ||
529 | 529 | ) |
530 | 530 | .ReturnsAsync(new TwitterUsers()); |
531 | 531 | |
532 | - using var twitterApi = new TwitterApi(); | |
532 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
533 | 533 | twitterApi.apiConnection = mock.Object; |
534 | 534 | |
535 | 535 | await twitterApi.ListsMembers(12345L, cursor: -1) |
@@ -556,7 +556,7 @@ namespace OpenTween.Api | ||
556 | 556 | ) |
557 | 557 | .ReturnsAsync(new TwitterUser()); |
558 | 558 | |
559 | - using var twitterApi = new TwitterApi(); | |
559 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
560 | 560 | twitterApi.apiConnection = mock.Object; |
561 | 561 | |
562 | 562 | await twitterApi.ListsMembersShow(12345L, "twitterapi") |
@@ -582,7 +582,7 @@ namespace OpenTween.Api | ||
582 | 582 | ) |
583 | 583 | .ReturnsAsync(LazyJson.Create(new TwitterUser())); |
584 | 584 | |
585 | - using var twitterApi = new TwitterApi(); | |
585 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
586 | 586 | twitterApi.apiConnection = mock.Object; |
587 | 587 | |
588 | 588 | await twitterApi.ListsMembersCreate(12345L, "twitterapi") |
@@ -609,7 +609,7 @@ namespace OpenTween.Api | ||
609 | 609 | ) |
610 | 610 | .ReturnsAsync(LazyJson.Create(new TwitterUser())); |
611 | 611 | |
612 | - using var twitterApi = new TwitterApi(); | |
612 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
613 | 613 | twitterApi.apiConnection = mock.Object; |
614 | 614 | |
615 | 615 | await twitterApi.ListsMembersDestroy(12345L, "twitterapi") |
@@ -634,7 +634,7 @@ namespace OpenTween.Api | ||
634 | 634 | ) |
635 | 635 | .ReturnsAsync(new TwitterMessageEventList()); |
636 | 636 | |
637 | - using var twitterApi = new TwitterApi(); | |
637 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
638 | 638 | twitterApi.apiConnection = mock.Object; |
639 | 639 | |
640 | 640 | await twitterApi.DirectMessagesEventsList(count: 50, cursor: "12345abcdefg") |
@@ -672,7 +672,7 @@ namespace OpenTween.Api | ||
672 | 672 | ) |
673 | 673 | .ReturnsAsync(LazyJson.Create(new TwitterMessageEventSingle())); |
674 | 674 | |
675 | - using var twitterApi = new TwitterApi(); | |
675 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
676 | 676 | twitterApi.apiConnection = mock.Object; |
677 | 677 | |
678 | 678 | await twitterApi.DirectMessagesEventsNew(recipientId: 12345L, text: "hogehoge", mediaId: 67890L) |
@@ -691,7 +691,7 @@ namespace OpenTween.Api | ||
691 | 691 | ) |
692 | 692 | .Returns(Task.CompletedTask); |
693 | 693 | |
694 | - using var twitterApi = new TwitterApi(); | |
694 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
695 | 695 | twitterApi.apiConnection = mock.Object; |
696 | 696 | |
697 | 697 | await twitterApi.DirectMessagesEventsDestroy(eventId: "100") |
@@ -717,7 +717,7 @@ namespace OpenTween.Api | ||
717 | 717 | ) |
718 | 718 | .ReturnsAsync(new TwitterUser { ScreenName = "twitterapi" }); |
719 | 719 | |
720 | - using var twitterApi = new TwitterApi(); | |
720 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
721 | 721 | twitterApi.apiConnection = mock.Object; |
722 | 722 | |
723 | 723 | await twitterApi.UsersShow(screenName: "twitterapi") |
@@ -743,7 +743,7 @@ namespace OpenTween.Api | ||
743 | 743 | ) |
744 | 744 | .ReturnsAsync(Array.Empty<TwitterUser>()); |
745 | 745 | |
746 | - using var twitterApi = new TwitterApi(); | |
746 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
747 | 747 | twitterApi.apiConnection = mock.Object; |
748 | 748 | |
749 | 749 | await twitterApi.UsersLookup(userIds: new[] { "11111", "22222" }) |
@@ -766,7 +766,7 @@ namespace OpenTween.Api | ||
766 | 766 | ) |
767 | 767 | .ReturnsAsync(LazyJson.Create(new TwitterUser { ScreenName = "twitterapi" })); |
768 | 768 | |
769 | - using var twitterApi = new TwitterApi(); | |
769 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
770 | 770 | twitterApi.apiConnection = mock.Object; |
771 | 771 | |
772 | 772 | await twitterApi.UsersReportSpam(screenName: "twitterapi") |
@@ -795,7 +795,7 @@ namespace OpenTween.Api | ||
795 | 795 | ) |
796 | 796 | .ReturnsAsync(Array.Empty<TwitterStatus>()); |
797 | 797 | |
798 | - using var twitterApi = new TwitterApi(); | |
798 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
799 | 799 | twitterApi.apiConnection = mock.Object; |
800 | 800 | |
801 | 801 | await twitterApi.FavoritesList(200, maxId: 900L, sinceId: 100L) |
@@ -818,7 +818,7 @@ namespace OpenTween.Api | ||
818 | 818 | ) |
819 | 819 | .ReturnsAsync(LazyJson.Create(new TwitterStatus { Id = 100L })); |
820 | 820 | |
821 | - using var twitterApi = new TwitterApi(); | |
821 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
822 | 822 | twitterApi.apiConnection = mock.Object; |
823 | 823 | |
824 | 824 | await twitterApi.FavoritesCreate(statusId: 100L) |
@@ -842,7 +842,7 @@ namespace OpenTween.Api | ||
842 | 842 | ) |
843 | 843 | .ReturnsAsync(LazyJson.Create(new TwitterStatus { Id = 100L })); |
844 | 844 | |
845 | - using var twitterApi = new TwitterApi(); | |
845 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
846 | 846 | twitterApi.apiConnection = mock.Object; |
847 | 847 | |
848 | 848 | await twitterApi.FavoritesDestroy(statusId: 100L) |
@@ -864,7 +864,7 @@ namespace OpenTween.Api | ||
864 | 864 | ) |
865 | 865 | .ReturnsAsync(new TwitterFriendship()); |
866 | 866 | |
867 | - using var twitterApi = new TwitterApi(); | |
867 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
868 | 868 | twitterApi.apiConnection = mock.Object; |
869 | 869 | |
870 | 870 | await twitterApi.FriendshipsShow(sourceScreenName: "twitter", targetScreenName: "twitterapi") |
@@ -884,7 +884,7 @@ namespace OpenTween.Api | ||
884 | 884 | ) |
885 | 885 | .ReturnsAsync(LazyJson.Create(new TwitterFriendship())); |
886 | 886 | |
887 | - using var twitterApi = new TwitterApi(); | |
887 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
888 | 888 | twitterApi.apiConnection = mock.Object; |
889 | 889 | |
890 | 890 | await twitterApi.FriendshipsCreate(screenName: "twitterapi") |
@@ -905,7 +905,7 @@ namespace OpenTween.Api | ||
905 | 905 | ) |
906 | 906 | .ReturnsAsync(LazyJson.Create(new TwitterFriendship())); |
907 | 907 | |
908 | - using var twitterApi = new TwitterApi(); | |
908 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
909 | 909 | twitterApi.apiConnection = mock.Object; |
910 | 910 | |
911 | 911 | await twitterApi.FriendshipsDestroy(screenName: "twitterapi") |
@@ -927,7 +927,7 @@ namespace OpenTween.Api | ||
927 | 927 | ) |
928 | 928 | .ReturnsAsync(Array.Empty<long>()); |
929 | 929 | |
930 | - using var twitterApi = new TwitterApi(); | |
930 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
931 | 931 | twitterApi.apiConnection = mock.Object; |
932 | 932 | |
933 | 933 | await twitterApi.NoRetweetIds() |
@@ -948,7 +948,7 @@ namespace OpenTween.Api | ||
948 | 948 | ) |
949 | 949 | .ReturnsAsync(new TwitterIds()); |
950 | 950 | |
951 | - using var twitterApi = new TwitterApi(); | |
951 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
952 | 952 | twitterApi.apiConnection = mock.Object; |
953 | 953 | |
954 | 954 | await twitterApi.FollowersIds(cursor: -1L) |
@@ -969,7 +969,7 @@ namespace OpenTween.Api | ||
969 | 969 | ) |
970 | 970 | .ReturnsAsync(new TwitterIds()); |
971 | 971 | |
972 | - using var twitterApi = new TwitterApi(); | |
972 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
973 | 973 | twitterApi.apiConnection = mock.Object; |
974 | 974 | |
975 | 975 | await twitterApi.MutesUsersIds(cursor: -1L) |
@@ -990,7 +990,7 @@ namespace OpenTween.Api | ||
990 | 990 | ) |
991 | 991 | .ReturnsAsync(new TwitterIds()); |
992 | 992 | |
993 | - using var twitterApi = new TwitterApi(); | |
993 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
994 | 994 | twitterApi.apiConnection = mock.Object; |
995 | 995 | |
996 | 996 | await twitterApi.BlocksIds(cursor: -1L) |
@@ -1013,7 +1013,7 @@ namespace OpenTween.Api | ||
1013 | 1013 | ) |
1014 | 1014 | .ReturnsAsync(LazyJson.Create(new TwitterUser())); |
1015 | 1015 | |
1016 | - using var twitterApi = new TwitterApi(); | |
1016 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
1017 | 1017 | twitterApi.apiConnection = mock.Object; |
1018 | 1018 | |
1019 | 1019 | await twitterApi.BlocksCreate(screenName: "twitterapi") |
@@ -1037,7 +1037,7 @@ namespace OpenTween.Api | ||
1037 | 1037 | ) |
1038 | 1038 | .ReturnsAsync(LazyJson.Create(new TwitterUser())); |
1039 | 1039 | |
1040 | - using var twitterApi = new TwitterApi(); | |
1040 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
1041 | 1041 | twitterApi.apiConnection = mock.Object; |
1042 | 1042 | |
1043 | 1043 | await twitterApi.BlocksDestroy(screenName: "twitterapi") |
@@ -1067,7 +1067,7 @@ namespace OpenTween.Api | ||
1067 | 1067 | ScreenName = "opentween", |
1068 | 1068 | }); |
1069 | 1069 | |
1070 | - using var twitterApi = new TwitterApi(); | |
1070 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
1071 | 1071 | twitterApi.apiConnection = mock.Object; |
1072 | 1072 | |
1073 | 1073 | await twitterApi.AccountVerifyCredentials() |
@@ -1098,7 +1098,7 @@ namespace OpenTween.Api | ||
1098 | 1098 | ) |
1099 | 1099 | .ReturnsAsync(LazyJson.Create(new TwitterUser())); |
1100 | 1100 | |
1101 | - using var twitterApi = new TwitterApi(); | |
1101 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
1102 | 1102 | twitterApi.apiConnection = mock.Object; |
1103 | 1103 | |
1104 | 1104 | await twitterApi.AccountUpdateProfile(name: "Name", url: "http://example.com/", location: "Location", description: "<script>alert(1)</script>") |
@@ -1126,7 +1126,7 @@ namespace OpenTween.Api | ||
1126 | 1126 | ) |
1127 | 1127 | .ReturnsAsync(LazyJson.Create(new TwitterUser())); |
1128 | 1128 | |
1129 | - using var twitterApi = new TwitterApi(); | |
1129 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
1130 | 1130 | twitterApi.apiConnection = mock.Object; |
1131 | 1131 | |
1132 | 1132 | await twitterApi.AccountUpdateProfileImage(media) |
@@ -1148,7 +1148,7 @@ namespace OpenTween.Api | ||
1148 | 1148 | ) |
1149 | 1149 | .ReturnsAsync(new TwitterRateLimits()); |
1150 | 1150 | |
1151 | - using var twitterApi = new TwitterApi(); | |
1151 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
1152 | 1152 | twitterApi.apiConnection = mock.Object; |
1153 | 1153 | |
1154 | 1154 | await twitterApi.ApplicationRateLimitStatus() |
@@ -1169,7 +1169,7 @@ namespace OpenTween.Api | ||
1169 | 1169 | ) |
1170 | 1170 | .ReturnsAsync(new TwitterConfiguration()); |
1171 | 1171 | |
1172 | - using var twitterApi = new TwitterApi(); | |
1172 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
1173 | 1173 | twitterApi.apiConnection = mock.Object; |
1174 | 1174 | |
1175 | 1175 | await twitterApi.Configuration() |
@@ -1194,7 +1194,7 @@ namespace OpenTween.Api | ||
1194 | 1194 | ) |
1195 | 1195 | .ReturnsAsync(LazyJson.Create(new TwitterUploadMediaInit())); |
1196 | 1196 | |
1197 | - using var twitterApi = new TwitterApi(); | |
1197 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
1198 | 1198 | twitterApi.apiConnection = mock.Object; |
1199 | 1199 | |
1200 | 1200 | await twitterApi.MediaUploadInit(totalBytes: 123456L, mediaType: "image/png", mediaCategory: "dm_image") |
@@ -1222,7 +1222,7 @@ namespace OpenTween.Api | ||
1222 | 1222 | ) |
1223 | 1223 | .Returns(Task.CompletedTask); |
1224 | 1224 | |
1225 | - using var twitterApi = new TwitterApi(); | |
1225 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
1226 | 1226 | twitterApi.apiConnection = mock.Object; |
1227 | 1227 | |
1228 | 1228 | await twitterApi.MediaUploadAppend(mediaId: 11111L, segmentIndex: 1, media: media) |
@@ -1245,7 +1245,7 @@ namespace OpenTween.Api | ||
1245 | 1245 | ) |
1246 | 1246 | .ReturnsAsync(LazyJson.Create(new TwitterUploadMediaResult())); |
1247 | 1247 | |
1248 | - using var twitterApi = new TwitterApi(); | |
1248 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
1249 | 1249 | twitterApi.apiConnection = mock.Object; |
1250 | 1250 | |
1251 | 1251 | await twitterApi.MediaUploadFinalize(mediaId: 11111L) |
@@ -1270,7 +1270,7 @@ namespace OpenTween.Api | ||
1270 | 1270 | ) |
1271 | 1271 | .ReturnsAsync(new TwitterUploadMediaResult()); |
1272 | 1272 | |
1273 | - using var twitterApi = new TwitterApi(); | |
1273 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
1274 | 1274 | twitterApi.apiConnection = mock.Object; |
1275 | 1275 | |
1276 | 1276 | await twitterApi.MediaUploadStatus(mediaId: 11111L) |
@@ -1290,7 +1290,7 @@ namespace OpenTween.Api | ||
1290 | 1290 | ) |
1291 | 1291 | .Returns(Task.CompletedTask); |
1292 | 1292 | |
1293 | - using var twitterApi = new TwitterApi(); | |
1293 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
1294 | 1294 | twitterApi.apiConnection = mock.Object; |
1295 | 1295 | |
1296 | 1296 | await twitterApi.MediaMetadataCreate(mediaId: 12345L, altText: "hogehoge") |
@@ -1313,7 +1313,7 @@ namespace OpenTween.Api | ||
1313 | 1313 | ) |
1314 | 1314 | .ReturnsAsync(new MemoryStream()); |
1315 | 1315 | |
1316 | - using var twitterApi = new TwitterApi(); | |
1316 | + using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret")); | |
1317 | 1317 | twitterApi.apiConnection = mock.Object; |
1318 | 1318 | |
1319 | 1319 | var observable = twitterApi.UserStreams(replies: "all", track: "OpenTween"); |
@@ -0,0 +1,115 @@ | ||
1 | +// OpenTween - Client of Twitter | |
2 | +// Copyright (c) 2022 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/> | |
3 | +// All rights reserved. | |
4 | +// | |
5 | +// This file is part of OpenTween. | |
6 | +// | |
7 | +// This program is free software; you can redistribute it and/or modify it | |
8 | +// under the terms of the GNU General public License as published by the Free | |
9 | +// Software Foundation; either version 3 of the License, or (at your option) | |
10 | +// any later version. | |
11 | +// | |
12 | +// This program is distributed in the hope that it will be useful, but | |
13 | +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General public License | |
15 | +// for more details. | |
16 | +// | |
17 | +// You should have received a copy of the GNU General public License along | |
18 | +// with this program. If not, see <http://www.gnu.org/licenses/>, or write to | |
19 | +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, | |
20 | +// Boston, MA 02110-1301, USA. | |
21 | + | |
22 | +using System; | |
23 | +using System.Collections.Generic; | |
24 | +using System.Linq; | |
25 | +using System.Text; | |
26 | +using System.Threading.Tasks; | |
27 | +using Xunit; | |
28 | + | |
29 | +namespace OpenTween | |
30 | +{ | |
31 | + public class ApiKeyTest | |
32 | + { | |
33 | + [Fact] | |
34 | + public void Encrypt_FormatTest() | |
35 | + { | |
36 | + var password = "hoge"; | |
37 | + var plainText = "aaaaaaa"; | |
38 | + var encrypted = ApiKey.Encrypt(password, plainText); | |
39 | + Assert.StartsWith("%e%", encrypted); | |
40 | + Assert.Equal(5, encrypted.Split('%').Length); | |
41 | + } | |
42 | + | |
43 | + [Fact] | |
44 | + public void Encrypt_NonceTest() | |
45 | + { | |
46 | + // 同じ平文に対する暗号文を繰り返し生成しても出力は毎回変化する | |
47 | + var password = "hoge"; | |
48 | + var plainText = "aaaaaaa"; | |
49 | + var encrypted1 = ApiKey.Encrypt(password, plainText); | |
50 | + var encrypted2 = ApiKey.Encrypt(password, plainText); | |
51 | + Assert.NotEqual(encrypted1, encrypted2); | |
52 | + } | |
53 | + | |
54 | + [Fact] | |
55 | + public void Decrypt_Test() | |
56 | + { | |
57 | + var password = "password"; | |
58 | + var encrypted = "%e%m6EH2dECH7HWT9SFE0SK4Q==%mAAWPhPALf48s32s/yQarg==%zoCs8crMqZN6Nfj8ALkl2R3kbD/FORecuepU1LJ3CK0="; | |
59 | + var decrypted = ApiKey.Decrypt(password, encrypted); | |
60 | + Assert.Equal("hogehoge", decrypted); | |
61 | + } | |
62 | + | |
63 | + [Fact] | |
64 | + public void Decrypt_PlainTextTest() | |
65 | + { | |
66 | + // %e% から始まっていない文字列は平文として何もせずに返す | |
67 | + var password = "password"; | |
68 | + var plainText = "plaintext"; | |
69 | + var decrypted = ApiKey.Decrypt(password, plainText); | |
70 | + Assert.Equal("plaintext", decrypted); | |
71 | + } | |
72 | + | |
73 | + [Fact] | |
74 | + public void Decrypt_InvalidFormatTest() | |
75 | + { | |
76 | + var password = "password"; | |
77 | + var encrypted = "%e%INVALID_FORMAT"; | |
78 | + Assert.Throws<ApiKeyDecryptException>(() => ApiKey.Decrypt(password, encrypted)); | |
79 | + } | |
80 | + | |
81 | + [Fact] | |
82 | + public void Decrypt_InvalidBase64Test() | |
83 | + { | |
84 | + var password = "password"; | |
85 | + var encrypted = "%e%!!!!!!!!!!%!!!!!!!!!!%!!!!!!!!!!"; | |
86 | + Assert.Throws<ApiKeyDecryptException>(() => ApiKey.Decrypt(password, encrypted)); | |
87 | + } | |
88 | + | |
89 | + [Fact] | |
90 | + public void Decrypt_InvalidHMACTest() | |
91 | + { | |
92 | + var password = "password"; | |
93 | + var encrypted = "%e%m6EH2dECH7HWT9SFE0SK4Q==%mAAWPhPALf48s32s/yQarg==%AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; | |
94 | + Assert.Throws<ApiKeyDecryptException>(() => ApiKey.Decrypt(password, encrypted)); | |
95 | + } | |
96 | + | |
97 | + [Fact] | |
98 | + public void Decrypt_InvalidHMACLengthTest() | |
99 | + { | |
100 | + // HMAC が途中まで一致しているが長さが足りない場合 | |
101 | + var password = "password"; | |
102 | + var encrypted = "%e%m6EH2dECH7HWT9SFE0SK4Q==%mAAWPhPALf48s32s/yQarg==%zoCs8"; | |
103 | + Assert.Throws<ApiKeyDecryptException>(() => ApiKey.Decrypt(password, encrypted)); | |
104 | + } | |
105 | + | |
106 | + [Fact] | |
107 | + public void EncryptAndDecrypt_Test() | |
108 | + { | |
109 | + var password = "hoge"; | |
110 | + var plainText = "aaaaaaa"; | |
111 | + var encrypted = ApiKey.Encrypt(password, plainText); | |
112 | + Assert.Equal(plainText, ApiKey.Decrypt(password, encrypted)); | |
113 | + } | |
114 | + } | |
115 | +} |
@@ -34,7 +34,7 @@ namespace OpenTween.Connection | ||
34 | 34 | [Fact] |
35 | 35 | public void GetOAuthParameter_Test() |
36 | 36 | { |
37 | - var param = OAuthUtility.GetOAuthParameter("ConsumerKey", "Token"); | |
37 | + var param = OAuthUtility.GetOAuthParameter(ApiKey.Create("ConsumerKey"), "Token"); | |
38 | 38 | |
39 | 39 | Assert.Equal("ConsumerKey", param["oauth_consumer_key"]); |
40 | 40 | Assert.Equal("HMAC-SHA1", param["oauth_signature_method"]); |
@@ -53,7 +53,7 @@ namespace OpenTween.Connection | ||
53 | 53 | { |
54 | 54 | // GET http://example.com/hoge?aaa=foo に対する署名を生成 |
55 | 55 | // 実際の param は oauth_consumer_key などのパラメーターが加わった状態で渡される |
56 | - var oauthSignature = OAuthUtility.CreateSignature("ConsumerSecret", "TokenSecret", | |
56 | + var oauthSignature = OAuthUtility.CreateSignature(ApiKey.Create("ConsumerSecret"), "TokenSecret", | |
57 | 57 | "GET", new Uri("http://example.com/hoge"), new Dictionary<string, string> { ["aaa"] = "foo" }); |
58 | 58 | |
59 | 59 | var expectSignatureBase = "GET&http%3A%2F%2Fexample.com%2Fhoge&aaa%3Dfoo"; |
@@ -69,7 +69,7 @@ namespace OpenTween.Connection | ||
69 | 69 | { |
70 | 70 | // GET http://example.com/hoge?aaa=foo&bbb=bar に対する署名を生成 |
71 | 71 | // 複数のパラメータが渡される場合は name 順でソートされる |
72 | - var oauthSignature = OAuthUtility.CreateSignature("ConsumerSecret", "TokenSecret", | |
72 | + var oauthSignature = OAuthUtility.CreateSignature(ApiKey.Create("ConsumerSecret"), "TokenSecret", | |
73 | 73 | "GET", new Uri("http://example.com/hoge"), new Dictionary<string, string> { |
74 | 74 | ["bbb"] = "bar", |
75 | 75 | ["aaa"] = "foo", |
@@ -88,7 +88,7 @@ namespace OpenTween.Connection | ||
88 | 88 | { |
89 | 89 | // GET http://example.com/hoge?aaa=foo に対する署名を生成 |
90 | 90 | // リクエストトークンの発行時は tokenSecret が空の状態で署名を生成することになる |
91 | - var oauthSignature = OAuthUtility.CreateSignature("ConsumerSecret", null, | |
91 | + var oauthSignature = OAuthUtility.CreateSignature(ApiKey.Create("ConsumerSecret"), null, | |
92 | 92 | "GET", new Uri("http://example.com/hoge"), new Dictionary<string, string> { ["aaa"] = "foo" }); |
93 | 93 | |
94 | 94 | var expectSignatureBase = "GET&http%3A%2F%2Fexample.com%2Fhoge&aaa%3Dfoo"; |
@@ -104,7 +104,7 @@ namespace OpenTween.Connection | ||
104 | 104 | { |
105 | 105 | var authorization = OAuthUtility.CreateAuthorization( |
106 | 106 | "GET", new Uri("http://example.com/hoge"), new Dictionary<string, string> { ["aaa"] = "hoge" }, |
107 | - "ConsumerKey", "ConsumerSecret", "AccessToken", "AccessSecret", "Realm"); | |
107 | + ApiKey.Create("ConsumerKey"), ApiKey.Create("ConsumerSecret"), "AccessToken", "AccessSecret", "Realm"); | |
108 | 108 | |
109 | 109 | Assert.StartsWith("OAuth ", authorization, StringComparison.Ordinal); |
110 | 110 |
@@ -56,7 +56,7 @@ namespace OpenTween.Connection | ||
56 | 56 | { |
57 | 57 | using var mockHandler = new HttpMessageHandlerMock(); |
58 | 58 | using var http = new HttpClient(mockHandler); |
59 | - using var apiConnection = new TwitterApiConnection("", ""); | |
59 | + using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", ""); | |
60 | 60 | apiConnection.http = http; |
61 | 61 | |
62 | 62 | mockHandler.Enqueue(x => |
@@ -95,7 +95,7 @@ namespace OpenTween.Connection | ||
95 | 95 | { |
96 | 96 | using var mockHandler = new HttpMessageHandlerMock(); |
97 | 97 | using var http = new HttpClient(mockHandler); |
98 | - using var apiConnection = new TwitterApiConnection("", ""); | |
98 | + using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", ""); | |
99 | 99 | apiConnection.http = http; |
100 | 100 | |
101 | 101 | mockHandler.Enqueue(x => |
@@ -133,7 +133,7 @@ namespace OpenTween.Connection | ||
133 | 133 | { |
134 | 134 | using var mockHandler = new HttpMessageHandlerMock(); |
135 | 135 | using var http = new HttpClient(mockHandler); |
136 | - using var apiConnection = new TwitterApiConnection("", ""); | |
136 | + using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", ""); | |
137 | 137 | apiConnection.http = http; |
138 | 138 | |
139 | 139 | mockHandler.Enqueue(x => |
@@ -174,7 +174,7 @@ namespace OpenTween.Connection | ||
174 | 174 | { |
175 | 175 | using var mockHandler = new HttpMessageHandlerMock(); |
176 | 176 | using var http = new HttpClient(mockHandler); |
177 | - using var apiConnection = new TwitterApiConnection("", ""); | |
177 | + using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", ""); | |
178 | 178 | apiConnection.http = http; |
179 | 179 | |
180 | 180 | mockHandler.Enqueue(x => |
@@ -202,7 +202,7 @@ namespace OpenTween.Connection | ||
202 | 202 | { |
203 | 203 | using var mockHandler = new HttpMessageHandlerMock(); |
204 | 204 | using var http = new HttpClient(mockHandler); |
205 | - using var apiConnection = new TwitterApiConnection("", ""); | |
205 | + using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", ""); | |
206 | 206 | apiConnection.http = http; |
207 | 207 | |
208 | 208 | mockHandler.Enqueue(x => |
@@ -232,7 +232,7 @@ namespace OpenTween.Connection | ||
232 | 232 | { |
233 | 233 | using var mockHandler = new HttpMessageHandlerMock(); |
234 | 234 | using var http = new HttpClient(mockHandler); |
235 | - using var apiConnection = new TwitterApiConnection("", ""); | |
235 | + using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", ""); | |
236 | 236 | using var image = TestUtils.CreateDummyImage(); |
237 | 237 | apiConnection.http = http; |
238 | 238 |
@@ -279,7 +279,7 @@ namespace OpenTween.Connection | ||
279 | 279 | { |
280 | 280 | using var mockHandler = new HttpMessageHandlerMock(); |
281 | 281 | using var http = new HttpClient(mockHandler); |
282 | - using var apiConnection = new TwitterApiConnection("", ""); | |
282 | + using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", ""); | |
283 | 283 | apiConnection.http = http; |
284 | 284 | |
285 | 285 | mockHandler.Enqueue(async x => |
@@ -322,7 +322,7 @@ namespace OpenTween.Connection | ||
322 | 322 | { |
323 | 323 | using var mockHandler = new HttpMessageHandlerMock(); |
324 | 324 | using var http = new HttpClient(mockHandler); |
325 | - using var apiConnection = new TwitterApiConnection("", ""); | |
325 | + using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", ""); | |
326 | 326 | apiConnection.httpUpload = http; |
327 | 327 | |
328 | 328 | using var image = TestUtils.CreateDummyImage(); |
@@ -394,7 +394,7 @@ namespace OpenTween.Connection | ||
394 | 394 | { |
395 | 395 | using var mockHandler = new HttpMessageHandlerMock(); |
396 | 396 | using var http = new HttpClient(mockHandler); |
397 | - using var apiConnection = new TwitterApiConnection("", ""); | |
397 | + using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", ""); | |
398 | 398 | apiConnection.httpUpload = http; |
399 | 399 | |
400 | 400 | mockHandler.Enqueue(async x => |
@@ -441,7 +441,7 @@ namespace OpenTween.Connection | ||
441 | 441 | { |
442 | 442 | using var mockHandler = new HttpMessageHandlerMock(); |
443 | 443 | using var http = new HttpClient(mockHandler); |
444 | - using var apiConnection = new TwitterApiConnection("", ""); | |
444 | + using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", ""); | |
445 | 445 | apiConnection.http = http; |
446 | 446 | |
447 | 447 | mockHandler.Enqueue(async x => |
@@ -473,7 +473,7 @@ namespace OpenTween.Connection | ||
473 | 473 | { |
474 | 474 | using var mockHandler = new HttpMessageHandlerMock(); |
475 | 475 | using var http = new HttpClient(mockHandler); |
476 | - using var apiConnection = new TwitterApiConnection("", ""); | |
476 | + using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", ""); | |
477 | 477 | apiConnection.http = http; |
478 | 478 | |
479 | 479 | mockHandler.Enqueue(async x => |
@@ -513,7 +513,7 @@ namespace OpenTween.Connection | ||
513 | 513 | { |
514 | 514 | using var mockHandler = new HttpMessageHandlerMock(); |
515 | 515 | using var http = new HttpClient(mockHandler); |
516 | - using var apiConnection = new TwitterApiConnection("", ""); | |
516 | + using var apiConnection = new TwitterApiConnection(ApiKey.Create(""), ApiKey.Create(""), "", ""); | |
517 | 517 | apiConnection.http = http; |
518 | 518 | |
519 | 519 | mockHandler.Enqueue(x => |
@@ -9,6 +9,7 @@ using System.Runtime.InteropServices; | ||
9 | 9 | using System.Text; |
10 | 10 | using System.Text.RegularExpressions; |
11 | 11 | using Moq; |
12 | +using OpenTween.Api; | |
12 | 13 | using OpenTween.Api.DataModel; |
13 | 14 | using Xunit; |
14 | 15 |
@@ -30,7 +31,8 @@ namespace OpenTween | ||
30 | 31 | [Fact] |
31 | 32 | public void Initialize_TwitterTest() |
32 | 33 | { |
33 | - using var twitter = new Twitter(); | |
34 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
35 | + using var twitter = new Twitter(twitterApi); | |
34 | 36 | using var mediaSelector = new MediaSelector(); |
35 | 37 | twitter.Initialize("", "", "", 0L); |
36 | 38 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Twitter"); |
@@ -51,7 +53,8 @@ namespace OpenTween | ||
51 | 53 | [Fact] |
52 | 54 | public void Initialize_ImgurTest() |
53 | 55 | { |
54 | - using var twitter = new Twitter(); | |
56 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
57 | + using var twitter = new Twitter(twitterApi); | |
55 | 58 | using var mediaSelector = new MediaSelector(); |
56 | 59 | twitter.Initialize("", "", "", 0L); |
57 | 60 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Imgur"); |
@@ -70,7 +73,8 @@ namespace OpenTween | ||
70 | 73 | [Fact] |
71 | 74 | public void BeginSelection_BlankTest() |
72 | 75 | { |
73 | - using var twitter = new Twitter(); | |
76 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
77 | + using var twitter = new Twitter(twitterApi); | |
74 | 78 | using var mediaSelector = new MediaSelector { Visible = false, Enabled = false }; |
75 | 79 | twitter.Initialize("", "", "", 0L); |
76 | 80 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Twitter"); |
@@ -97,7 +101,8 @@ namespace OpenTween | ||
97 | 101 | [Fact] |
98 | 102 | public void BeginSelection_FilePathTest() |
99 | 103 | { |
100 | - using var twitter = new Twitter(); | |
104 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
105 | + using var twitter = new Twitter(twitterApi); | |
101 | 106 | using var mediaSelector = new MediaSelector { Visible = false, Enabled = false }; |
102 | 107 | twitter.Initialize("", "", "", 0L); |
103 | 108 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Twitter"); |
@@ -129,7 +134,8 @@ namespace OpenTween | ||
129 | 134 | [Fact] |
130 | 135 | public void BeginSelection_MemoryImageTest() |
131 | 136 | { |
132 | - using var twitter = new Twitter(); | |
137 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
138 | + using var twitter = new Twitter(twitterApi); | |
133 | 139 | using var mediaSelector = new MediaSelector { Visible = false, Enabled = false }; |
134 | 140 | twitter.Initialize("", "", "", 0L); |
135 | 141 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Twitter"); |
@@ -164,7 +170,8 @@ namespace OpenTween | ||
164 | 170 | [Fact] |
165 | 171 | public void BeginSelection_MultiImageTest() |
166 | 172 | { |
167 | - using var twitter = new Twitter(); | |
173 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
174 | + using var twitter = new Twitter(twitterApi); | |
168 | 175 | using var mediaSelector = new MediaSelector { Visible = false, Enabled = false }; |
169 | 176 | twitter.Initialize("", "", "", 0L); |
170 | 177 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Twitter"); |
@@ -188,7 +195,8 @@ namespace OpenTween | ||
188 | 195 | [Fact] |
189 | 196 | public void EndSelection_Test() |
190 | 197 | { |
191 | - using var twitter = new Twitter(); | |
198 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
199 | + using var twitter = new Twitter(twitterApi); | |
192 | 200 | using var mediaSelector = new MediaSelector { Visible = false, Enabled = false }; |
193 | 201 | twitter.Initialize("", "", "", 0L); |
194 | 202 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Twitter"); |
@@ -211,7 +219,8 @@ namespace OpenTween | ||
211 | 219 | [Fact] |
212 | 220 | public void PageChange_Test() |
213 | 221 | { |
214 | - using var twitter = new Twitter(); | |
222 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
223 | + using var twitter = new Twitter(twitterApi); | |
215 | 224 | using var mediaSelector = new MediaSelector { Visible = false, Enabled = false }; |
216 | 225 | twitter.Initialize("", "", "", 0L); |
217 | 226 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Twitter"); |
@@ -254,7 +263,8 @@ namespace OpenTween | ||
254 | 263 | [Fact] |
255 | 264 | public void PageChange_AlternativeTextTest() |
256 | 265 | { |
257 | - using var twitter = new Twitter(); | |
266 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
267 | + using var twitter = new Twitter(twitterApi); | |
258 | 268 | using var mediaSelector = new MediaSelector { Visible = false, Enabled = false }; |
259 | 269 | twitter.Initialize("", "", "", 0L); |
260 | 270 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Twitter"); |
@@ -291,7 +301,8 @@ namespace OpenTween | ||
291 | 301 | [Fact] |
292 | 302 | public void PageChange_ImageDisposeTest() |
293 | 303 | { |
294 | - using var twitter = new Twitter(); | |
304 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
305 | + using var twitter = new Twitter(twitterApi); | |
295 | 306 | using var mediaSelector = new MediaSelector { Visible = false, Enabled = false }; |
296 | 307 | twitter.Initialize("", "", "", 0L); |
297 | 308 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Twitter"); |
@@ -319,7 +330,8 @@ namespace OpenTween | ||
319 | 330 | [Fact] |
320 | 331 | public void ImagePathInput_Test() |
321 | 332 | { |
322 | - using var twitter = new Twitter(); | |
333 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
334 | + using var twitter = new Twitter(twitterApi); | |
323 | 335 | using var mediaSelector = new MediaSelector { Visible = false, Enabled = false }; |
324 | 336 | twitter.Initialize("", "", "", 0L); |
325 | 337 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Twitter"); |
@@ -344,7 +356,8 @@ namespace OpenTween | ||
344 | 356 | [Fact] |
345 | 357 | public void ImagePathInput_ReplaceFileMediaItemTest() |
346 | 358 | { |
347 | - using var twitter = new Twitter(); | |
359 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
360 | + using var twitter = new Twitter(twitterApi); | |
348 | 361 | using var mediaSelector = new MediaSelector { Visible = false, Enabled = false }; |
349 | 362 | twitter.Initialize("", "", "", 0L); |
350 | 363 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Twitter"); |
@@ -372,7 +385,8 @@ namespace OpenTween | ||
372 | 385 | [Fact] |
373 | 386 | public void ImagePathInput_ReplaceMemoryImageMediaItemTest() |
374 | 387 | { |
375 | - using var twitter = new Twitter(); | |
388 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
389 | + using var twitter = new Twitter(twitterApi); | |
376 | 390 | using var mediaSelector = new MediaSelector { Visible = false, Enabled = false }; |
377 | 391 | twitter.Initialize("", "", "", 0L); |
378 | 392 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Twitter"); |
@@ -411,7 +425,8 @@ namespace OpenTween | ||
411 | 425 | [Fact] |
412 | 426 | public void ImageServiceChange_Test() |
413 | 427 | { |
414 | - using var twitter = new Twitter(); | |
428 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
429 | + using var twitter = new Twitter(twitterApi); | |
415 | 430 | using var mediaSelector = new MediaSelector { Visible = false, Enabled = false }; |
416 | 431 | twitter.Initialize("", "", "", 0L); |
417 | 432 | mediaSelector.Initialize(twitter, TwitterConfiguration.DefaultConfiguration(), "Twitter"); |
@@ -71,7 +71,7 @@ namespace OpenTween.Thumbnail.Services | ||
71 | 71 | var handler = new HttpMessageHandlerMock(); |
72 | 72 | using (var http = new HttpClient(handler)) |
73 | 73 | { |
74 | - var service = new FoursquareCheckin(http); | |
74 | + var service = new FoursquareCheckin(http, ApiKey.Create("fake_client_id"), ApiKey.Create("fake_client_secret")); | |
75 | 75 | |
76 | 76 | handler.Enqueue(x => |
77 | 77 | { |
@@ -81,8 +81,8 @@ namespace OpenTween.Thumbnail.Services | ||
81 | 81 | |
82 | 82 | var query = HttpUtility.ParseQueryString(x.RequestUri.Query); |
83 | 83 | |
84 | - Assert.Equal(ApplicationSettings.FoursquareClientId, query["client_id"]); | |
85 | - Assert.Equal(ApplicationSettings.FoursquareClientSecret, query["client_secret"]); | |
84 | + Assert.Equal("fake_client_id", query["client_id"]); | |
85 | + Assert.Equal("fake_client_secret", query["client_secret"]); | |
86 | 86 | Assert.NotNull(query["v"]); |
87 | 87 | Assert.Equal("xxxxxxxx", query["shortId"]); |
88 | 88 |
@@ -109,7 +109,7 @@ namespace OpenTween.Thumbnail.Services | ||
109 | 109 | var handler = new HttpMessageHandlerMock(); |
110 | 110 | using (var http = new HttpClient(handler)) |
111 | 111 | { |
112 | - var service = new FoursquareCheckin(http); | |
112 | + var service = new FoursquareCheckin(http, ApiKey.Create("fake_client_id"), ApiKey.Create("fake_client_secret")); | |
113 | 113 | |
114 | 114 | handler.Enqueue(x => |
115 | 115 | { |
@@ -119,8 +119,8 @@ namespace OpenTween.Thumbnail.Services | ||
119 | 119 | |
120 | 120 | var query = HttpUtility.ParseQueryString(x.RequestUri.Query); |
121 | 121 | |
122 | - Assert.Equal(ApplicationSettings.FoursquareClientId, query["client_id"]); | |
123 | - Assert.Equal(ApplicationSettings.FoursquareClientSecret, query["client_secret"]); | |
122 | + Assert.Equal("fake_client_id", query["client_id"]); | |
123 | + Assert.Equal("fake_client_secret", query["client_secret"]); | |
124 | 124 | Assert.NotNull(query["v"]); |
125 | 125 | Assert.Null(query["signature"]); |
126 | 126 |
@@ -147,7 +147,7 @@ namespace OpenTween.Thumbnail.Services | ||
147 | 147 | var handler = new HttpMessageHandlerMock(); |
148 | 148 | using (var http = new HttpClient(handler)) |
149 | 149 | { |
150 | - var service = new FoursquareCheckin(http); | |
150 | + var service = new FoursquareCheckin(http, ApiKey.Create("fake_client_id"), ApiKey.Create("fake_client_secret")); | |
151 | 151 | |
152 | 152 | handler.Enqueue(x => |
153 | 153 | { |
@@ -157,8 +157,8 @@ namespace OpenTween.Thumbnail.Services | ||
157 | 157 | |
158 | 158 | var query = HttpUtility.ParseQueryString(x.RequestUri.Query); |
159 | 159 | |
160 | - Assert.Equal(ApplicationSettings.FoursquareClientId, query["client_id"]); | |
161 | - Assert.Equal(ApplicationSettings.FoursquareClientSecret, query["client_secret"]); | |
160 | + Assert.Equal("fake_client_id", query["client_id"]); | |
161 | + Assert.Equal("fake_client_secret", query["client_secret"]); | |
162 | 162 | Assert.NotNull(query["v"]); |
163 | 163 | Assert.Equal("aaaaaaa", query["signature"]); |
164 | 164 |
@@ -185,7 +185,7 @@ namespace OpenTween.Thumbnail.Services | ||
185 | 185 | var handler = new HttpMessageHandlerMock(); |
186 | 186 | using (var http = new HttpClient(handler)) |
187 | 187 | { |
188 | - var service = new FoursquareCheckin(http); | |
188 | + var service = new FoursquareCheckin(http, ApiKey.Create("fake_client_id"), ApiKey.Create("fake_client_secret")); | |
189 | 189 | |
190 | 190 | handler.Enqueue(x => |
191 | 191 | { |
@@ -209,6 +209,20 @@ namespace OpenTween.Thumbnail.Services | ||
209 | 209 | } |
210 | 210 | |
211 | 211 | [Fact] |
212 | + public async Task GetThumbnailInfoAsync_ApiKeyErrorTest() | |
213 | + { | |
214 | + var handler = new HttpMessageHandlerMock(); | |
215 | + using var http = new HttpClient(handler); | |
216 | + var service = new FoursquareCheckin(http, ApiKey.Create("%e%INVALID_API_KEY"), ApiKey.Create("%e%INVALID_API_KEY")); | |
217 | + | |
218 | + var post = new PostClass(); | |
219 | + var thumb = await service.GetThumbnailInfoAsync("https://www.swarmapp.com/c/xxxxxxxx", post, CancellationToken.None) | |
220 | + .ConfigureAwait(false); | |
221 | + | |
222 | + Assert.Null(thumb); | |
223 | + } | |
224 | + | |
225 | + [Fact] | |
212 | 226 | public void ParseInLocation_Test() |
213 | 227 | { |
214 | 228 | var json = @"{ |
@@ -43,11 +43,11 @@ namespace OpenTween.Thumbnail.Services | ||
43 | 43 | public string FakeXml { get; set; } = ""; |
44 | 44 | |
45 | 45 | public TestTinami() |
46 | - : base(null) | |
46 | + : base(ApiKey.Create("fake_api_key"), null) | |
47 | 47 | { |
48 | 48 | } |
49 | 49 | |
50 | - protected override Task<XDocument> FetchContentInfoApiAsync(string contentId, CancellationToken token) | |
50 | + protected override Task<XDocument> FetchContentInfoApiAsync(string apiKey, string contentId, CancellationToken token) | |
51 | 51 | => Task.FromResult(XDocument.Parse(this.FakeXml)); |
52 | 52 | } |
53 | 53 |
@@ -92,5 +92,16 @@ namespace OpenTween.Thumbnail.Services | ||
92 | 92 | |
93 | 93 | Assert.Null(thumbinfo); |
94 | 94 | } |
95 | + | |
96 | + [Fact] | |
97 | + public async Task GetThumbnailInfoAsync_ApiKeyErrorTest() | |
98 | + { | |
99 | + var service = new Tinami(ApiKey.Create("%e%INVALID_API_KEY"), null); | |
100 | + | |
101 | + var thumbinfo = await service.GetThumbnailInfoAsync("http://www.tinami.com/view/12345", new PostClass(), CancellationToken.None) | |
102 | + .ConfigureAwait(false); | |
103 | + | |
104 | + Assert.Null(thumbinfo); | |
105 | + } | |
95 | 106 | } |
96 | 107 | } |
@@ -70,7 +70,7 @@ namespace OpenTween.Thumbnail.Services | ||
70 | 70 | |
71 | 71 | var query = HttpUtility.ParseQueryString(x.RequestUri.Query); |
72 | 72 | |
73 | - Assert.Equal(ApplicationSettings.TumblrConsumerKey, query["api_key"]); | |
73 | + Assert.Equal("fake_api_key", query["api_key"]); | |
74 | 74 | Assert.Equal("1234567", query["id"]); |
75 | 75 | |
76 | 76 | return new HttpResponseMessage(HttpStatusCode.OK) |
@@ -84,7 +84,7 @@ namespace OpenTween.Thumbnail.Services | ||
84 | 84 | |
85 | 85 | using (var http = new HttpClient(handler)) |
86 | 86 | { |
87 | - var service = new Tumblr(http); | |
87 | + var service = new Tumblr(ApiKey.Create("fake_api_key"), http); | |
88 | 88 | |
89 | 89 | var url = "http://hoge.tumblr.com/post/1234567/tetetete"; |
90 | 90 | await service.GetThumbnailInfoAsync(url, new PostClass(), CancellationToken.None) |
@@ -107,7 +107,7 @@ namespace OpenTween.Thumbnail.Services | ||
107 | 107 | |
108 | 108 | var query = HttpUtility.ParseQueryString(x.RequestUri.Query); |
109 | 109 | |
110 | - Assert.Equal(ApplicationSettings.TumblrConsumerKey, query["api_key"]); | |
110 | + Assert.Equal("fake_api_key", query["api_key"]); | |
111 | 111 | Assert.Equal("1234567", query["id"]); |
112 | 112 | |
113 | 113 | return new HttpResponseMessage(HttpStatusCode.OK) |
@@ -121,7 +121,7 @@ namespace OpenTween.Thumbnail.Services | ||
121 | 121 | |
122 | 122 | using (var http = new HttpClient(handler)) |
123 | 123 | { |
124 | - var service = new Tumblr(http); | |
124 | + var service = new Tumblr(ApiKey.Create("fake_api_key"), http); | |
125 | 125 | |
126 | 126 | // Tumblrのカスタムドメイン名を使ってるっぽいURL |
127 | 127 | var url = "http://tumblr.example.com/post/1234567/tetetete"; |
@@ -131,5 +131,19 @@ namespace OpenTween.Thumbnail.Services | ||
131 | 131 | |
132 | 132 | Assert.Equal(0, handler.QueueCount); |
133 | 133 | } |
134 | + | |
135 | + [Fact] | |
136 | + public async Task GetThumbnailInfoAsync_ApiKeyErrorTest() | |
137 | + { | |
138 | + var handler = new HttpMessageHandlerMock(); | |
139 | + | |
140 | + using var http = new HttpClient(handler); | |
141 | + var service = new Tumblr(ApiKey.Create("%e%INVALID_API_KEY"), http); | |
142 | + | |
143 | + var url = "http://hoge.tumblr.com/post/1234567/tetetete"; | |
144 | + var thumb = await service.GetThumbnailInfoAsync(url, new PostClass(), CancellationToken.None) | |
145 | + .ConfigureAwait(false); | |
146 | + Assert.Null(thumb); | |
147 | + } | |
134 | 148 | } |
135 | 149 | } |
@@ -24,6 +24,7 @@ using System.Collections.Generic; | ||
24 | 24 | using System.Linq; |
25 | 25 | using System.Text; |
26 | 26 | using System.Text.RegularExpressions; |
27 | +using OpenTween.Api; | |
27 | 28 | using OpenTween.Api.DataModel; |
28 | 29 | using OpenTween.Models; |
29 | 30 | using OpenTween.Setting; |
@@ -541,108 +542,108 @@ namespace OpenTween | ||
541 | 542 | [Fact] |
542 | 543 | public void GetTextLengthRemain_Test() |
543 | 544 | { |
544 | - using (var twitter = new Twitter()) | |
545 | - { | |
546 | - Assert.Equal(280, twitter.GetTextLengthRemain("")); | |
547 | - Assert.Equal(272, twitter.GetTextLengthRemain("hogehoge")); | |
548 | - } | |
545 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
546 | + using var twitter = new Twitter(twitterApi); | |
547 | + | |
548 | + Assert.Equal(280, twitter.GetTextLengthRemain("")); | |
549 | + Assert.Equal(272, twitter.GetTextLengthRemain("hogehoge")); | |
549 | 550 | } |
550 | 551 | |
551 | 552 | [Fact] |
552 | 553 | public void GetTextLengthRemain_DirectMessageTest() |
553 | 554 | { |
554 | - using (var twitter = new Twitter()) | |
555 | - { | |
556 | - // 2015年8月から DM の文字数上限が 10,000 文字に変更された | |
557 | - // https://twittercommunity.com/t/41348 | |
558 | - twitter.Configuration.DmTextCharacterLimit = 10000; | |
555 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
556 | + using var twitter = new Twitter(twitterApi); | |
559 | 557 | |
560 | - Assert.Equal(10000, twitter.GetTextLengthRemain("D twitter ")); | |
561 | - Assert.Equal(9992, twitter.GetTextLengthRemain("D twitter hogehoge")); | |
558 | + // 2015年8月から DM の文字数上限が 10,000 文字に変更された | |
559 | + // https://twittercommunity.com/t/41348 | |
560 | + twitter.Configuration.DmTextCharacterLimit = 10000; | |
562 | 561 | |
563 | - // t.co に短縮される分の文字数を考慮 | |
564 | - twitter.Configuration.ShortUrlLength = 20; | |
565 | - Assert.Equal(9971, twitter.GetTextLengthRemain("D twitter hogehoge http://example.com/")); | |
562 | + Assert.Equal(10000, twitter.GetTextLengthRemain("D twitter ")); | |
563 | + Assert.Equal(9992, twitter.GetTextLengthRemain("D twitter hogehoge")); | |
566 | 564 | |
567 | - twitter.Configuration.ShortUrlLengthHttps = 21; | |
568 | - Assert.Equal(9970, twitter.GetTextLengthRemain("D twitter hogehoge https://example.com/")); | |
569 | - } | |
565 | + // t.co に短縮される分の文字数を考慮 | |
566 | + twitter.Configuration.ShortUrlLength = 20; | |
567 | + Assert.Equal(9971, twitter.GetTextLengthRemain("D twitter hogehoge http://example.com/")); | |
568 | + | |
569 | + twitter.Configuration.ShortUrlLengthHttps = 21; | |
570 | + Assert.Equal(9970, twitter.GetTextLengthRemain("D twitter hogehoge https://example.com/")); | |
570 | 571 | } |
571 | 572 | |
572 | 573 | [Fact] |
573 | 574 | public void GetTextLengthRemain_UrlTest() |
574 | 575 | { |
575 | - using (var twitter = new Twitter()) | |
576 | - { | |
577 | - // t.co に短縮される分の文字数を考慮 | |
578 | - twitter.TextConfiguration.TransformedURLLength = 20; | |
579 | - Assert.Equal(260, twitter.GetTextLengthRemain("http://example.com/")); | |
580 | - Assert.Equal(260, twitter.GetTextLengthRemain("http://example.com/hogehoge")); | |
581 | - Assert.Equal(251, twitter.GetTextLengthRemain("hogehoge http://example.com/")); | |
582 | - | |
583 | - Assert.Equal(260, twitter.GetTextLengthRemain("https://example.com/")); | |
584 | - Assert.Equal(260, twitter.GetTextLengthRemain("https://example.com/hogehoge")); | |
585 | - Assert.Equal(251, twitter.GetTextLengthRemain("hogehoge https://example.com/")); | |
586 | - } | |
576 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
577 | + using var twitter = new Twitter(twitterApi); | |
578 | + | |
579 | + // t.co に短縮される分の文字数を考慮 | |
580 | + twitter.TextConfiguration.TransformedURLLength = 20; | |
581 | + Assert.Equal(260, twitter.GetTextLengthRemain("http://example.com/")); | |
582 | + Assert.Equal(260, twitter.GetTextLengthRemain("http://example.com/hogehoge")); | |
583 | + Assert.Equal(251, twitter.GetTextLengthRemain("hogehoge http://example.com/")); | |
584 | + | |
585 | + Assert.Equal(260, twitter.GetTextLengthRemain("https://example.com/")); | |
586 | + Assert.Equal(260, twitter.GetTextLengthRemain("https://example.com/hogehoge")); | |
587 | + Assert.Equal(251, twitter.GetTextLengthRemain("hogehoge https://example.com/")); | |
587 | 588 | } |
588 | 589 | |
589 | 590 | [Fact] |
590 | 591 | public void GetTextLengthRemain_UrlWithoutSchemeTest() |
591 | 592 | { |
592 | - using (var twitter = new Twitter()) | |
593 | - { | |
594 | - // t.co に短縮される分の文字数を考慮 | |
595 | - twitter.TextConfiguration.TransformedURLLength = 20; | |
596 | - Assert.Equal(260, twitter.GetTextLengthRemain("example.com")); | |
597 | - Assert.Equal(260, twitter.GetTextLengthRemain("example.com/hogehoge")); | |
598 | - Assert.Equal(251, twitter.GetTextLengthRemain("hogehoge example.com")); | |
599 | - | |
600 | - // スキーム (http://) を省略かつ末尾が ccTLD の場合は t.co に短縮されない | |
601 | - Assert.Equal(270, twitter.GetTextLengthRemain("example.jp")); | |
602 | - // ただし、末尾にパスが続く場合は t.co に短縮される | |
603 | - Assert.Equal(260, twitter.GetTextLengthRemain("example.jp/hogehoge")); | |
604 | - } | |
593 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
594 | + using var twitter = new Twitter(twitterApi); | |
595 | + | |
596 | + // t.co に短縮される分の文字数を考慮 | |
597 | + twitter.TextConfiguration.TransformedURLLength = 20; | |
598 | + Assert.Equal(260, twitter.GetTextLengthRemain("example.com")); | |
599 | + Assert.Equal(260, twitter.GetTextLengthRemain("example.com/hogehoge")); | |
600 | + Assert.Equal(251, twitter.GetTextLengthRemain("hogehoge example.com")); | |
601 | + | |
602 | + // スキーム (http://) を省略かつ末尾が ccTLD の場合は t.co に短縮されない | |
603 | + Assert.Equal(270, twitter.GetTextLengthRemain("example.jp")); | |
604 | + // ただし、末尾にパスが続く場合は t.co に短縮される | |
605 | + Assert.Equal(260, twitter.GetTextLengthRemain("example.jp/hogehoge")); | |
605 | 606 | } |
606 | 607 | |
607 | 608 | [Fact] |
608 | 609 | public void GetTextLengthRemain_SurrogatePairTest() |
609 | 610 | { |
610 | - using (var twitter = new Twitter()) | |
611 | - { | |
612 | - Assert.Equal(278, twitter.GetTextLengthRemain("🍣")); | |
613 | - Assert.Equal(267, twitter.GetTextLengthRemain("🔥🐔🔥 焼き鳥")); | |
614 | - } | |
611 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
612 | + using var twitter = new Twitter(twitterApi); | |
613 | + | |
614 | + Assert.Equal(278, twitter.GetTextLengthRemain("🍣")); | |
615 | + Assert.Equal(267, twitter.GetTextLengthRemain("🔥🐔🔥 焼き鳥")); | |
615 | 616 | } |
616 | 617 | |
617 | 618 | [Fact] |
618 | 619 | public void GetTextLengthRemain_EmojiTest() |
619 | 620 | { |
620 | - using (var twitter = new Twitter()) | |
621 | - { | |
622 | - // 絵文字の文字数カウントの仕様変更に対するテストケース | |
623 | - // https://twittercommunity.com/t/114607 | |
624 | - | |
625 | - Assert.Equal(279, twitter.GetTextLengthRemain("©")); // 基本多言語面の絵文字 | |
626 | - Assert.Equal(277, twitter.GetTextLengthRemain("©\uFE0E")); // 異字体セレクタ付き (text style) | |
627 | - Assert.Equal(279, twitter.GetTextLengthRemain("©\uFE0F")); // 異字体セレクタ付き (emoji style) | |
628 | - Assert.Equal(278, twitter.GetTextLengthRemain("🍣")); // 拡張面の絵文字 | |
629 | - Assert.Equal(279, twitter.GetTextLengthRemain("#⃣")); // 合字で表現される絵文字 | |
630 | - Assert.Equal(278, twitter.GetTextLengthRemain("👦\U0001F3FF")); // Emoji modifier 付きの絵文字 | |
631 | - Assert.Equal(278, twitter.GetTextLengthRemain("\U0001F3FF")); // Emoji modifier 単体 | |
632 | - Assert.Equal(278, twitter.GetTextLengthRemain("👨\u200D🎨")); // ZWJ で結合された絵文字 | |
633 | - Assert.Equal(278, twitter.GetTextLengthRemain("🏃\u200D♀\uFE0F")); // ZWJ と異字体セレクタを含む絵文字 | |
634 | - } | |
621 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
622 | + using var twitter = new Twitter(twitterApi); | |
623 | + | |
624 | + // 絵文字の文字数カウントの仕様変更に対するテストケース | |
625 | + // https://twittercommunity.com/t/114607 | |
626 | + | |
627 | + Assert.Equal(279, twitter.GetTextLengthRemain("©")); // 基本多言語面の絵文字 | |
628 | + Assert.Equal(277, twitter.GetTextLengthRemain("©\uFE0E")); // 異字体セレクタ付き (text style) | |
629 | + Assert.Equal(279, twitter.GetTextLengthRemain("©\uFE0F")); // 異字体セレクタ付き (emoji style) | |
630 | + Assert.Equal(278, twitter.GetTextLengthRemain("🍣")); // 拡張面の絵文字 | |
631 | + Assert.Equal(279, twitter.GetTextLengthRemain("#⃣")); // 合字で表現される絵文字 | |
632 | + Assert.Equal(278, twitter.GetTextLengthRemain("👦\U0001F3FF")); // Emoji modifier 付きの絵文字 | |
633 | + Assert.Equal(278, twitter.GetTextLengthRemain("\U0001F3FF")); // Emoji modifier 単体 | |
634 | + Assert.Equal(278, twitter.GetTextLengthRemain("👨\u200D🎨")); // ZWJ で結合された絵文字 | |
635 | + Assert.Equal(278, twitter.GetTextLengthRemain("🏃\u200D♀\uFE0F")); // ZWJ と異字体セレクタを含む絵文字 | |
635 | 636 | } |
636 | 637 | |
637 | 638 | [Fact] |
638 | 639 | public void GetTextLengthRemain_BrokenSurrogateTest() |
639 | 640 | { |
640 | - using (var twitter = new Twitter()) | |
641 | - { | |
642 | - // 投稿欄に IME から絵文字を入力すると HighSurrogate のみ入力された状態で TextChanged イベントが呼ばれることがある | |
643 | - Assert.Equal(278, twitter.GetTextLengthRemain("\ud83d")); | |
644 | - Assert.Equal(9999, twitter.GetTextLengthRemain("D twitter \ud83d")); | |
645 | - } | |
641 | + using var twitterApi = new TwitterApi(ApiKey.Create(""), ApiKey.Create("")); | |
642 | + using var twitter = new Twitter(twitterApi); | |
643 | + | |
644 | + // 投稿欄に IME から絵文字を入力すると HighSurrogate のみ入力された状態で TextChanged イベントが呼ばれることがある | |
645 | + Assert.Equal(278, twitter.GetTextLengthRemain("\ud83d")); | |
646 | + Assert.Equal(9999, twitter.GetTextLengthRemain("D twitter \ud83d")); | |
646 | 647 | } |
647 | 648 | } |
648 | 649 | } |
@@ -45,16 +45,23 @@ namespace OpenTween.Api | ||
45 | 45 | public string EndUserLoginName { get; set; } = ""; |
46 | 46 | public string EndUserApiKey { get; set; } = ""; |
47 | 47 | |
48 | + private readonly ApiKey clientId; | |
49 | + private readonly ApiKey clientSecret; | |
50 | + | |
48 | 51 | private HttpClient http => this.localHttpClient ?? Networking.Http; |
49 | 52 | private readonly HttpClient? localHttpClient; |
50 | 53 | |
51 | 54 | public BitlyApi() |
52 | - : this(null) | |
55 | + : this(ApplicationSettings.BitlyClientId, ApplicationSettings.BitlyClientSecret, null) | |
53 | 56 | { |
54 | 57 | } |
55 | 58 | |
56 | - public BitlyApi(HttpClient? http) | |
57 | - => this.localHttpClient = http; | |
59 | + public BitlyApi(ApiKey clientId, ApiKey clientSecret, HttpClient? http) | |
60 | + { | |
61 | + this.clientId = clientId; | |
62 | + this.clientSecret = clientSecret; | |
63 | + this.localHttpClient = http; | |
64 | + } | |
58 | 65 | |
59 | 66 | public async Task<Uri> ShortenAsync(Uri srcUri, string? domain = null) |
60 | 67 | { |
@@ -104,7 +111,12 @@ namespace OpenTween.Api | ||
104 | 111 | using var request = new HttpRequestMessage(HttpMethod.Post, endpoint); |
105 | 112 | using var postContent = new FormUrlEncodedContent(param); |
106 | 113 | |
107 | - var authzParam = ApplicationSettings.BitlyClientId + ":" + ApplicationSettings.BitlyClientSecret; | |
114 | + if (!(this.clientId, this.clientSecret).TryGetValue(out var keyPair)) | |
115 | + throw new WebApiException("bit.ly APIキーが使用できません"); | |
116 | + | |
117 | + var (clientId, clientSecret) = keyPair; | |
118 | + | |
119 | + var authzParam = clientId + ":" + clientSecret; | |
108 | 120 | request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes(authzParam))); |
109 | 121 | |
110 | 122 | request.Content = postContent; |
@@ -37,17 +37,17 @@ namespace OpenTween.Api | ||
37 | 37 | { |
38 | 38 | public class ImgurApi : IImgurApi |
39 | 39 | { |
40 | - private readonly string clientId; | |
40 | + private readonly ApiKey clientId; | |
41 | 41 | private readonly HttpClient http; |
42 | 42 | |
43 | 43 | public static readonly Uri UploadEndpoint = new Uri("https://api.imgur.com/3/image.xml"); |
44 | 44 | |
45 | 45 | public ImgurApi() |
46 | - : this(ApplicationSettings.ImgurClientID, null) | |
46 | + : this(ApplicationSettings.ImgurClientId, null) | |
47 | 47 | { |
48 | 48 | } |
49 | 49 | |
50 | - public ImgurApi(string clientId, HttpClient? http) | |
50 | + public ImgurApi(ApiKey clientId, HttpClient? http) | |
51 | 51 | { |
52 | 52 | this.clientId = clientId; |
53 | 53 |
@@ -97,6 +97,9 @@ namespace OpenTween.Api | ||
97 | 97 | |
98 | 98 | private async Task<HttpResponseMessage> SendRequestAsync(IMediaItem item, string title) |
99 | 99 | { |
100 | + if (!this.clientId.TryGetValue(out var clientId)) | |
101 | + throw new WebApiException("Err:imgur APIキーが使用できません"); | |
102 | + | |
100 | 103 | using var content = new MultipartFormDataContent(); |
101 | 104 | using var mediaStream = item.OpenRead(); |
102 | 105 | using var mediaContent = new StreamContent(mediaStream); |
@@ -106,7 +109,7 @@ namespace OpenTween.Api | ||
106 | 109 | content.Add(titleContent, "title"); |
107 | 110 | |
108 | 111 | using var request = new HttpRequestMessage(HttpMethod.Post, UploadEndpoint); |
109 | - request.Headers.Authorization = new AuthenticationHeaderValue("Client-ID", this.clientId); | |
112 | + request.Headers.Authorization = new AuthenticationHeaderValue("Client-ID", clientId); | |
110 | 113 | request.Content = content; |
111 | 114 | |
112 | 115 | return await this.http.SendAsync(request) |
@@ -44,19 +44,27 @@ namespace OpenTween.Api | ||
44 | 44 | public string AccessToken { get; internal set; } = ""; |
45 | 45 | public DateTimeUtc RefreshAccessTokenAt { get; internal set; } = DateTimeUtc.MinValue; |
46 | 46 | |
47 | + private readonly ApiKey subscriptionKey; | |
48 | + | |
47 | 49 | private HttpClient Http => this.localHttpClient ?? Networking.Http; |
48 | 50 | private readonly HttpClient? localHttpClient; |
49 | 51 | |
50 | 52 | public MicrosoftTranslatorApi() |
51 | - : this(null) | |
53 | + : this(ApplicationSettings.TranslatorSubscriptionKey, null) | |
52 | 54 | { |
53 | 55 | } |
54 | 56 | |
55 | - public MicrosoftTranslatorApi(HttpClient? http) | |
56 | - => this.localHttpClient = http; | |
57 | + public MicrosoftTranslatorApi(ApiKey subscriptionKey, HttpClient? http) | |
58 | + { | |
59 | + this.subscriptionKey = subscriptionKey; | |
60 | + this.localHttpClient = http; | |
61 | + } | |
57 | 62 | |
58 | 63 | public async Task<string> TranslateAsync(string text, string langTo, string? langFrom = null) |
59 | 64 | { |
65 | + if (!this.subscriptionKey.TryGetValue(out _)) | |
66 | + throw new WebApiException("APIキーが使用できません"); | |
67 | + | |
60 | 68 | await this.UpdateAccessTokenIfExpired() |
61 | 69 | .ConfigureAwait(false); |
62 | 70 |
@@ -83,7 +91,8 @@ namespace OpenTween.Api | ||
83 | 91 | using var response = await this.Http.SendAsync(request) |
84 | 92 | .ConfigureAwait(false); |
85 | 93 | |
86 | - response.EnsureSuccessStatusCode(); | |
94 | + if (!response.IsSuccessStatusCode) | |
95 | + throw new WebApiException(response.StatusCode.ToString()); | |
87 | 96 | |
88 | 97 | var responseJson = await response.Content.ReadAsByteArrayAsync() |
89 | 98 | .ConfigureAwait(false); |
@@ -112,7 +121,7 @@ namespace OpenTween.Api | ||
112 | 121 | internal virtual async Task<(string AccessToken, TimeSpan ExpiresIn)> GetAccessTokenAsync() |
113 | 122 | { |
114 | 123 | using var request = new HttpRequestMessage(HttpMethod.Post, IssueTokenEndpoint); |
115 | - request.Headers.Add("Ocp-Apim-Subscription-Key", ApplicationSettings.TranslatorSubscriptionKey); | |
124 | + request.Headers.Add("Ocp-Apim-Subscription-Key", this.subscriptionKey.Value); | |
116 | 125 | |
117 | 126 | using var response = await this.Http.SendAsync(request) |
118 | 127 | .ConfigureAwait(false); |
@@ -36,7 +36,7 @@ namespace OpenTween.Api | ||
36 | 36 | { |
37 | 37 | public class MobypictureApi : IMobypictureApi |
38 | 38 | { |
39 | - private readonly string apiKey; | |
39 | + private readonly ApiKey apiKey; | |
40 | 40 | private readonly HttpClient http; |
41 | 41 | |
42 | 42 | public static readonly Uri UploadEndpoint = new Uri("https://api.mobypicture.com/2.0/upload.xml"); |
@@ -49,7 +49,7 @@ namespace OpenTween.Api | ||
49 | 49 | { |
50 | 50 | } |
51 | 51 | |
52 | - public MobypictureApi(string apiKey, TwitterApi twitterApi) | |
52 | + public MobypictureApi(ApiKey apiKey, TwitterApi twitterApi) | |
53 | 53 | { |
54 | 54 | this.apiKey = apiKey; |
55 | 55 |
@@ -58,7 +58,7 @@ namespace OpenTween.Api | ||
58 | 58 | this.http.Timeout = Networking.UploadImageTimeout; |
59 | 59 | } |
60 | 60 | |
61 | - public MobypictureApi(string apiKey, HttpClient http) | |
61 | + public MobypictureApi(ApiKey apiKey, HttpClient http) | |
62 | 62 | { |
63 | 63 | this.apiKey = apiKey; |
64 | 64 | this.http = http; |
@@ -98,13 +98,16 @@ namespace OpenTween.Api | ||
98 | 98 | |
99 | 99 | private async Task<HttpResponseMessage> SendRequestAsync(IMediaItem item, string message) |
100 | 100 | { |
101 | + if (!this.apiKey.TryGetValue(out var apiKey)) | |
102 | + throw new WebApiException("Err:Mobypicture APIキーが使用できません"); | |
103 | + | |
101 | 104 | // 参照: http://developers.mobypicture.com/documentation/2-0/upload/ |
102 | 105 | |
103 | 106 | using var request = new HttpRequestMessage(HttpMethod.Post, UploadEndpoint); |
104 | 107 | using var multipart = new MultipartFormDataContent(); |
105 | 108 | request.Content = multipart; |
106 | 109 | |
107 | - using var apiKeyContent = new StringContent(this.apiKey); | |
110 | + using var apiKeyContent = new StringContent(apiKey); | |
108 | 111 | using var messageContent = new StringContent(message); |
109 | 112 | using var mediaStream = item.OpenRead(); |
110 | 113 | using var mediaContent = new StreamContent(mediaStream); |
@@ -42,9 +42,18 @@ namespace OpenTween.Api | ||
42 | 42 | |
43 | 43 | internal IApiConnection? apiConnection; |
44 | 44 | |
45 | + private readonly ApiKey consumerKey; | |
46 | + private readonly ApiKey consumerSecret; | |
47 | + | |
48 | + public TwitterApi(ApiKey consumerKey, ApiKey consumerSecret) | |
49 | + { | |
50 | + this.consumerKey = consumerKey; | |
51 | + this.consumerSecret = consumerSecret; | |
52 | + } | |
53 | + | |
45 | 54 | public void Initialize(string accessToken, string accessSecret, long userId, string screenName) |
46 | 55 | { |
47 | - var newInstance = new TwitterApiConnection(accessToken, accessSecret); | |
56 | + var newInstance = new TwitterApiConnection(this.consumerKey, this.consumerSecret, accessToken, accessSecret); | |
48 | 57 | var oldInstance = Interlocked.Exchange(ref this.apiConnection, newInstance); |
49 | 58 | oldInstance?.Dispose(); |
50 | 59 |
@@ -0,0 +1,240 @@ | ||
1 | +// OpenTween - Client of Twitter | |
2 | +// Copyright (c) 2022 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/> | |
3 | +// All rights reserved. | |
4 | +// | |
5 | +// This file is part of OpenTween. | |
6 | +// | |
7 | +// This program is free software; you can redistribute it and/or modify it | |
8 | +// under the terms of the GNU General public License as published by the Free | |
9 | +// Software Foundation; either version 3 of the License, or (at your option) | |
10 | +// any later version. | |
11 | +// | |
12 | +// This program is distributed in the hope that it will be useful, but | |
13 | +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General public License | |
15 | +// for more details. | |
16 | +// | |
17 | +// You should have received a copy of the GNU General public License along | |
18 | +// with this program. If not, see <http://www.gnu.org/licenses/>, or write to | |
19 | +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, | |
20 | +// Boston, MA 02110-1301, USA. | |
21 | + | |
22 | +#nullable enable | |
23 | + | |
24 | +using System; | |
25 | +using System.Collections.Generic; | |
26 | +using System.Diagnostics.CodeAnalysis; | |
27 | +using System.IO; | |
28 | +using System.Linq; | |
29 | +using System.Runtime.Serialization; | |
30 | +using System.Security.Cryptography; | |
31 | +using System.Text; | |
32 | +using System.Threading.Tasks; | |
33 | + | |
34 | +namespace OpenTween | |
35 | +{ | |
36 | + public class ApiKey | |
37 | + { | |
38 | + private static readonly string EncryptionPrefix = "%e%"; | |
39 | + private static readonly int SaltSize = 16; | |
40 | + private static readonly int KeySize = 32; | |
41 | + private static readonly int BlockSize = 16; | |
42 | + private static readonly int IterationCount = 10_000; | |
43 | + private static readonly HashAlgorithmName HashAlgorithm = HashAlgorithmName.SHA256; | |
44 | + | |
45 | + private readonly string rawKey; | |
46 | + private readonly Lazy<string> decryptLazy; | |
47 | + | |
48 | + /// <summary> | |
49 | + /// 平文の API キー | |
50 | + /// </summary> | |
51 | + /// <exception cref="ApiKeyDecryptException" /> | |
52 | + public string Value => this.decryptLazy.Value; | |
53 | + | |
54 | + private ApiKey(string password, string rawKey) | |
55 | + { | |
56 | + this.rawKey = rawKey; | |
57 | + this.decryptLazy = new Lazy<string>( | |
58 | + () => Decrypt(password, this.rawKey) | |
59 | + ); | |
60 | + } | |
61 | + | |
62 | + /// <summary> | |
63 | + /// 平文の API キーを返します | |
64 | + /// </summary> | |
65 | + /// <returns> | |
66 | + /// 成功した場合は true、暗号化された API キーの復号に失敗した場合は false を返します | |
67 | + /// </returns> | |
68 | + public bool TryGetValue([NotNullWhen(true)]out string output) | |
69 | + { | |
70 | + try | |
71 | + { | |
72 | + output = this.Value; | |
73 | + return true; | |
74 | + } | |
75 | + catch (ApiKeyDecryptException) | |
76 | + { | |
77 | + output = null!; | |
78 | + return false; | |
79 | + } | |
80 | + } | |
81 | + | |
82 | + /// <summary> | |
83 | + /// <see cref="ApiKey"/> インスタンスを作成します | |
84 | + /// </summary> | |
85 | + public static ApiKey Create(string rawKey) | |
86 | + => Create(ApplicationSettings.EncryptionPassword, rawKey); | |
87 | + | |
88 | + /// <summary> | |
89 | + /// <see cref="ApiKey"/> インスタンスを作成します | |
90 | + /// </summary> | |
91 | + public static ApiKey Create(string password, string rawKey) | |
92 | + => new ApiKey(password, rawKey); | |
93 | + | |
94 | + /// <summary> | |
95 | + /// 指定された文字列を暗号化して返します | |
96 | + /// </summary> | |
97 | + public static string Encrypt(string password, string plainText) | |
98 | + { | |
99 | + var salt = GenerateSalt(); | |
100 | + var (encryptionKey, iv, macKey) = GenerateKeyAndIV(password, salt); | |
101 | + | |
102 | + using var aes = CreateAes(); | |
103 | + aes.Key = encryptionKey; | |
104 | + aes.IV = iv; | |
105 | + | |
106 | + var plainBytes = Encoding.UTF8.GetBytes(plainText); | |
107 | + using var encryptor = aes.CreateEncryptor(); | |
108 | + using var memoryStream = new MemoryStream(plainBytes); | |
109 | + using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Read); | |
110 | + | |
111 | + using var cipherStream = new MemoryStream(capacity: plainBytes.Length + BlockSize + SaltSize); | |
112 | + cryptoStream.CopyTo(cipherStream); | |
113 | + | |
114 | + var cipherBytes = cipherStream.ToArray(); | |
115 | + var macBytes = GenerateMAC(plainBytes, macKey); | |
116 | + var saltText = Convert.ToBase64String(salt); | |
117 | + var cipherText = Convert.ToBase64String(cipherBytes); | |
118 | + var macText = Convert.ToBase64String(macBytes); | |
119 | + | |
120 | + return $"{EncryptionPrefix}{saltText}%{cipherText}%{macText}"; | |
121 | + } | |
122 | + | |
123 | + /// <summary> | |
124 | + /// 暗号化された文字列を復号します | |
125 | + /// </summary> | |
126 | + /// <exception cref="ApiKeyDecryptException" /> | |
127 | + public static string Decrypt(string password, string encryptedText) | |
128 | + { | |
129 | + // 先頭が "%e%" から始まっていない場合、APIキーは平文のまま書かれているものとして扱う | |
130 | + if (!encryptedText.StartsWith(EncryptionPrefix)) | |
131 | + return encryptedText; | |
132 | + | |
133 | + var splitted = encryptedText.Split('%'); | |
134 | + if (splitted.Length != 5) | |
135 | + throw new ApiKeyDecryptException("暗号文のフォーマットが不正です"); | |
136 | + | |
137 | + byte[] salt, cipherBytes, macBytes; | |
138 | + try | |
139 | + { | |
140 | + // e.g. "%e%...salt...%...cipher...%...mac..." | |
141 | + salt = Convert.FromBase64String(splitted[2]); | |
142 | + cipherBytes = Convert.FromBase64String(splitted[3]); | |
143 | + macBytes = Convert.FromBase64String(splitted[4]); | |
144 | + } | |
145 | + catch (FormatException ex) | |
146 | + { | |
147 | + throw new ApiKeyDecryptException("不正な Base64 フォーマットです", ex); | |
148 | + } | |
149 | + | |
150 | + var (encryptionKey, iv, macKey) = GenerateKeyAndIV(password, salt); | |
151 | + using var aes = CreateAes(); | |
152 | + aes.Key = encryptionKey; | |
153 | + aes.IV = iv; | |
154 | + | |
155 | + byte[] decryptedBytes; | |
156 | + try | |
157 | + { | |
158 | + using var decryptor = aes.CreateDecryptor(); | |
159 | + using var memoryStream = new MemoryStream(cipherBytes); | |
160 | + using var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read); | |
161 | + | |
162 | + using var decryptedStream = new MemoryStream(capacity: cipherBytes.Length); | |
163 | + cryptoStream.CopyTo(decryptedStream); | |
164 | + | |
165 | + decryptedBytes = decryptedStream.ToArray(); | |
166 | + } | |
167 | + catch (CryptographicException ex) | |
168 | + { | |
169 | + throw new ApiKeyDecryptException("API キーの復号に失敗しました", ex); | |
170 | + } | |
171 | + | |
172 | + var macBytesExpected = GenerateMAC(decryptedBytes, macKey); | |
173 | + | |
174 | + var isValid = macBytes.Length == macBytesExpected.Length && | |
175 | + Enumerable.Zip(macBytes, macBytesExpected, (x, y) => x == y).All(x => x); | |
176 | + if (!isValid) | |
177 | + throw new ApiKeyDecryptException("ダイジェストが一致しません"); | |
178 | + | |
179 | + return Encoding.UTF8.GetString(decryptedBytes); | |
180 | + } | |
181 | + | |
182 | + private static byte[] GenerateSalt() | |
183 | + { | |
184 | + using var random = new RNGCryptoServiceProvider(); | |
185 | + var salt = new byte[SaltSize]; | |
186 | + random.GetBytes(salt); | |
187 | + return salt; | |
188 | + } | |
189 | + | |
190 | + private static (byte[], byte[], byte[]) GenerateKeyAndIV(string password, byte[] salt) | |
191 | + { | |
192 | + using var generator = new Rfc2898DeriveBytes(password, salt, IterationCount, HashAlgorithm); | |
193 | + var encryptionKey = generator.GetBytes(KeySize); | |
194 | + var iv = generator.GetBytes(BlockSize); | |
195 | + var macKey = generator.GetBytes(KeySize); | |
196 | + return (encryptionKey, iv, macKey); | |
197 | + } | |
198 | + | |
199 | + private static byte[] GenerateMAC(byte[] source, byte[] key) | |
200 | + { | |
201 | + using var hmac = new HMACSHA256(key); | |
202 | + return hmac.ComputeHash(source); | |
203 | + } | |
204 | + | |
205 | + private static Aes CreateAes() | |
206 | + { | |
207 | + var aes = Aes.Create(); | |
208 | + aes.KeySize = KeySize * 8; | |
209 | + aes.BlockSize = BlockSize * 8; | |
210 | + aes.Mode = CipherMode.CBC; | |
211 | + aes.Padding = PaddingMode.PKCS7; | |
212 | + return aes; | |
213 | + } | |
214 | + } | |
215 | + | |
216 | + public static class ApiKeyExtensions | |
217 | + { | |
218 | + public static bool TryGetValue(this ValueTuple<ApiKey, ApiKey> apiKeys, out ValueTuple<string, string> decryptedKeys) | |
219 | + { | |
220 | + var (apiKey1, apiKey2) = apiKeys; | |
221 | + if (apiKey1.TryGetValue(out var decrypted1) && apiKey2.TryGetValue(out var decrypted2)) | |
222 | + { | |
223 | + decryptedKeys = (decrypted1, decrypted2); | |
224 | + return true; | |
225 | + } | |
226 | + | |
227 | + decryptedKeys = ("", ""); | |
228 | + return false; | |
229 | + } | |
230 | + } | |
231 | + | |
232 | + [Serializable] | |
233 | + public class ApiKeyDecryptException : Exception | |
234 | + { | |
235 | + public ApiKeyDecryptException() { } | |
236 | + public ApiKeyDecryptException(string message) : base(message) { } | |
237 | + public ApiKeyDecryptException(string message, Exception innerException) : base(message, innerException) { } | |
238 | + protected ApiKeyDecryptException(SerializationInfo info, StreamingContext context) : base(info, context) { } | |
239 | + } | |
240 | +} |
@@ -23,6 +23,7 @@ | ||
23 | 23 | |
24 | 24 | using System; |
25 | 25 | using System.Collections.Generic; |
26 | +using System.IO; | |
26 | 27 | using System.Linq; |
27 | 28 | using System.Text; |
28 | 29 | using System.Windows.Forms; |
@@ -105,14 +106,26 @@ namespace OpenTween | ||
105 | 106 | public static readonly string VersionInfoUrl = "https://www.opentween.org/status/version.txt"; |
106 | 107 | |
107 | 108 | //===================================================================== |
109 | + // 暗号化キー | |
110 | + | |
111 | + /// <summary> | |
112 | + /// APIキーの暗号化・復号に使用するパスワード | |
113 | + /// </summary> | |
114 | + public static readonly string EncryptionPassword = ApplicationName; | |
115 | + | |
116 | + //===================================================================== | |
108 | 117 | |
109 | - // https://dev.twitter.com/ から取得できます。 | |
118 | + // https://developer.twitter.com/ から取得できます。 | |
119 | + | |
120 | + /// <summary> | |
121 | + /// Twitter API Key | |
122 | + /// </summary> | |
123 | + public static readonly ApiKey TwitterConsumerKey = ApiKey.Create("zIoJPq3FsuViPTAs89FetDHYz"); | |
110 | 124 | |
111 | 125 | /// <summary> |
112 | - /// Twitter コンシューマーキー | |
126 | + /// Twitter API Key Secret | |
113 | 127 | /// </summary> |
114 | - public const string TwitterConsumerKey = "zIoJPq3FsuViPTAs89FetDHYz"; | |
115 | - public const string TwitterConsumerSecret = "prTAs2fqLv12nHxlMoLQZT8AkpZt0yYb8A7ktGS2VYeRj0TddS"; | |
128 | + public static readonly ApiKey TwitterConsumerSecret = ApiKey.Create("prTAs2fqLv12nHxlMoLQZT8AkpZt0yYb8A7ktGS2VYeRj0TddS"); | |
116 | 129 | |
117 | 130 | //===================================================================== |
118 | 131 | // Foursquare |
@@ -121,12 +134,12 @@ namespace OpenTween | ||
121 | 134 | /// <summary> |
122 | 135 | /// Foursquare Client Id |
123 | 136 | /// </summary> |
124 | - public const string FoursquareClientId = "5H3K5YQPT55DNQUFEOAJFNJA5D01ZJGO2ITEAJ3ASRIDONUB"; | |
137 | + public static readonly ApiKey FoursquareClientId = ApiKey.Create("5H3K5YQPT55DNQUFEOAJFNJA5D01ZJGO2ITEAJ3ASRIDONUB"); | |
125 | 138 | |
126 | 139 | /// <summary> |
127 | 140 | /// Foursquare Client Secret |
128 | 141 | /// </summary> |
129 | - public const string FoursquareClientSecret = "JFRHP1L451M3AEPF11UZLTIIUZCZTZRVHVOWB5TQ0AJOVPBB"; | |
142 | + public static readonly ApiKey FoursquareClientSecret = ApiKey.Create("JFRHP1L451M3AEPF11UZLTIIUZCZTZRVHVOWB5TQ0AJOVPBB"); | |
130 | 143 | |
131 | 144 | //===================================================================== |
132 | 145 | // bit.ly |
@@ -135,12 +148,12 @@ namespace OpenTween | ||
135 | 148 | /// <summary> |
136 | 149 | /// bit.ly Client ID |
137 | 150 | /// </summary> |
138 | - public const string BitlyClientId = "ddab8ec50f4459c315cbde9d923cf490923b6d2e"; | |
151 | + public static readonly ApiKey BitlyClientId = ApiKey.Create("ddab8ec50f4459c315cbde9d923cf490923b6d2e"); | |
139 | 152 | |
140 | 153 | /// <summary> |
141 | 154 | /// bit.ly Client Secret |
142 | 155 | /// </summary> |
143 | - public const string BitlyClientSecret = "485c9d03dd264f8eeb4fc65d38e2762c4420cee7"; | |
156 | + public static readonly ApiKey BitlyClientSecret = ApiKey.Create("485c9d03dd264f8eeb4fc65d38e2762c4420cee7"); | |
144 | 157 | |
145 | 158 | //===================================================================== |
146 | 159 | // TINAMI |
@@ -149,7 +162,7 @@ namespace OpenTween | ||
149 | 162 | /// <summary> |
150 | 163 | /// TINAMI APIキー |
151 | 164 | /// </summary> |
152 | - public const string TINAMIApiKey = "4f48bb4858d36"; | |
165 | + public static readonly ApiKey TINAMIApiKey = ApiKey.Create("4f48bb4858d36"); | |
153 | 166 | |
154 | 167 | //===================================================================== |
155 | 168 | // Microsoft Translator API (Cognitive Service) |
@@ -158,7 +171,7 @@ namespace OpenTween | ||
158 | 171 | /// <summary> |
159 | 172 | /// Translator Text API Subscription Key |
160 | 173 | /// </summary> |
161 | - public readonly static string TranslatorSubscriptionKey = "6c47d2ea341148bf856bdbfafd429db7"; | |
174 | + public static readonly ApiKey TranslatorSubscriptionKey = ApiKey.Create("6c47d2ea341148bf856bdbfafd429db7"); | |
162 | 175 | |
163 | 176 | //===================================================================== |
164 | 177 | // Imgur |
@@ -167,12 +180,12 @@ namespace OpenTween | ||
167 | 180 | /// <summary> |
168 | 181 | /// Imgur Client ID |
169 | 182 | /// </summary> |
170 | - public readonly static string ImgurClientID = "a5fff36fb83568c"; | |
183 | + public static readonly ApiKey ImgurClientId = ApiKey.Create("a5fff36fb83568c"); | |
171 | 184 | |
172 | 185 | /// <summary> |
173 | 186 | /// Imgur Client Secret |
174 | 187 | /// </summary> |
175 | - public readonly static string ImgurClientSecret = "af5d668a9aa83b34a8f0f735e12073edafbc9a5d"; | |
188 | + public static readonly ApiKey ImgurClientSecret = ApiKey.Create("af5d668a9aa83b34a8f0f735e12073edafbc9a5d"); | |
176 | 189 | |
177 | 190 | //===================================================================== |
178 | 191 | // Mobypicture |
@@ -181,7 +194,7 @@ namespace OpenTween | ||
181 | 194 | /// <summary> |
182 | 195 | /// Mobypicture Developer Key |
183 | 196 | /// </summary> |
184 | - public readonly static string MobypictureKey = "quPWTX0UrPHxqdH7"; | |
197 | + public static readonly ApiKey MobypictureKey = ApiKey.Create("quPWTX0UrPHxqdH7"); | |
185 | 198 | |
186 | 199 | //===================================================================== |
187 | 200 | // Tumblr |
@@ -190,6 +203,6 @@ namespace OpenTween | ||
190 | 203 | /// <summary> |
191 | 204 | /// Tumblr OAuth Consumer Key |
192 | 205 | /// </summary> |
193 | - public readonly static string TumblrConsumerKey = "Nsk62V6wMIqVNbiGyN0g3aDGBlgU7Fcb9GJ8Se0z2MUDHAY15l"; | |
206 | + public static readonly ApiKey TumblrConsumerKey = ApiKey.Create("Nsk62V6wMIqVNbiGyN0g3aDGBlgU7Fcb9GJ8Se0z2MUDHAY15l"); | |
194 | 207 | } |
195 | 208 | } |
@@ -170,12 +170,7 @@ namespace OpenTween | ||
170 | 170 | private readonly MicrosoftTranslatorApi translatorApi; |
171 | 171 | |
172 | 172 | public Bing() |
173 | - : this(null) | |
174 | - { | |
175 | - } | |
176 | - | |
177 | - public Bing(HttpClient? http) | |
178 | - => this.translatorApi = new MicrosoftTranslatorApi(http); | |
173 | + => this.translatorApi = new MicrosoftTranslatorApi(); | |
179 | 174 | |
180 | 175 | /// <summary> |
181 | 176 | /// Microsoft Translator API を使用した翻訳を非同期に行います |
@@ -53,7 +53,7 @@ namespace OpenTween.Connection | ||
53 | 53 | } |
54 | 54 | |
55 | 55 | public static OAuthEchoHandler CreateHandler(HttpMessageHandler innerHandler, Uri authServiceProvider, |
56 | - string consumerKey, string consumerSecret, string accessToken, string accessSecret, Uri? realm = null) | |
56 | + ApiKey consumerKey, ApiKey consumerSecret, string accessToken, string accessSecret, Uri? realm = null) | |
57 | 57 | { |
58 | 58 | var credential = OAuthUtility.CreateAuthorization("GET", authServiceProvider, null, |
59 | 59 | consumerKey, consumerSecret, accessToken, accessSecret, realm?.AbsoluteUri); |
@@ -38,12 +38,12 @@ namespace OpenTween.Connection | ||
38 | 38 | /// </summary> |
39 | 39 | public class OAuthHandler : DelegatingHandler |
40 | 40 | { |
41 | - public string ConsumerKey { get; } | |
42 | - public string ConsumerSecret { get; } | |
41 | + public ApiKey ConsumerKey { get; } | |
42 | + public ApiKey ConsumerSecret { get; } | |
43 | 43 | public string AccessToken { get; } |
44 | 44 | public string AccessSecret { get; } |
45 | 45 | |
46 | - public OAuthHandler(HttpMessageHandler innerHandler, string consumerKey, string consumerSecret, string accessToken, string accessSecret) | |
46 | + public OAuthHandler(HttpMessageHandler innerHandler, ApiKey consumerKey, ApiKey consumerSecret, string accessToken, string accessSecret) | |
47 | 47 | : base(innerHandler) |
48 | 48 | { |
49 | 49 | this.ConsumerKey = consumerKey; |
@@ -56,7 +56,7 @@ namespace OpenTween.Connection | ||
56 | 56 | /// <param name="tokenSecret">アクセストークンシークレット。認証処理では空文字列</param> |
57 | 57 | /// <param name="realm">realm (必要な場合のみ)</param> |
58 | 58 | public static string CreateAuthorization(string httpMethod, Uri requestUri, IEnumerable<KeyValuePair<string, string>>? query, |
59 | - string consumerKey, string consumerSecret, string token, string tokenSecret, | |
59 | + ApiKey consumerKey, ApiKey consumerSecret, string token, string tokenSecret, | |
60 | 60 | string? realm = null) |
61 | 61 | { |
62 | 62 | // OAuth共通情報取得 |
@@ -86,11 +86,11 @@ namespace OpenTween.Connection | ||
86 | 86 | /// </summary> |
87 | 87 | /// <param name="token">アクセストークン、もしくはリクエストトークン。未取得なら空文字列</param> |
88 | 88 | /// <returns>OAuth情報のディクショナリ</returns> |
89 | - public static Dictionary<string, string> GetOAuthParameter(string consumerKey, string token) | |
89 | + public static Dictionary<string, string> GetOAuthParameter(ApiKey consumerKey, string token) | |
90 | 90 | { |
91 | 91 | var parameter = new Dictionary<string, string> |
92 | 92 | { |
93 | - ["oauth_consumer_key"] = consumerKey, | |
93 | + ["oauth_consumer_key"] = consumerKey.Value, | |
94 | 94 | ["oauth_signature_method"] = "HMAC-SHA1", |
95 | 95 | ["oauth_timestamp"] = DateTimeUtc.Now.ToUnixTime().ToString(), // epoch秒 |
96 | 96 | ["oauth_nonce"] = NonceRandom.Next(123400, 9999999).ToString(), |
@@ -109,7 +109,7 @@ namespace OpenTween.Connection | ||
109 | 109 | /// <param name="uri">アクセス先Uri</param> |
110 | 110 | /// <param name="parameter">クエリ、もしくはPOSTデータ</param> |
111 | 111 | /// <returns>署名文字列</returns> |
112 | - public static string CreateSignature(string consumerSecret, string? tokenSecret, string method, Uri uri, Dictionary<string, string> parameter) | |
112 | + public static string CreateSignature(ApiKey consumerSecret, string? tokenSecret, string method, Uri uri, Dictionary<string, string> parameter) | |
113 | 113 | { |
114 | 114 | // パラメタをソート済みディクショナリに詰替(OAuthの仕様) |
115 | 115 | var sorted = new SortedDictionary<string, string>(parameter); |
@@ -120,7 +120,7 @@ namespace OpenTween.Connection | ||
120 | 120 | // 署名のベース文字列生成(&区切り)。クエリ形式文字列は再エンコードする |
121 | 121 | var signatureBase = string.Format("{0}&{1}&{2}", method, MyCommon.UrlEncode(url), MyCommon.UrlEncode(paramString)); |
122 | 122 | // 署名鍵の文字列をコンシューマー秘密鍵とアクセストークン秘密鍵から生成(&区切り。アクセストークン秘密鍵なくても&残すこと) |
123 | - var key = MyCommon.UrlEncode(consumerSecret) + "&"; | |
123 | + var key = MyCommon.UrlEncode(consumerSecret.Value) + "&"; | |
124 | 124 | if (!MyCommon.IsNullOrEmpty(tokenSecret)) |
125 | 125 | key += MyCommon.UrlEncode(tokenSecret); |
126 | 126 | // 鍵生成&署名生成 |
@@ -58,8 +58,13 @@ namespace OpenTween.Connection | ||
58 | 58 | internal HttpClient httpUpload = null!; |
59 | 59 | internal HttpClient httpStreaming = null!; |
60 | 60 | |
61 | - public TwitterApiConnection(string accessToken, string accessSecret) | |
61 | + private readonly ApiKey consumerKey; | |
62 | + private readonly ApiKey consumerSecret; | |
63 | + | |
64 | + public TwitterApiConnection(ApiKey consumerKey, ApiKey consumerSecret, string accessToken, string accessSecret) | |
62 | 65 | { |
66 | + this.consumerKey = consumerKey; | |
67 | + this.consumerSecret = consumerSecret; | |
63 | 68 | this.AccessToken = accessToken; |
64 | 69 | this.AccessSecret = accessSecret; |
65 | 70 |
@@ -69,12 +74,12 @@ namespace OpenTween.Connection | ||
69 | 74 | |
70 | 75 | private void InitializeHttpClients() |
71 | 76 | { |
72 | - this.http = InitializeHttpClient(this.AccessToken, this.AccessSecret); | |
77 | + this.http = InitializeHttpClient(this.consumerKey, this.consumerSecret, this.AccessToken, this.AccessSecret); | |
73 | 78 | |
74 | - this.httpUpload = InitializeHttpClient(this.AccessToken, this.AccessSecret); | |
79 | + this.httpUpload = InitializeHttpClient(this.consumerKey, this.consumerSecret, this.AccessToken, this.AccessSecret); | |
75 | 80 | this.httpUpload.Timeout = Networking.UploadImageTimeout; |
76 | 81 | |
77 | - this.httpStreaming = InitializeHttpClient(this.AccessToken, this.AccessSecret, disableGzip: true); | |
82 | + this.httpStreaming = InitializeHttpClient(this.consumerKey, this.consumerSecret, this.AccessToken, this.AccessSecret, disableGzip: true); | |
78 | 83 | this.httpStreaming.Timeout = Timeout.InfiniteTimeSpan; |
79 | 84 | } |
80 | 85 |
@@ -433,8 +438,7 @@ namespace OpenTween.Connection | ||
433 | 438 | var uri = new Uri(RestApiBase, authServiceProvider); |
434 | 439 | |
435 | 440 | return OAuthEchoHandler.CreateHandler(Networking.CreateHttpClientHandler(), uri, |
436 | - ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret, | |
437 | - this.AccessToken, this.AccessSecret, realm); | |
441 | + this.consumerKey, this.consumerSecret, this.AccessToken, this.AccessSecret, realm); | |
438 | 442 | } |
439 | 443 | |
440 | 444 | public void Dispose() |
@@ -465,13 +469,16 @@ namespace OpenTween.Connection | ||
465 | 469 | private void Networking_WebProxyChanged(object sender, EventArgs e) |
466 | 470 | => this.InitializeHttpClients(); |
467 | 471 | |
468 | - public static async Task<(string Token, string TokenSecret)> GetRequestTokenAsync() | |
472 | + public static Task<(string Token, string TokenSecret)> GetRequestTokenAsync() | |
473 | + => GetRequestTokenAsync(ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret); | |
474 | + | |
475 | + public static async Task<(string Token, string TokenSecret)> GetRequestTokenAsync(ApiKey consumerKey, ApiKey consumerSecret) | |
469 | 476 | { |
470 | 477 | var param = new Dictionary<string, string> |
471 | 478 | { |
472 | 479 | ["oauth_callback"] = "oob", |
473 | 480 | }; |
474 | - var response = await GetOAuthTokenAsync(new Uri("https://api.twitter.com/oauth/request_token"), param, oauthToken: null) | |
481 | + var response = await GetOAuthTokenAsync(new Uri("https://api.twitter.com/oauth/request_token"), param, consumerKey, consumerSecret, oauthToken: null) | |
475 | 482 | .ConfigureAwait(false); |
476 | 483 | |
477 | 484 | return (response["oauth_token"], response["oauth_token_secret"]); |
@@ -490,26 +497,29 @@ namespace OpenTween.Connection | ||
490 | 497 | return new Uri("https://api.twitter.com/oauth/authorize?" + MyCommon.BuildQueryString(param)); |
491 | 498 | } |
492 | 499 | |
493 | - public static async Task<IDictionary<string, string>> GetAccessTokenAsync((string Token, string TokenSecret) requestToken, string verifier) | |
500 | + public static Task<IDictionary<string, string>> GetAccessTokenAsync((string Token, string TokenSecret) requestToken, string verifier) | |
501 | + => GetAccessTokenAsync(ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret, requestToken, verifier); | |
502 | + | |
503 | + public static async Task<IDictionary<string, string>> GetAccessTokenAsync(ApiKey consumerKey, ApiKey consumerSecret, (string Token, string TokenSecret) requestToken, string verifier) | |
494 | 504 | { |
495 | 505 | var param = new Dictionary<string, string> |
496 | 506 | { |
497 | 507 | ["oauth_verifier"] = verifier, |
498 | 508 | }; |
499 | - var response = await GetOAuthTokenAsync(new Uri("https://api.twitter.com/oauth/access_token"), param, requestToken) | |
509 | + var response = await GetOAuthTokenAsync(new Uri("https://api.twitter.com/oauth/access_token"), param, consumerKey, consumerSecret, requestToken) | |
500 | 510 | .ConfigureAwait(false); |
501 | 511 | |
502 | 512 | return response; |
503 | 513 | } |
504 | 514 | |
505 | 515 | private static async Task<IDictionary<string, string>> GetOAuthTokenAsync(Uri uri, IDictionary<string, string> param, |
506 | - (string Token, string TokenSecret)? oauthToken) | |
516 | + ApiKey consumerKey, ApiKey consumerSecret, (string Token, string TokenSecret)? oauthToken) | |
507 | 517 | { |
508 | 518 | HttpClient authorizeClient; |
509 | 519 | if (oauthToken != null) |
510 | - authorizeClient = InitializeHttpClient(oauthToken.Value.Token, oauthToken.Value.TokenSecret); | |
520 | + authorizeClient = InitializeHttpClient(consumerKey, consumerSecret, oauthToken.Value.Token, oauthToken.Value.TokenSecret); | |
511 | 521 | else |
512 | - authorizeClient = InitializeHttpClient("", ""); | |
522 | + authorizeClient = InitializeHttpClient(consumerKey, consumerSecret, "", ""); | |
513 | 523 | |
514 | 524 | var requestUri = new Uri(uri, "?" + MyCommon.BuildQueryString(param)); |
515 | 525 |
@@ -541,7 +551,7 @@ namespace OpenTween.Connection | ||
541 | 551 | } |
542 | 552 | } |
543 | 553 | |
544 | - private static HttpClient InitializeHttpClient(string accessToken, string accessSecret, bool disableGzip = false) | |
554 | + private static HttpClient InitializeHttpClient(ApiKey consumerKey, ApiKey consumerSecret, string accessToken, string accessSecret, bool disableGzip = false) | |
545 | 555 | { |
546 | 556 | var innerHandler = Networking.CreateHttpClientHandler(); |
547 | 557 | innerHandler.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache); |
@@ -549,9 +559,7 @@ namespace OpenTween.Connection | ||
549 | 559 | if (disableGzip) |
550 | 560 | innerHandler.AutomaticDecompression = DecompressionMethods.None; |
551 | 561 | |
552 | - var handler = new OAuthHandler(innerHandler, | |
553 | - ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret, | |
554 | - accessToken, accessSecret); | |
562 | + var handler = new OAuthHandler(innerHandler, consumerKey, consumerSecret, accessToken, accessSecret); | |
555 | 563 | |
556 | 564 | return Networking.CreateHttpClient(handler); |
557 | 565 | } |
@@ -0,0 +1,131 @@ | ||
1 | +namespace OpenTween | |
2 | +{ | |
3 | + partial class EncryptApiKeyDialog | |
4 | + { | |
5 | + /// <summary> | |
6 | + /// Required designer variable. | |
7 | + /// </summary> | |
8 | + private System.ComponentModel.IContainer components = null; | |
9 | + | |
10 | + /// <summary> | |
11 | + /// Clean up any resources being used. | |
12 | + /// </summary> | |
13 | + /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> | |
14 | + protected override void Dispose(bool disposing) | |
15 | + { | |
16 | + if (disposing && (components != null)) | |
17 | + { | |
18 | + components.Dispose(); | |
19 | + } | |
20 | + base.Dispose(disposing); | |
21 | + } | |
22 | + | |
23 | + #region Windows Form Designer generated code | |
24 | + | |
25 | + /// <summary> | |
26 | + /// Required method for Designer support - do not modify | |
27 | + /// the contents of this method with the code editor. | |
28 | + /// </summary> | |
29 | + private void InitializeComponent() | |
30 | + { | |
31 | + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(EncryptApiKeyDialog)); | |
32 | + this.label1 = new System.Windows.Forms.Label(); | |
33 | + this.textBoxPlainText = new System.Windows.Forms.TextBox(); | |
34 | + this.textBoxPassword = new System.Windows.Forms.TextBox(); | |
35 | + this.textBoxEncryptedText = new System.Windows.Forms.TextBox(); | |
36 | + this.label2 = new System.Windows.Forms.Label(); | |
37 | + this.label3 = new System.Windows.Forms.Label(); | |
38 | + this.buttonEncrypt = new System.Windows.Forms.Button(); | |
39 | + this.buttonClose = new System.Windows.Forms.Button(); | |
40 | + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); | |
41 | + this.tableLayoutPanel1.SuspendLayout(); | |
42 | + this.SuspendLayout(); | |
43 | + // | |
44 | + // label1 | |
45 | + // | |
46 | + resources.ApplyResources(this.label1, "label1"); | |
47 | + this.label1.Name = "label1"; | |
48 | + // | |
49 | + // textBoxPlainText | |
50 | + // | |
51 | + resources.ApplyResources(this.textBoxPlainText, "textBoxPlainText"); | |
52 | + this.textBoxPlainText.Name = "textBoxPlainText"; | |
53 | + // | |
54 | + // textBoxPassword | |
55 | + // | |
56 | + resources.ApplyResources(this.textBoxPassword, "textBoxPassword"); | |
57 | + this.textBoxPassword.Name = "textBoxPassword"; | |
58 | + this.textBoxPassword.ReadOnly = true; | |
59 | + // | |
60 | + // textBoxEncryptedText | |
61 | + // | |
62 | + resources.ApplyResources(this.textBoxEncryptedText, "textBoxEncryptedText"); | |
63 | + this.textBoxEncryptedText.Name = "textBoxEncryptedText"; | |
64 | + // | |
65 | + // label2 | |
66 | + // | |
67 | + resources.ApplyResources(this.label2, "label2"); | |
68 | + this.label2.Name = "label2"; | |
69 | + // | |
70 | + // label3 | |
71 | + // | |
72 | + resources.ApplyResources(this.label3, "label3"); | |
73 | + this.label3.Name = "label3"; | |
74 | + // | |
75 | + // buttonEncrypt | |
76 | + // | |
77 | + resources.ApplyResources(this.buttonEncrypt, "buttonEncrypt"); | |
78 | + this.tableLayoutPanel1.SetColumnSpan(this.buttonEncrypt, 2); | |
79 | + this.buttonEncrypt.Name = "buttonEncrypt"; | |
80 | + this.buttonEncrypt.UseVisualStyleBackColor = true; | |
81 | + this.buttonEncrypt.Click += new System.EventHandler(this.ButtonEncrypt_Click); | |
82 | + // | |
83 | + // buttonClose | |
84 | + // | |
85 | + resources.ApplyResources(this.buttonClose, "buttonClose"); | |
86 | + this.buttonClose.DialogResult = System.Windows.Forms.DialogResult.Cancel; | |
87 | + this.buttonClose.Name = "buttonClose"; | |
88 | + this.buttonClose.UseVisualStyleBackColor = true; | |
89 | + // | |
90 | + // tableLayoutPanel1 | |
91 | + // | |
92 | + resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); | |
93 | + this.tableLayoutPanel1.Controls.Add(this.label2, 0, 0); | |
94 | + this.tableLayoutPanel1.Controls.Add(this.buttonClose, 1, 4); | |
95 | + this.tableLayoutPanel1.Controls.Add(this.textBoxPlainText, 1, 0); | |
96 | + this.tableLayoutPanel1.Controls.Add(this.textBoxEncryptedText, 1, 3); | |
97 | + this.tableLayoutPanel1.Controls.Add(this.label3, 0, 3); | |
98 | + this.tableLayoutPanel1.Controls.Add(this.buttonEncrypt, 0, 2); | |
99 | + this.tableLayoutPanel1.Controls.Add(this.label1, 0, 1); | |
100 | + this.tableLayoutPanel1.Controls.Add(this.textBoxPassword, 1, 1); | |
101 | + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; | |
102 | + // | |
103 | + // EncryptApiKeyDialog | |
104 | + // | |
105 | + resources.ApplyResources(this, "$this"); | |
106 | + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; | |
107 | + this.CancelButton = this.buttonClose; | |
108 | + this.Controls.Add(this.tableLayoutPanel1); | |
109 | + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; | |
110 | + this.MaximizeBox = false; | |
111 | + this.Name = "EncryptApiKeyDialog"; | |
112 | + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; | |
113 | + this.tableLayoutPanel1.ResumeLayout(false); | |
114 | + this.tableLayoutPanel1.PerformLayout(); | |
115 | + this.ResumeLayout(false); | |
116 | + | |
117 | + } | |
118 | + | |
119 | + #endregion | |
120 | + | |
121 | + private System.Windows.Forms.Label label1; | |
122 | + private System.Windows.Forms.TextBox textBoxPlainText; | |
123 | + private System.Windows.Forms.TextBox textBoxPassword; | |
124 | + private System.Windows.Forms.TextBox textBoxEncryptedText; | |
125 | + private System.Windows.Forms.Label label2; | |
126 | + private System.Windows.Forms.Label label3; | |
127 | + private System.Windows.Forms.Button buttonEncrypt; | |
128 | + private System.Windows.Forms.Button buttonClose; | |
129 | + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; | |
130 | + } | |
131 | +} | |
\ No newline at end of file |
@@ -0,0 +1,49 @@ | ||
1 | +// OpenTween - Client of Twitter | |
2 | +// Copyright (c) 2022 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/> | |
3 | +// All rights reserved. | |
4 | +// | |
5 | +// This file is part of OpenTween. | |
6 | +// | |
7 | +// This program is free software; you can redistribute it and/or modify it | |
8 | +// under the terms of the GNU General public License as published by the Free | |
9 | +// Software Foundation; either version 3 of the License, or (at your option) | |
10 | +// any later version. | |
11 | +// | |
12 | +// This program is distributed in the hope that it will be useful, but | |
13 | +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General public License | |
15 | +// for more details. | |
16 | +// | |
17 | +// You should have received a copy of the GNU General public License along | |
18 | +// with this program. If not, see <http://www.gnu.org/licenses/>, or write to | |
19 | +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, | |
20 | +// Boston, MA 02110-1301, USA. | |
21 | + | |
22 | +#nullable enable | |
23 | + | |
24 | +using System; | |
25 | +using System.Collections.Generic; | |
26 | +using System.ComponentModel; | |
27 | +using System.Data; | |
28 | +using System.Drawing; | |
29 | +using System.Linq; | |
30 | +using System.Text; | |
31 | +using System.Threading.Tasks; | |
32 | +using System.Windows.Forms; | |
33 | + | |
34 | +namespace OpenTween | |
35 | +{ | |
36 | + public partial class EncryptApiKeyDialog : OTBaseForm | |
37 | + { | |
38 | + public EncryptApiKeyDialog() | |
39 | + { | |
40 | + this.InitializeComponent(); | |
41 | + this.textBoxPassword.Text = ApplicationSettings.EncryptionPassword; | |
42 | + } | |
43 | + | |
44 | + private void ButtonEncrypt_Click(object sender, EventArgs e) | |
45 | + { | |
46 | + this.textBoxEncryptedText.Text = ApiKey.Encrypt(ApplicationSettings.EncryptionPassword, this.textBoxPlainText.Text); | |
47 | + } | |
48 | + } | |
49 | +} |
@@ -0,0 +1,426 @@ | ||
1 | +<?xml version="1.0" encoding="utf-8"?> | |
2 | +<root> | |
3 | + <!-- | |
4 | + Microsoft ResX Schema | |
5 | + | |
6 | + Version 2.0 | |
7 | + | |
8 | + The primary goals of this format is to allow a simple XML format | |
9 | + that is mostly human readable. The generation and parsing of the | |
10 | + various data types are done through the TypeConverter classes | |
11 | + associated with the data types. | |
12 | + | |
13 | + Example: | |
14 | + | |
15 | + ... ado.net/XML headers & schema ... | |
16 | + <resheader name="resmimetype">text/microsoft-resx</resheader> | |
17 | + <resheader name="version">2.0</resheader> | |
18 | + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> | |
19 | + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> | |
20 | + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> | |
21 | + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> | |
22 | + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> | |
23 | + <value>[base64 mime encoded serialized .NET Framework object]</value> | |
24 | + </data> | |
25 | + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> | |
26 | + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> | |
27 | + <comment>This is a comment</comment> | |
28 | + </data> | |
29 | + | |
30 | + There are any number of "resheader" rows that contain simple | |
31 | + name/value pairs. | |
32 | + | |
33 | + Each data row contains a name, and value. The row also contains a | |
34 | + type or mimetype. Type corresponds to a .NET class that support | |
35 | + text/value conversion through the TypeConverter architecture. | |
36 | + Classes that don't support this are serialized and stored with the | |
37 | + mimetype set. | |
38 | + | |
39 | + The mimetype is used for serialized objects, and tells the | |
40 | + ResXResourceReader how to depersist the object. This is currently not | |
41 | + extensible. For a given mimetype the value must be set accordingly: | |
42 | + | |
43 | + Note - application/x-microsoft.net.object.binary.base64 is the format | |
44 | + that the ResXResourceWriter will generate, however the reader can | |
45 | + read any of the formats listed below. | |
46 | + | |
47 | + mimetype: application/x-microsoft.net.object.binary.base64 | |
48 | + value : The object must be serialized with | |
49 | + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter | |
50 | + : and then encoded with base64 encoding. | |
51 | + | |
52 | + mimetype: application/x-microsoft.net.object.soap.base64 | |
53 | + value : The object must be serialized with | |
54 | + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter | |
55 | + : and then encoded with base64 encoding. | |
56 | + | |
57 | + mimetype: application/x-microsoft.net.object.bytearray.base64 | |
58 | + value : The object must be serialized into a byte array | |
59 | + : using a System.ComponentModel.TypeConverter | |
60 | + : and then encoded with base64 encoding. | |
61 | + --> | |
62 | + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> | |
63 | + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> | |
64 | + <xsd:element name="root" msdata:IsDataSet="true"> | |
65 | + <xsd:complexType> | |
66 | + <xsd:choice maxOccurs="unbounded"> | |
67 | + <xsd:element name="metadata"> | |
68 | + <xsd:complexType> | |
69 | + <xsd:sequence> | |
70 | + <xsd:element name="value" type="xsd:string" minOccurs="0" /> | |
71 | + </xsd:sequence> | |
72 | + <xsd:attribute name="name" use="required" type="xsd:string" /> | |
73 | + <xsd:attribute name="type" type="xsd:string" /> | |
74 | + <xsd:attribute name="mimetype" type="xsd:string" /> | |
75 | + <xsd:attribute ref="xml:space" /> | |
76 | + </xsd:complexType> | |
77 | + </xsd:element> | |
78 | + <xsd:element name="assembly"> | |
79 | + <xsd:complexType> | |
80 | + <xsd:attribute name="alias" type="xsd:string" /> | |
81 | + <xsd:attribute name="name" type="xsd:string" /> | |
82 | + </xsd:complexType> | |
83 | + </xsd:element> | |
84 | + <xsd:element name="data"> | |
85 | + <xsd:complexType> | |
86 | + <xsd:sequence> | |
87 | + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |
88 | + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> | |
89 | + </xsd:sequence> | |
90 | + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> | |
91 | + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> | |
92 | + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> | |
93 | + <xsd:attribute ref="xml:space" /> | |
94 | + </xsd:complexType> | |
95 | + </xsd:element> | |
96 | + <xsd:element name="resheader"> | |
97 | + <xsd:complexType> | |
98 | + <xsd:sequence> | |
99 | + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |
100 | + </xsd:sequence> | |
101 | + <xsd:attribute name="name" type="xsd:string" use="required" /> | |
102 | + </xsd:complexType> | |
103 | + </xsd:element> | |
104 | + </xsd:choice> | |
105 | + </xsd:complexType> | |
106 | + </xsd:element> | |
107 | + </xsd:schema> | |
108 | + <resheader name="resmimetype"> | |
109 | + <value>text/microsoft-resx</value> | |
110 | + </resheader> | |
111 | + <resheader name="version"> | |
112 | + <value>2.0</value> | |
113 | + </resheader> | |
114 | + <resheader name="reader"> | |
115 | + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |
116 | + </resheader> | |
117 | + <resheader name="writer"> | |
118 | + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |
119 | + </resheader> | |
120 | + <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> | |
121 | + <data name="label1.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms"> | |
122 | + <value>Left, Right</value> | |
123 | + </data> | |
124 | + <assembly alias="mscorlib" name="mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> | |
125 | + <data name="label1.AutoSize" type="System.Boolean, mscorlib"> | |
126 | + <value>True</value> | |
127 | + </data> | |
128 | + <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> | |
129 | + <data name="label1.Location" type="System.Drawing.Point, System.Drawing"> | |
130 | + <value>3, 33</value> | |
131 | + </data> | |
132 | + <data name="label1.Size" type="System.Drawing.Size, System.Drawing"> | |
133 | + <value>105, 12</value> | |
134 | + </data> | |
135 | + <data name="label1.TabIndex" type="System.Int32, mscorlib"> | |
136 | + <value>2</value> | |
137 | + </data> | |
138 | + <data name="label1.Text" xml:space="preserve"> | |
139 | + <value>暗号化パスワード</value> | |
140 | + </data> | |
141 | + <data name=">>label1.Name" xml:space="preserve"> | |
142 | + <value>label1</value> | |
143 | + </data> | |
144 | + <data name=">>label1.Type" xml:space="preserve"> | |
145 | + <value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |
146 | + </data> | |
147 | + <data name=">>label1.Parent" xml:space="preserve"> | |
148 | + <value>tableLayoutPanel1</value> | |
149 | + </data> | |
150 | + <data name=">>label1.ZOrder" xml:space="preserve"> | |
151 | + <value>6</value> | |
152 | + </data> | |
153 | + <data name="textBoxPlainText.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms"> | |
154 | + <value>Left, Right</value> | |
155 | + </data> | |
156 | + <data name="textBoxPlainText.Location" type="System.Drawing.Point, System.Drawing"> | |
157 | + <value>114, 5</value> | |
158 | + </data> | |
159 | + <data name="textBoxPlainText.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms"> | |
160 | + <value>3, 3, 3, 0</value> | |
161 | + </data> | |
162 | + <data name="textBoxPlainText.Size" type="System.Drawing.Size, System.Drawing"> | |
163 | + <value>221, 19</value> | |
164 | + </data> | |
165 | + <data name="textBoxPlainText.TabIndex" type="System.Int32, mscorlib"> | |
166 | + <value>1</value> | |
167 | + </data> | |
168 | + <data name=">>textBoxPlainText.Name" xml:space="preserve"> | |
169 | + <value>textBoxPlainText</value> | |
170 | + </data> | |
171 | + <data name=">>textBoxPlainText.Type" xml:space="preserve"> | |
172 | + <value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |
173 | + </data> | |
174 | + <data name=">>textBoxPlainText.Parent" xml:space="preserve"> | |
175 | + <value>tableLayoutPanel1</value> | |
176 | + </data> | |
177 | + <data name=">>textBoxPlainText.ZOrder" xml:space="preserve"> | |
178 | + <value>2</value> | |
179 | + </data> | |
180 | + <data name="textBoxPassword.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms"> | |
181 | + <value>Left, Right</value> | |
182 | + </data> | |
183 | + <data name="textBoxPassword.Location" type="System.Drawing.Point, System.Drawing"> | |
184 | + <value>114, 31</value> | |
185 | + </data> | |
186 | + <data name="textBoxPassword.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms"> | |
187 | + <value>3, 3, 3, 0</value> | |
188 | + </data> | |
189 | + <data name="textBoxPassword.Size" type="System.Drawing.Size, System.Drawing"> | |
190 | + <value>221, 19</value> | |
191 | + </data> | |
192 | + <data name="textBoxPassword.TabIndex" type="System.Int32, mscorlib"> | |
193 | + <value>3</value> | |
194 | + </data> | |
195 | + <data name=">>textBoxPassword.Name" xml:space="preserve"> | |
196 | + <value>textBoxPassword</value> | |
197 | + </data> | |
198 | + <data name=">>textBoxPassword.Type" xml:space="preserve"> | |
199 | + <value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |
200 | + </data> | |
201 | + <data name=">>textBoxPassword.Parent" xml:space="preserve"> | |
202 | + <value>tableLayoutPanel1</value> | |
203 | + </data> | |
204 | + <data name=">>textBoxPassword.ZOrder" xml:space="preserve"> | |
205 | + <value>7</value> | |
206 | + </data> | |
207 | + <data name="textBoxEncryptedText.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms"> | |
208 | + <value>Left, Right</value> | |
209 | + </data> | |
210 | + <data name="textBoxEncryptedText.Location" type="System.Drawing.Point, System.Drawing"> | |
211 | + <value>114, 83</value> | |
212 | + </data> | |
213 | + <data name="textBoxEncryptedText.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms"> | |
214 | + <value>3, 3, 3, 0</value> | |
215 | + </data> | |
216 | + <data name="textBoxEncryptedText.Size" type="System.Drawing.Size, System.Drawing"> | |
217 | + <value>221, 19</value> | |
218 | + </data> | |
219 | + <data name="textBoxEncryptedText.TabIndex" type="System.Int32, mscorlib"> | |
220 | + <value>6</value> | |
221 | + </data> | |
222 | + <data name=">>textBoxEncryptedText.Name" xml:space="preserve"> | |
223 | + <value>textBoxEncryptedText</value> | |
224 | + </data> | |
225 | + <data name=">>textBoxEncryptedText.Type" xml:space="preserve"> | |
226 | + <value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |
227 | + </data> | |
228 | + <data name=">>textBoxEncryptedText.Parent" xml:space="preserve"> | |
229 | + <value>tableLayoutPanel1</value> | |
230 | + </data> | |
231 | + <data name=">>textBoxEncryptedText.ZOrder" xml:space="preserve"> | |
232 | + <value>3</value> | |
233 | + </data> | |
234 | + <data name="label2.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms"> | |
235 | + <value>Left, Right</value> | |
236 | + </data> | |
237 | + <data name="label2.AutoSize" type="System.Boolean, mscorlib"> | |
238 | + <value>True</value> | |
239 | + </data> | |
240 | + <data name="label2.Location" type="System.Drawing.Point, System.Drawing"> | |
241 | + <value>3, 7</value> | |
242 | + </data> | |
243 | + <data name="label2.Size" type="System.Drawing.Size, System.Drawing"> | |
244 | + <value>105, 12</value> | |
245 | + </data> | |
246 | + <data name="label2.TabIndex" type="System.Int32, mscorlib"> | |
247 | + <value>0</value> | |
248 | + </data> | |
249 | + <data name="label2.Text" xml:space="preserve"> | |
250 | + <value>暗号化する文字列</value> | |
251 | + </data> | |
252 | + <data name=">>label2.Name" xml:space="preserve"> | |
253 | + <value>label2</value> | |
254 | + </data> | |
255 | + <data name=">>label2.Type" xml:space="preserve"> | |
256 | + <value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |
257 | + </data> | |
258 | + <data name=">>label2.Parent" xml:space="preserve"> | |
259 | + <value>tableLayoutPanel1</value> | |
260 | + </data> | |
261 | + <data name=">>label2.ZOrder" xml:space="preserve"> | |
262 | + <value>0</value> | |
263 | + </data> | |
264 | + <data name="label3.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms"> | |
265 | + <value>Left, Right</value> | |
266 | + </data> | |
267 | + <data name="label3.AutoSize" type="System.Boolean, mscorlib"> | |
268 | + <value>True</value> | |
269 | + </data> | |
270 | + <data name="label3.Location" type="System.Drawing.Point, System.Drawing"> | |
271 | + <value>3, 85</value> | |
272 | + </data> | |
273 | + <data name="label3.Size" type="System.Drawing.Size, System.Drawing"> | |
274 | + <value>105, 12</value> | |
275 | + </data> | |
276 | + <data name="label3.TabIndex" type="System.Int32, mscorlib"> | |
277 | + <value>5</value> | |
278 | + </data> | |
279 | + <data name="label3.Text" xml:space="preserve"> | |
280 | + <value>暗号化された文字列</value> | |
281 | + </data> | |
282 | + <data name=">>label3.Name" xml:space="preserve"> | |
283 | + <value>label3</value> | |
284 | + </data> | |
285 | + <data name=">>label3.Type" xml:space="preserve"> | |
286 | + <value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |
287 | + </data> | |
288 | + <data name=">>label3.Parent" xml:space="preserve"> | |
289 | + <value>tableLayoutPanel1</value> | |
290 | + </data> | |
291 | + <data name=">>label3.ZOrder" xml:space="preserve"> | |
292 | + <value>4</value> | |
293 | + </data> | |
294 | + <data name="buttonEncrypt.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms"> | |
295 | + <value>Top, Bottom, Left, Right</value> | |
296 | + </data> | |
297 | + <data name="buttonEncrypt.AutoSize" type="System.Boolean, mscorlib"> | |
298 | + <value>True</value> | |
299 | + </data> | |
300 | + <data name="buttonEncrypt.AutoSizeMode" type="System.Windows.Forms.AutoSizeMode, System.Windows.Forms"> | |
301 | + <value>GrowAndShrink</value> | |
302 | + </data> | |
303 | + <data name="tableLayoutPanel1.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms"> | |
304 | + <value>Top, Bottom, Left, Right</value> | |
305 | + </data> | |
306 | + <data name="tableLayoutPanel1.ColumnCount" type="System.Int32, mscorlib"> | |
307 | + <value>2</value> | |
308 | + </data> | |
309 | + <data name="buttonClose.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms"> | |
310 | + <value>Top, Bottom, Right</value> | |
311 | + </data> | |
312 | + <data name="buttonClose.AutoSize" type="System.Boolean, mscorlib"> | |
313 | + <value>True</value> | |
314 | + </data> | |
315 | + <data name="buttonClose.AutoSizeMode" type="System.Windows.Forms.AutoSizeMode, System.Windows.Forms"> | |
316 | + <value>GrowAndShrink</value> | |
317 | + </data> | |
318 | + <data name="buttonClose.Location" type="System.Drawing.Point, System.Drawing"> | |
319 | + <value>250, 107</value> | |
320 | + </data> | |
321 | + <data name="buttonClose.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms"> | |
322 | + <value>3, 3, 3, 0</value> | |
323 | + </data> | |
324 | + <data name="buttonClose.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms"> | |
325 | + <value>10, 0, 10, 0</value> | |
326 | + </data> | |
327 | + <data name="buttonClose.Size" type="System.Drawing.Size, System.Drawing"> | |
328 | + <value>85, 23</value> | |
329 | + </data> | |
330 | + <data name="buttonClose.TabIndex" type="System.Int32, mscorlib"> | |
331 | + <value>7</value> | |
332 | + </data> | |
333 | + <data name="buttonClose.Text" xml:space="preserve"> | |
334 | + <value>閉じる (&C)</value> | |
335 | + </data> | |
336 | + <data name=">>buttonClose.Name" xml:space="preserve"> | |
337 | + <value>buttonClose</value> | |
338 | + </data> | |
339 | + <data name=">>buttonClose.Type" xml:space="preserve"> | |
340 | + <value>System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |
341 | + </data> | |
342 | + <data name=">>buttonClose.Parent" xml:space="preserve"> | |
343 | + <value>tableLayoutPanel1</value> | |
344 | + </data> | |
345 | + <data name=">>buttonClose.ZOrder" xml:space="preserve"> | |
346 | + <value>1</value> | |
347 | + </data> | |
348 | + <data name="tableLayoutPanel1.Location" type="System.Drawing.Point, System.Drawing"> | |
349 | + <value>12, 9</value> | |
350 | + </data> | |
351 | + <data name="tableLayoutPanel1.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms"> | |
352 | + <value>3, 0, 3, 3</value> | |
353 | + </data> | |
354 | + <data name="tableLayoutPanel1.RowCount" type="System.Int32, mscorlib"> | |
355 | + <value>5</value> | |
356 | + </data> | |
357 | + <data name="tableLayoutPanel1.Size" type="System.Drawing.Size, System.Drawing"> | |
358 | + <value>338, 130</value> | |
359 | + </data> | |
360 | + <data name="tableLayoutPanel1.TabIndex" type="System.Int32, mscorlib"> | |
361 | + <value>0</value> | |
362 | + </data> | |
363 | + <data name=">>tableLayoutPanel1.Name" xml:space="preserve"> | |
364 | + <value>tableLayoutPanel1</value> | |
365 | + </data> | |
366 | + <data name=">>tableLayoutPanel1.Type" xml:space="preserve"> | |
367 | + <value>System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |
368 | + </data> | |
369 | + <data name=">>tableLayoutPanel1.Parent" xml:space="preserve"> | |
370 | + <value>$this</value> | |
371 | + </data> | |
372 | + <data name=">>tableLayoutPanel1.ZOrder" xml:space="preserve"> | |
373 | + <value>0</value> | |
374 | + </data> | |
375 | + <data name="tableLayoutPanel1.LayoutSettings" type="System.Windows.Forms.TableLayoutSettings, System.Windows.Forms"> | |
376 | + <value><?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="label2" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="buttonClose" Row="4" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="textBoxPlainText" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="textBoxEncryptedText" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label3" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="buttonEncrypt" Row="2" RowSpan="1" Column="0" ColumnSpan="2" /><Control Name="label1" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="textBoxPassword" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,AutoSize,0" /><Rows Styles="Percent,20,Percent,20,Percent,20,Percent,20,Percent,20" /></TableLayoutSettings></value> | |
377 | + </data> | |
378 | + <data name="buttonEncrypt.Location" type="System.Drawing.Point, System.Drawing"> | |
379 | + <value>3, 55</value> | |
380 | + </data> | |
381 | + <data name="buttonEncrypt.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms"> | |
382 | + <value>3, 3, 3, 0</value> | |
383 | + </data> | |
384 | + <data name="buttonEncrypt.Size" type="System.Drawing.Size, System.Drawing"> | |
385 | + <value>332, 23</value> | |
386 | + </data> | |
387 | + <data name="buttonEncrypt.TabIndex" type="System.Int32, mscorlib"> | |
388 | + <value>4</value> | |
389 | + </data> | |
390 | + <data name="buttonEncrypt.Text" xml:space="preserve"> | |
391 | + <value>暗号化</value> | |
392 | + </data> | |
393 | + <data name=">>buttonEncrypt.Name" xml:space="preserve"> | |
394 | + <value>buttonEncrypt</value> | |
395 | + </data> | |
396 | + <data name=">>buttonEncrypt.Type" xml:space="preserve"> | |
397 | + <value>System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |
398 | + </data> | |
399 | + <data name=">>buttonEncrypt.Parent" xml:space="preserve"> | |
400 | + <value>tableLayoutPanel1</value> | |
401 | + </data> | |
402 | + <data name=">>buttonEncrypt.ZOrder" xml:space="preserve"> | |
403 | + <value>5</value> | |
404 | + </data> | |
405 | + <metadata name="$this.Localizable" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |
406 | + <value>True</value> | |
407 | + </metadata> | |
408 | + <data name="$this.AutoScaleDimensions" type="System.Drawing.SizeF, System.Drawing"> | |
409 | + <value>96, 96</value> | |
410 | + </data> | |
411 | + <data name="$this.ClientSize" type="System.Drawing.Size, System.Drawing"> | |
412 | + <value>362, 151</value> | |
413 | + </data> | |
414 | + <data name="$this.StartPosition" type="System.Windows.Forms.FormStartPosition, System.Windows.Forms"> | |
415 | + <value>CenterParent</value> | |
416 | + </data> | |
417 | + <data name="$this.Text" xml:space="preserve"> | |
418 | + <value>APIキー暗号化</value> | |
419 | + </data> | |
420 | + <data name=">>$this.Name" xml:space="preserve"> | |
421 | + <value>EncryptApiKeyDialog</value> | |
422 | + </data> | |
423 | + <data name=">>$this.Type" xml:space="preserve"> | |
424 | + <value>OpenTween.OTBaseForm, OpenTween, Version=2.4.3.1, Culture=neutral, PublicKeyToken=null</value> | |
425 | + </data> | |
426 | +</root> | |
\ No newline at end of file |
@@ -68,6 +68,7 @@ | ||
68 | 68 | <Compile Include="ApiInfoDialog.Designer.cs"> |
69 | 69 | <DependentUpon>ApiInfoDialog.cs</DependentUpon> |
70 | 70 | </Compile> |
71 | + <Compile Include="ApiKey.cs" /> | |
71 | 72 | <Compile Include="Api\ApiLimit.cs" /> |
72 | 73 | <Compile Include="Api\BitlyApi.cs" /> |
73 | 74 | <Compile Include="Api\DataModel\GeoJson.cs" /> |
@@ -133,6 +134,12 @@ | ||
133 | 134 | <Compile Include="Connection\OAuthUtility.cs" /> |
134 | 135 | <Compile Include="Connection\TwitterApiConnection.cs" /> |
135 | 136 | <Compile Include="DateTimeUtc.cs" /> |
137 | + <Compile Include="EncryptApiKeyDialog.cs"> | |
138 | + <SubType>Form</SubType> | |
139 | + </Compile> | |
140 | + <Compile Include="EncryptApiKeyDialog.Designer.cs"> | |
141 | + <DependentUpon>EncryptApiKeyDialog.cs</DependentUpon> | |
142 | + </Compile> | |
136 | 143 | <Compile Include="EventViewerDialog.cs"> |
137 | 144 | <SubType>Form</SubType> |
138 | 145 | </Compile> |
@@ -470,6 +477,9 @@ | ||
470 | 477 | <EmbeddedResource Include="AuthDialog.resx"> |
471 | 478 | <DependentUpon>AuthDialog.cs</DependentUpon> |
472 | 479 | </EmbeddedResource> |
480 | + <EmbeddedResource Include="EncryptApiKeyDialog.resx"> | |
481 | + <DependentUpon>EncryptApiKeyDialog.cs</DependentUpon> | |
482 | + </EmbeddedResource> | |
473 | 483 | <EmbeddedResource Include="EventViewerDialog.en.resx"> |
474 | 484 | <DependentUpon>EventViewerDialog.cs</DependentUpon> |
475 | 485 | </EmbeddedResource> |
@@ -45,6 +45,7 @@ | ||
45 | 45 | this.CheckNicoms = new System.Windows.Forms.CheckBox(); |
46 | 46 | this.EnableImgAzyobuziNetCheckBox = new System.Windows.Forms.CheckBox(); |
47 | 47 | this.ImgAzyobuziNetDisabledInDMCheckBox = new System.Windows.Forms.CheckBox(); |
48 | + this.EncryptApiKeyButton = new System.Windows.Forms.Button(); | |
48 | 49 | this.MapThumbnailGroupBox.SuspendLayout(); |
49 | 50 | this.SuspendLayout(); |
50 | 51 | // |
@@ -278,10 +279,18 @@ | ||
278 | 279 | this.ImgAzyobuziNetDisabledInDMCheckBox.Name = "ImgAzyobuziNetDisabledInDMCheckBox"; |
279 | 280 | this.ImgAzyobuziNetDisabledInDMCheckBox.UseVisualStyleBackColor = true; |
280 | 281 | // |
282 | + // EncryptApiKeyButton | |
283 | + // | |
284 | + resources.ApplyResources(this.EncryptApiKeyButton, "EncryptApiKeyButton"); | |
285 | + this.EncryptApiKeyButton.Name = "EncryptApiKeyButton"; | |
286 | + this.EncryptApiKeyButton.UseVisualStyleBackColor = true; | |
287 | + this.EncryptApiKeyButton.Click += new System.EventHandler(this.EncryptApiKeyButton_Click); | |
288 | + // | |
281 | 289 | // CooperatePanel |
282 | 290 | // |
283 | 291 | resources.ApplyResources(this, "$this"); |
284 | 292 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; |
293 | + this.Controls.Add(this.EncryptApiKeyButton); | |
285 | 294 | this.Controls.Add(this.ImgAzyobuziNetDisabledInDMCheckBox); |
286 | 295 | this.Controls.Add(this.EnableImgAzyobuziNetCheckBox); |
287 | 296 | this.Controls.Add(this.MapThumbnailGroupBox); |
@@ -316,5 +325,6 @@ | ||
316 | 325 | internal System.Windows.Forms.ComboBox MapThumbnailProviderComboBox; |
317 | 326 | internal System.Windows.Forms.CheckBox EnableImgAzyobuziNetCheckBox; |
318 | 327 | internal System.Windows.Forms.CheckBox ImgAzyobuziNetDisabledInDMCheckBox; |
328 | + private System.Windows.Forms.Button EncryptApiKeyButton; | |
319 | 329 | } |
320 | 330 | } |
@@ -41,7 +41,10 @@ namespace OpenTween.Setting.Panel | ||
41 | 41 | public partial class CooperatePanel : SettingPanelBase |
42 | 42 | { |
43 | 43 | public CooperatePanel() |
44 | - => this.InitializeComponent(); | |
44 | + { | |
45 | + this.InitializeComponent(); | |
46 | + this.EncryptApiKeyButton.Visible = MyCommon.DebugBuild; | |
47 | + } | |
45 | 48 | |
46 | 49 | public void LoadConfig(SettingCommon settingCommon) |
47 | 50 | { |
@@ -79,5 +82,11 @@ namespace OpenTween.Setting.Panel | ||
79 | 82 | |
80 | 83 | private void EnableImgAzyobuziNetCheckBox_CheckedChanged(object sender, EventArgs e) |
81 | 84 | => this.ImgAzyobuziNetDisabledInDMCheckBox.Enabled = this.EnableImgAzyobuziNetCheckBox.Checked; |
85 | + | |
86 | + private void EncryptApiKeyButton_Click(object sender, EventArgs e) | |
87 | + { | |
88 | + using var dialog = new EncryptApiKeyDialog(); | |
89 | + dialog.ShowDialog(this.ParentForm); | |
90 | + } | |
82 | 91 | } |
83 | 92 | } |
@@ -154,13 +154,10 @@ | ||
154 | 154 | <data name="CheckNicoms.Text" xml:space="preserve"> |
155 | 155 | <value>Shorten nicovideo urls by nico.ms</value> |
156 | 156 | </data> |
157 | - <data name="Label60.Size" type="System.Drawing.Size, System.Drawing"> | |
158 | - <value>107, 12</value> | |
157 | + <data name="EncryptApiKeyButton.Size" type="System.Drawing.Size, System.Drawing"> | |
158 | + <value>154, 23</value> | |
159 | 159 | </data> |
160 | - <data name="Label59.Size" type="System.Drawing.Size, System.Drawing"> | |
161 | - <value>86, 12</value> | |
162 | - </data> | |
163 | - <data name="Label59.Text" xml:space="preserve"> | |
164 | - <value>Your secret key</value> | |
160 | + <data name="EncryptApiKeyButton.Text" xml:space="preserve"> | |
161 | + <value>API Key Encryption</value> | |
165 | 162 | </data> |
166 | 163 | </root> |
\ No newline at end of file |
@@ -117,21 +117,20 @@ | ||
117 | 117 | <resheader name="writer"> |
118 | 118 | <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> |
119 | 119 | </resheader> |
120 | - <assembly alias="mscorlib" name="mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> | |
121 | - <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> | |
122 | - <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> | |
123 | 120 | <data name="MapThumbnailProviderComboBox.Items" xml:space="preserve"> |
124 | 121 | <value>OpenStreetMap</value> |
125 | 122 | </data> |
126 | 123 | <data name="MapThumbnailProviderComboBox.Items1" xml:space="preserve"> |
127 | 124 | <value>Google Maps</value> |
128 | 125 | </data> |
126 | + <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> | |
129 | 127 | <data name="MapThumbnailProviderComboBox.Location" type="System.Drawing.Point, System.Drawing"> |
130 | 128 | <value>102, 22</value> |
131 | 129 | </data> |
132 | 130 | <data name="MapThumbnailProviderComboBox.Size" type="System.Drawing.Size, System.Drawing"> |
133 | 131 | <value>121, 20</value> |
134 | 132 | </data> |
133 | + <assembly alias="mscorlib" name="mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> | |
135 | 134 | <data name="MapThumbnailProviderComboBox.TabIndex" type="System.Int32, mscorlib"> |
136 | 135 | <value>1</value> |
137 | 136 | </data> |
@@ -150,6 +149,7 @@ | ||
150 | 149 | <data name="label48.AutoSize" type="System.Boolean, mscorlib"> |
151 | 150 | <value>True</value> |
152 | 151 | </data> |
152 | + <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> | |
153 | 153 | <data name="label48.ImeMode" type="System.Windows.Forms.ImeMode, System.Windows.Forms"> |
154 | 154 | <value>NoControl</value> |
155 | 155 | </data> |
@@ -355,7 +355,7 @@ | ||
355 | 355 | <value>$this</value> |
356 | 356 | </data> |
357 | 357 | <data name=">>MapThumbnailGroupBox.ZOrder" xml:space="preserve"> |
358 | - <value>2</value> | |
358 | + <value>3</value> | |
359 | 359 | </data> |
360 | 360 | <data name="Label39.AutoSize" type="System.Boolean, mscorlib"> |
361 | 361 | <value>True</value> |
@@ -385,7 +385,7 @@ | ||
385 | 385 | <value>$this</value> |
386 | 386 | </data> |
387 | 387 | <data name=">>Label39.ZOrder" xml:space="preserve"> |
388 | - <value>3</value> | |
388 | + <value>4</value> | |
389 | 389 | </data> |
390 | 390 | <data name="UserAppointUrlText.Location" type="System.Drawing.Point, System.Drawing"> |
391 | 391 | <value>218, 102</value> |
@@ -406,7 +406,7 @@ | ||
406 | 406 | <value>$this</value> |
407 | 407 | </data> |
408 | 408 | <data name=">>UserAppointUrlText.ZOrder" xml:space="preserve"> |
409 | - <value>4</value> | |
409 | + <value>5</value> | |
410 | 410 | </data> |
411 | 411 | <data name="ComboBoxTranslateLanguage.Items" xml:space="preserve"> |
412 | 412 | <value>Afrikaans</value> |
@@ -799,7 +799,7 @@ | ||
799 | 799 | <value>$this</value> |
800 | 800 | </data> |
801 | 801 | <data name=">>ComboBoxTranslateLanguage.ZOrder" xml:space="preserve"> |
802 | - <value>5</value> | |
802 | + <value>6</value> | |
803 | 803 | </data> |
804 | 804 | <data name="Label29.AutoSize" type="System.Boolean, mscorlib"> |
805 | 805 | <value>True</value> |
@@ -829,7 +829,7 @@ | ||
829 | 829 | <value>$this</value> |
830 | 830 | </data> |
831 | 831 | <data name=">>Label29.ZOrder" xml:space="preserve"> |
832 | - <value>6</value> | |
832 | + <value>7</value> | |
833 | 833 | </data> |
834 | 834 | <data name="CheckNicoms.AutoSize" type="System.Boolean, mscorlib"> |
835 | 835 | <value>True</value> |
@@ -859,7 +859,7 @@ | ||
859 | 859 | <value>$this</value> |
860 | 860 | </data> |
861 | 861 | <data name=">>CheckNicoms.ZOrder" xml:space="preserve"> |
862 | - <value>7</value> | |
862 | + <value>8</value> | |
863 | 863 | </data> |
864 | 864 | <data name="EnableImgAzyobuziNetCheckBox.AutoSize" type="System.Boolean, mscorlib"> |
865 | 865 | <value>True</value> |
@@ -886,7 +886,7 @@ | ||
886 | 886 | <value>$this</value> |
887 | 887 | </data> |
888 | 888 | <data name=">>EnableImgAzyobuziNetCheckBox.ZOrder" xml:space="preserve"> |
889 | - <value>1</value> | |
889 | + <value>2</value> | |
890 | 890 | </data> |
891 | 891 | <data name="ImgAzyobuziNetDisabledInDMCheckBox.AutoSize" type="System.Boolean, mscorlib"> |
892 | 892 | <value>True</value> |
@@ -916,6 +916,36 @@ | ||
916 | 916 | <value>$this</value> |
917 | 917 | </data> |
918 | 918 | <data name=">>ImgAzyobuziNetDisabledInDMCheckBox.ZOrder" xml:space="preserve"> |
919 | + <value>1</value> | |
920 | + </data> | |
921 | + <data name="EncryptApiKeyButton.AutoSize" type="System.Boolean, mscorlib"> | |
922 | + <value>True</value> | |
923 | + </data> | |
924 | + <data name="EncryptApiKeyButton.Location" type="System.Drawing.Point, System.Drawing"> | |
925 | + <value>23, 293</value> | |
926 | + </data> | |
927 | + <data name="EncryptApiKeyButton.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms"> | |
928 | + <value>20, 0, 20, 0</value> | |
929 | + </data> | |
930 | + <data name="EncryptApiKeyButton.Size" type="System.Drawing.Size, System.Drawing"> | |
931 | + <value>135, 23</value> | |
932 | + </data> | |
933 | + <data name="EncryptApiKeyButton.TabIndex" type="System.Int32, mscorlib"> | |
934 | + <value>8</value> | |
935 | + </data> | |
936 | + <data name="EncryptApiKeyButton.Text" xml:space="preserve"> | |
937 | + <value>APIキー暗号化</value> | |
938 | + </data> | |
939 | + <data name=">>EncryptApiKeyButton.Name" xml:space="preserve"> | |
940 | + <value>EncryptApiKeyButton</value> | |
941 | + </data> | |
942 | + <data name=">>EncryptApiKeyButton.Type" xml:space="preserve"> | |
943 | + <value>System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |
944 | + </data> | |
945 | + <data name=">>EncryptApiKeyButton.Parent" xml:space="preserve"> | |
946 | + <value>$this</value> | |
947 | + </data> | |
948 | + <data name=">>EncryptApiKeyButton.ZOrder" xml:space="preserve"> | |
919 | 949 | <value>0</value> |
920 | 950 | </data> |
921 | 951 | <metadata name="$this.Localizable" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> |
@@ -928,6 +958,6 @@ | ||
928 | 958 | <value>CooperatePanel</value> |
929 | 959 | </data> |
930 | 960 | <data name=">>$this.Type" xml:space="preserve"> |
931 | - <value>OpenTween.Setting.Panel.SettingPanelBase, OpenTween, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null</value> | |
961 | + <value>OpenTween.Setting.Panel.SettingPanelBase, OpenTween, Version=2.4.3.1, Culture=neutral, PublicKeyToken=null</value> | |
932 | 962 | </data> |
933 | 963 | </root> |
\ No newline at end of file |
@@ -52,14 +52,20 @@ namespace OpenTween.Thumbnail.Services | ||
52 | 52 | => this.localHttpClient ?? Networking.Http; |
53 | 53 | |
54 | 54 | private readonly HttpClient? localHttpClient; |
55 | + private readonly ApiKey clientId; | |
56 | + private readonly ApiKey clientSecret; | |
55 | 57 | |
56 | 58 | public FoursquareCheckin() |
57 | - : this(null) | |
59 | + : this(null, ApplicationSettings.FoursquareClientId, ApplicationSettings.FoursquareClientSecret) | |
58 | 60 | { |
59 | 61 | } |
60 | 62 | |
61 | - public FoursquareCheckin(HttpClient? http) | |
62 | - => this.localHttpClient = http; | |
63 | + public FoursquareCheckin(HttpClient? http, ApiKey clientId, ApiKey clientSecret) | |
64 | + { | |
65 | + this.localHttpClient = http; | |
66 | + this.clientId = clientId; | |
67 | + this.clientSecret = clientSecret; | |
68 | + } | |
63 | 69 | |
64 | 70 | public override async Task<ThumbnailInfo?> GetThumbnailInfoAsync(string url, PostClass post, CancellationToken token) |
65 | 71 | { |
@@ -96,6 +102,10 @@ namespace OpenTween.Thumbnail.Services | ||
96 | 102 | if (!match.Success) |
97 | 103 | return null; |
98 | 104 | |
105 | + if (!(this.clientId, this.clientSecret).TryGetValue(out var keyPair)) | |
106 | + return null; | |
107 | + | |
108 | + var (clientId, clientSecret) = keyPair; | |
99 | 109 | var checkinIdGroup = match.Groups["checkin_id"]; |
100 | 110 | |
101 | 111 | try |
@@ -105,8 +115,8 @@ namespace OpenTween.Thumbnail.Services | ||
105 | 115 | |
106 | 116 | var query = new Dictionary<string, string> |
107 | 117 | { |
108 | - ["client_id"] = ApplicationSettings.FoursquareClientId, | |
109 | - ["client_secret"] = ApplicationSettings.FoursquareClientSecret, | |
118 | + ["client_id"] = clientId, | |
119 | + ["client_secret"] = clientSecret, | |
110 | 120 | ["v"] = "20140419", // https://developer.foursquare.com/overview/versioning |
111 | 121 | |
112 | 122 | ["shortId"] = checkinIdGroup.Value, |
@@ -140,6 +150,10 @@ namespace OpenTween.Thumbnail.Services | ||
140 | 150 | if (!match.Success) |
141 | 151 | return null; |
142 | 152 | |
153 | + if (!(this.clientId, this.clientSecret).TryGetValue(out var keyPair)) | |
154 | + return null; | |
155 | + | |
156 | + var (clientId, clientSecret) = keyPair; | |
143 | 157 | var checkinIdGroup = match.Groups["checkin_id"]; |
144 | 158 | var signatureGroup = match.Groups["signature"]; |
145 | 159 |
@@ -150,8 +164,8 @@ namespace OpenTween.Thumbnail.Services | ||
150 | 164 | |
151 | 165 | var query = new Dictionary<string, string> |
152 | 166 | { |
153 | - ["client_id"] = ApplicationSettings.FoursquareClientId, | |
154 | - ["client_secret"] = ApplicationSettings.FoursquareClientSecret, | |
167 | + ["client_id"] = clientId, | |
168 | + ["client_secret"] = clientSecret, | |
155 | 169 | ["v"] = "20140419", // https://developer.foursquare.com/overview/versioning |
156 | 170 | }; |
157 | 171 |
@@ -45,15 +45,19 @@ namespace OpenTween.Thumbnail.Services | ||
45 | 45 | protected HttpClient http |
46 | 46 | => this.localHttpClient ?? Networking.Http; |
47 | 47 | |
48 | + private readonly ApiKey apiKey; | |
48 | 49 | private readonly HttpClient? localHttpClient; |
49 | 50 | |
50 | 51 | public Tinami() |
51 | - : this(null) | |
52 | + : this(ApplicationSettings.TINAMIApiKey, null) | |
52 | 53 | { |
53 | 54 | } |
54 | 55 | |
55 | - public Tinami(HttpClient? http) | |
56 | - => this.localHttpClient = http; | |
56 | + public Tinami(ApiKey apiKey, HttpClient? http) | |
57 | + { | |
58 | + this.apiKey = apiKey; | |
59 | + this.localHttpClient = http; | |
60 | + } | |
57 | 61 | |
58 | 62 | public override async Task<ThumbnailInfo?> GetThumbnailInfoAsync(string url, PostClass post, CancellationToken token) |
59 | 63 | { |
@@ -61,11 +65,14 @@ namespace OpenTween.Thumbnail.Services | ||
61 | 65 | if (!match.Success) |
62 | 66 | return null; |
63 | 67 | |
68 | + if (!this.apiKey.TryGetValue(out var apiKey)) | |
69 | + return null; | |
70 | + | |
64 | 71 | var contentId = match.Groups["ContentId"].Value; |
65 | 72 | |
66 | 73 | try |
67 | 74 | { |
68 | - var xdoc = await this.FetchContentInfoApiAsync(contentId, token) | |
75 | + var xdoc = await this.FetchContentInfoApiAsync(apiKey, contentId, token) | |
69 | 76 | .ConfigureAwait(false); |
70 | 77 | |
71 | 78 | if (xdoc.XPathSelectElement("/rsp").Attribute("stat").Value != "ok") |
@@ -89,11 +96,11 @@ namespace OpenTween.Thumbnail.Services | ||
89 | 96 | return null; |
90 | 97 | } |
91 | 98 | |
92 | - protected virtual async Task<XDocument> FetchContentInfoApiAsync(string contentId, CancellationToken token) | |
99 | + protected virtual async Task<XDocument> FetchContentInfoApiAsync(string apiKey, string contentId, CancellationToken token) | |
93 | 100 | { |
94 | 101 | var query = new Dictionary<string, string> |
95 | 102 | { |
96 | - ["api_key"] = ApplicationSettings.TINAMIApiKey, | |
103 | + ["api_key"] = apiKey, | |
97 | 104 | ["cont_id"] = contentId, |
98 | 105 | }; |
99 | 106 |
@@ -46,15 +46,19 @@ namespace OpenTween.Thumbnail.Services | ||
46 | 46 | protected HttpClient http |
47 | 47 | => this.localHttpClient ?? Networking.Http; |
48 | 48 | |
49 | + private readonly ApiKey tumblrConsumerKey; | |
49 | 50 | private readonly HttpClient? localHttpClient; |
50 | 51 | |
51 | 52 | public Tumblr() |
52 | - : this(null) | |
53 | + : this(ApplicationSettings.TumblrConsumerKey, null) | |
53 | 54 | { |
54 | 55 | } |
55 | 56 | |
56 | - public Tumblr(HttpClient? http) | |
57 | - => this.localHttpClient = http; | |
57 | + public Tumblr(ApiKey apiKey, HttpClient? http) | |
58 | + { | |
59 | + this.tumblrConsumerKey = apiKey; | |
60 | + this.localHttpClient = http; | |
61 | + } | |
58 | 62 | |
59 | 63 | public override async Task<ThumbnailInfo?> GetThumbnailInfoAsync(string url, PostClass post, CancellationToken token) |
60 | 64 | { |
@@ -62,6 +66,9 @@ namespace OpenTween.Thumbnail.Services | ||
62 | 66 | if (!match.Success) |
63 | 67 | return null; |
64 | 68 | |
69 | + if (!this.tumblrConsumerKey.TryGetValue(out var apiKey)) | |
70 | + return null; | |
71 | + | |
65 | 72 | // 参照: http://www.tumblr.com/docs/en/api/v2#photo-posts |
66 | 73 | |
67 | 74 | var host = match.Groups["host"].Value; |
@@ -69,7 +76,7 @@ namespace OpenTween.Thumbnail.Services | ||
69 | 76 | |
70 | 77 | var param = new Dictionary<string, string> |
71 | 78 | { |
72 | - ["api_key"] = ApplicationSettings.TumblrConsumerKey, | |
79 | + ["api_key"] = apiKey, | |
73 | 80 | ["id"] = postId, |
74 | 81 | }; |
75 | 82 |
@@ -140,7 +140,7 @@ namespace OpenTween | ||
140 | 140 | private FormWindowState _formWindowState = FormWindowState.Normal; // フォームの状態保存用 通知領域からアイコンをクリックして復帰した際に使用する |
141 | 141 | |
142 | 142 | //twitter解析部 |
143 | - private readonly TwitterApi twitterApi = new TwitterApi(); | |
143 | + private readonly TwitterApi twitterApi = new TwitterApi(ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret); | |
144 | 144 | private Twitter tw = null!; |
145 | 145 | |
146 | 146 | //Growl呼び出し部 |
@@ -376,7 +376,7 @@ namespace OpenTween | ||
376 | 376 | |
377 | 377 | this.PostBrowser.DocumentText = this.Owner.createDetailHtml(translatedText); |
378 | 378 | } |
379 | - catch (HttpRequestException e) | |
379 | + catch (WebApiException e) | |
380 | 380 | { |
381 | 381 | this.RaiseStatusChanged("Err:" + e.Message); |
382 | 382 | } |
@@ -169,10 +169,6 @@ namespace OpenTween | ||
169 | 169 | |
170 | 170 | private long previousStatusId = -1L; |
171 | 171 | |
172 | - public Twitter() : this(new TwitterApi()) | |
173 | - { | |
174 | - } | |
175 | - | |
176 | 172 | public Twitter(TwitterApi api) |
177 | 173 | { |
178 | 174 | this.Api = api; |