OSDN Git Service

設定画面での bit.ly のアクセストークン取得 (OAuth2) に対応
authorKimura Youichi <kim.upsilon@bucyou.net>
Fri, 28 Apr 2017 18:15:32 +0000 (03:15 +0900)
committerKimura Youichi <kim.upsilon@bucyou.net>
Sat, 29 Apr 2017 03:46:39 +0000 (12:46 +0900)
OpenTween/Api/BitlyApi.cs
OpenTween/ApplicationSettings.cs
OpenTween/LoginDialog.cs
OpenTween/Properties/Resources.Designer.cs
OpenTween/Properties/Resources.en.resx
OpenTween/Properties/Resources.resx
OpenTween/Setting/Panel/ShortUrlPanel.Designer.cs
OpenTween/Setting/Panel/ShortUrlPanel.cs
OpenTween/Setting/Panel/ShortUrlPanel.en.resx
OpenTween/Setting/Panel/ShortUrlPanel.resx

index 5f41afc..693fbf4 100644 (file)
@@ -23,9 +23,13 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Runtime.Serialization.Json;
 using System.Text;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
+using System.Xml;
+using System.Xml.Linq;
 using OpenTween.Connection;
 
 namespace OpenTween.Api
@@ -86,6 +90,56 @@ namespace OpenTween.Api
             }
         }
 
+        public async Task<string> GetAccessTokenAsync(string username, string password)
+        {
+            var param = new Dictionary<string, string>
+            {
+                ["grant_type"] = "password",
+                ["username"] = username,
+                ["password"] = password,
+            };
+
+            var endpoint = new Uri(ApiBase, "/oauth/access_token");
+
+            using (var request = new HttpRequestMessage(HttpMethod.Post, endpoint))
+            using (var postContent = new FormUrlEncodedContent(param))
+            {
+                var authzParam = ApplicationSettings.BitlyClientId + ":" + ApplicationSettings.BitlyClientSecret;
+                request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes(authzParam)));
+
+                request.Content = postContent;
+
+                using (var response = await this.http.SendAsync(request).ConfigureAwait(false))
+                {
+                    var responseBytes = await response.Content.ReadAsByteArrayAsync()
+                        .ConfigureAwait(false);
+
+                    return this.ParseOAuthCredential(responseBytes);
+                }
+            }
+        }
+
+        private string ParseOAuthCredential(byte[] responseBytes)
+        {
+            using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(responseBytes, XmlDictionaryReaderQuotas.Max))
+            {
+                var xElm = XElement.Load(jsonReader);
+
+                var statusCode = xElm.Element("status_code")?.Value ?? "200";
+                if (statusCode != "200")
+                {
+                    var statusText = xElm.Element("status_txt")?.Value;
+                    throw new WebApiException(statusText ?? $"status_code = {statusCode}");
+                }
+
+                var accessToken = xElm.Element("access_token")?.Value;
+                if (accessToken == null)
+                    throw new WebApiException("Property `access_token` required");
+
+                return accessToken;
+            }
+        }
+
         private IEnumerable<KeyValuePair<string, string>> CreateAccessTokenParams()
         {
             if (string.IsNullOrEmpty(this.EndUserAccessToken))
index 00a8377..414a4e7 100644 (file)
@@ -119,17 +119,17 @@ namespace OpenTween
 
         //=====================================================================
         // bit.ly
-        // https://bitly.com/a/account から取得できます。
+        // https://bitly.com/a/oauth_apps から取得できます。
 
         /// <summary>
-        /// bit.ly ログイン名
+        /// bit.ly Client ID
         /// </summary>
-        public const string BitlyLoginId = "opentween";
+        public const string BitlyClientId = "ddab8ec50f4459c315cbde9d923cf490923b6d2e";
 
         /// <summary>
-        /// bit.ly APIキー
+        /// bit.ly Client Secret
         /// </summary>
-        public const string BitlyApiKey = "R_76319a25e2420b8d2c42e812fe177d8b";
+        public const string BitlyClientSecret = "485c9d03dd264f8eeb4fc65d38e2762c4420cee7";
 
         //=====================================================================
         // TINAMI
index e47cc30..1645a30 100644 (file)
@@ -1,4 +1,25 @@
-using System;
+// OpenTween - Client of Twitter
+// Copyright (c) 2017 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Data;
index a4bc25f..d0415e5 100644 (file)
@@ -335,6 +335,15 @@ namespace OpenTween.Properties {
         }
         
         /// <summary>
+        ///   認証に失敗しました ({0}) に類似しているローカライズされた文字列を検索します。
+        /// </summary>
+        internal static string BitlyAuthorize_ErrorText {
+            get {
+                return ResourceManager.GetString("BitlyAuthorize_ErrorText", resourceCulture);
+            }
+        }
+        
+        /// <summary>
         ///   ブラウザの起動に失敗しました。エラーコード: {0} に類似しているローカライズされた文字列を検索します。
         /// </summary>
         internal static string BrowserStartFailed {
index 4e46516..0891445 100644 (file)
@@ -1152,6 +1152,9 @@ Existing setting files are copied to {1}.
  * If you run as administrator, operations to post images with D&amp;D will be restricted.
  * After running, the setting files of {0} will be able to modify only from administrator.</value>
   </data>
+  <data name="BitlyAuthorize_ErrorText" xml:space="preserve">
+    <value>Failed to authorize. ({0})</value>
+  </data>
   <data name="UrlConvert_BitlyAuthRequired" xml:space="preserve">
     <value>To use Bitly, you must perform the Bitly authentication in settings dialog.</value>
   </data>
index cc55fbd..7450162 100644 (file)
  * 管理者権限で実行するとD&amp;Dで画像を投稿する操作が制限されます。
  * {0}の設定ファイルが一般ユーザー権限で編集できなくなります。</value>
   </data>
+  <data name="BitlyAuthorize_ErrorText" xml:space="preserve">
+    <value>認証に失敗しました ({0})</value>
+  </data>
   <data name="UrlConvert_BitlyAuthRequired" xml:space="preserve">
     <value>Bitlyを使用するには設定画面で認証情報を入力する必要があります</value>
   </data>
index bd21647..bf20d8a 100644 (file)
             System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ShortUrlPanel));
             this.ShortenTcoCheck = new System.Windows.Forms.CheckBox();
             this.CheckTinyURL = new System.Windows.Forms.CheckBox();
-            this.TextBitlyPw = new System.Windows.Forms.TextBox();
+            this.TextBitlyAccessToken = new System.Windows.Forms.TextBox();
             this.CheckAutoConvertUrl = new System.Windows.Forms.CheckBox();
             this.Label71 = new System.Windows.Forms.Label();
             this.ComboBoxAutoShortUrlFirst = new System.Windows.Forms.ComboBox();
-            this.Label76 = new System.Windows.Forms.Label();
             this.Label77 = new System.Windows.Forms.Label();
-            this.TextBitlyId = new System.Windows.Forms.TextBox();
+            this.ButtonBitlyAuthorize = new System.Windows.Forms.Button();
             this.SuspendLayout();
             // 
             // ShortenTcoCheck
             this.CheckTinyURL.Name = "CheckTinyURL";
             this.CheckTinyURL.UseVisualStyleBackColor = true;
             // 
-            // TextBitlyPw
+            // TextBitlyAccessToken
             // 
-            resources.ApplyResources(this.TextBitlyPw, "TextBitlyPw");
-            this.TextBitlyPw.Name = "TextBitlyPw";
+            resources.ApplyResources(this.TextBitlyAccessToken, "TextBitlyAccessToken");
+            this.TextBitlyAccessToken.Name = "TextBitlyAccessToken";
             // 
             // CheckAutoConvertUrl
             // 
             this.ComboBoxAutoShortUrlFirst.Name = "ComboBoxAutoShortUrlFirst";
             this.ComboBoxAutoShortUrlFirst.SelectedIndexChanged += new System.EventHandler(this.ComboBoxAutoShortUrlFirst_SelectedIndexChanged);
             // 
-            // Label76
-            // 
-            resources.ApplyResources(this.Label76, "Label76");
-            this.Label76.Name = "Label76";
-            // 
             // Label77
             // 
             resources.ApplyResources(this.Label77, "Label77");
             this.Label77.Name = "Label77";
             // 
-            // TextBitlyId
+            // ButtonBitlyAuthorize
             // 
-            resources.ApplyResources(this.TextBitlyId, "TextBitlyId");
-            this.TextBitlyId.Name = "TextBitlyId";
+            resources.ApplyResources(this.ButtonBitlyAuthorize, "ButtonBitlyAuthorize");
+            this.ButtonBitlyAuthorize.Name = "ButtonBitlyAuthorize";
+            this.ButtonBitlyAuthorize.UseVisualStyleBackColor = true;
+            this.ButtonBitlyAuthorize.Click += new System.EventHandler(this.ButtonBitlyAuthorize_Click);
             // 
             // ShortUrlPanel
             // 
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
             this.Controls.Add(this.ShortenTcoCheck);
             this.Controls.Add(this.CheckTinyURL);
-            this.Controls.Add(this.TextBitlyPw);
+            this.Controls.Add(this.TextBitlyAccessToken);
             this.Controls.Add(this.CheckAutoConvertUrl);
             this.Controls.Add(this.Label71);
             this.Controls.Add(this.ComboBoxAutoShortUrlFirst);
-            this.Controls.Add(this.Label76);
+            this.Controls.Add(this.ButtonBitlyAuthorize);
             this.Controls.Add(this.Label77);
-            this.Controls.Add(this.TextBitlyId);
             this.Name = "ShortUrlPanel";
             this.ResumeLayout(false);
             this.PerformLayout();
 
         internal System.Windows.Forms.CheckBox ShortenTcoCheck;
         internal System.Windows.Forms.CheckBox CheckTinyURL;
-        internal System.Windows.Forms.TextBox TextBitlyPw;
         internal System.Windows.Forms.CheckBox CheckAutoConvertUrl;
         internal System.Windows.Forms.Label Label71;
         internal System.Windows.Forms.ComboBox ComboBoxAutoShortUrlFirst;
-        internal System.Windows.Forms.Label Label76;
         internal System.Windows.Forms.Label Label77;
-        internal System.Windows.Forms.TextBox TextBitlyId;
+        private System.Windows.Forms.Button ButtonBitlyAuthorize;
+        private System.Windows.Forms.TextBox TextBitlyAccessToken;
     }
 }
index 564bdfd..cf96dd0 100644 (file)
@@ -32,6 +32,8 @@ using System.Data;
 using System.Linq;
 using System.Text;
 using System.Windows.Forms;
+using OpenTween.Api;
+using System.Threading.Tasks;
 
 namespace OpenTween.Setting.Panel
 {
@@ -53,10 +55,8 @@ namespace OpenTween.Setting.Panel
             this.ShortenTcoCheck.Enabled = this.CheckAutoConvertUrl.Checked;
 
             this.ComboBoxAutoShortUrlFirst.SelectedIndex = (int)settingCommon.AutoShortUrlFirst;
-            this.TextBitlyId.Text = settingCommon.BilyUser;
-            this.TextBitlyPw.Text = settingCommon.BitlyPwd;
-            this.TextBitlyId.Modified = false;
-            this.TextBitlyPw.Modified = false;
+            this.TextBitlyAccessToken.Text = settingCommon.BitlyAccessToken;
+            this.TextBitlyAccessToken.Modified = false;
         }
 
         public void SaveConfig(SettingCommon settingCommon)
@@ -65,8 +65,7 @@ namespace OpenTween.Setting.Panel
             settingCommon.UrlConvertAuto = this.CheckAutoConvertUrl.Checked;
             //settingCommon.ShortenTco = this.ShortenTcoCheck.Checked;
             settingCommon.AutoShortUrlFirst = (MyCommon.UrlConverter)this.ComboBoxAutoShortUrlFirst.SelectedIndex;
-            settingCommon.BilyUser = this.TextBitlyId.Text;
-            settingCommon.BitlyPwd = this.TextBitlyPw.Text;
+            settingCommon.BitlyAccessToken = this.TextBitlyAccessToken.Text;
         }
 
         private void ComboBoxAutoShortUrlFirst_SelectedIndexChanged(object sender, EventArgs e)
@@ -74,17 +73,15 @@ namespace OpenTween.Setting.Panel
             if (ComboBoxAutoShortUrlFirst.SelectedIndex == (int)MyCommon.UrlConverter.Bitly ||
                ComboBoxAutoShortUrlFirst.SelectedIndex == (int)MyCommon.UrlConverter.Jmp)
             {
-                Label76.Enabled = true;
                 Label77.Enabled = true;
-                TextBitlyId.Enabled = true;
-                TextBitlyPw.Enabled = true;
+                TextBitlyAccessToken.Enabled = true;
+                ButtonBitlyAuthorize.Enabled = true;
             }
             else
             {
-                Label76.Enabled = false;
                 Label77.Enabled = false;
-                TextBitlyId.Enabled = false;
-                TextBitlyPw.Enabled = false;
+                TextBitlyAccessToken.Enabled = false;
+                ButtonBitlyAuthorize.Enabled = false;
             }
         }
 
@@ -92,5 +89,36 @@ namespace OpenTween.Setting.Panel
         {
             ShortenTcoCheck.Enabled = CheckAutoConvertUrl.Checked;
         }
+
+        private void ButtonBitlyAuthorize_Click(object sender, EventArgs e)
+        {
+            using (var dialog = new LoginDialog())
+            {
+                const string DialogText = "Bitly Login";
+                dialog.Text = DialogText;
+
+                string accessToken = null;
+                dialog.LoginCallback = async () =>
+                {
+                    try
+                    {
+                        var bitly = new BitlyApi();
+                        accessToken = await bitly.GetAccessTokenAsync(dialog.LoginName, dialog.Password);
+                        return true;
+                    }
+                    catch (WebApiException ex)
+                    {
+                        var text = string.Format(Properties.Resources.BitlyAuthorize_ErrorText, ex.Message);
+                        MessageBox.Show(dialog, text, DialogText, MessageBoxButtons.OK, MessageBoxIcon.Error);
+                        return false;
+                    }
+                };
+
+                if (dialog.ShowDialog(this.ParentForm) == DialogResult.OK)
+                {
+                    this.TextBitlyAccessToken.Text = accessToken;
+                }
+            }
+        }
     }
 }
index 4dfc4cd..2ecd57d 100644 (file)
   <data name="Label71.Text" xml:space="preserve">
     <value>Primary URLshorten service</value>
   </data>
+  <data name="Label77.Size" type="System.Drawing.Size, System.Drawing">
+    <value>108, 12</value>
+  </data>
+  <data name="Label77.Text" xml:space="preserve">
+    <value>Bit.ly Access Token</value>
+  </data>
+  <data name="ButtonBitlyAuthorize.Text" xml:space="preserve">
+    <value>Authorize</value>
+  </data>
 </root>
\ No newline at end of file
index a6b97cb..41f6b3f 100644 (file)
     <value>95, 16</value>
   </data>
   <data name="ShortenTcoCheck.TabIndex" type="System.Int32, mscorlib">
-    <value>18</value>
+    <value>2</value>
   </data>
   <data name="ShortenTcoCheck.Text" xml:space="preserve">
     <value>t.coで短縮する</value>
     <value>122, 16</value>
   </data>
   <data name="CheckTinyURL.TabIndex" type="System.Int32, mscorlib">
-    <value>10</value>
+    <value>0</value>
   </data>
   <data name="CheckTinyURL.Text" xml:space="preserve">
     <value>短縮URLを解決する</value>
   <data name="&gt;&gt;CheckTinyURL.ZOrder" xml:space="preserve">
     <value>1</value>
   </data>
-  <data name="TextBitlyPw.Location" type="System.Drawing.Point, System.Drawing">
-    <value>419, 127</value>
+  <data name="TextBitlyAccessToken.Location" type="System.Drawing.Point, System.Drawing">
+    <value>249, 131</value>
   </data>
-  <data name="TextBitlyPw.Size" type="System.Drawing.Size, System.Drawing">
-    <value>70, 19</value>
+  <data name="TextBitlyAccessToken.Size" type="System.Drawing.Size, System.Drawing">
+    <value>173, 19</value>
   </data>
-  <data name="TextBitlyPw.TabIndex" type="System.Int32, mscorlib">
-    <value>17</value>
+  <data name="TextBitlyAccessToken.TabIndex" type="System.Int32, mscorlib">
+    <value>6</value>
   </data>
-  <data name="&gt;&gt;TextBitlyPw.Name" xml:space="preserve">
-    <value>TextBitlyPw</value>
+  <data name="&gt;&gt;TextBitlyAccessToken.Name" xml:space="preserve">
+    <value>TextBitlyAccessToken</value>
   </data>
-  <data name="&gt;&gt;TextBitlyPw.Type" xml:space="preserve">
+  <data name="&gt;&gt;TextBitlyAccessToken.Type" xml:space="preserve">
     <value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </data>
-  <data name="&gt;&gt;TextBitlyPw.Parent" xml:space="preserve">
+  <data name="&gt;&gt;TextBitlyAccessToken.Parent" xml:space="preserve">
     <value>$this</value>
   </data>
-  <data name="&gt;&gt;TextBitlyPw.ZOrder" xml:space="preserve">
+  <data name="&gt;&gt;TextBitlyAccessToken.ZOrder" xml:space="preserve">
     <value>2</value>
   </data>
   <data name="CheckAutoConvertUrl.AutoSize" type="System.Boolean, mscorlib">
     <value>242, 16</value>
   </data>
   <data name="CheckAutoConvertUrl.TabIndex" type="System.Int32, mscorlib">
-    <value>11</value>
+    <value>1</value>
   </data>
   <data name="CheckAutoConvertUrl.Text" xml:space="preserve">
     <value>入力欄のURLを投稿する際に自動で短縮する</value>
     <value>154, 12</value>
   </data>
   <data name="Label71.TabIndex" type="System.Int32, mscorlib">
-    <value>12</value>
+    <value>3</value>
   </data>
   <data name="Label71.Text" xml:space="preserve">
     <value>URL自動短縮で優先的に使用</value>
     <value>246, 20</value>
   </data>
   <data name="ComboBoxAutoShortUrlFirst.TabIndex" type="System.Int32, mscorlib">
-    <value>13</value>
+    <value>4</value>
   </data>
   <data name="&gt;&gt;ComboBoxAutoShortUrlFirst.Name" xml:space="preserve">
     <value>ComboBoxAutoShortUrlFirst</value>
   <data name="&gt;&gt;ComboBoxAutoShortUrlFirst.ZOrder" xml:space="preserve">
     <value>5</value>
   </data>
-  <data name="Label76.AutoSize" type="System.Boolean, mscorlib">
-    <value>True</value>
-  </data>
-  <data name="Label76.ImeMode" type="System.Windows.Forms.ImeMode, System.Windows.Forms">
-    <value>NoControl</value>
-  </data>
-  <data name="Label76.Location" type="System.Drawing.Point, System.Drawing">
-    <value>246, 130</value>
-  </data>
-  <data name="Label76.Size" type="System.Drawing.Size, System.Drawing">
-    <value>16, 12</value>
-  </data>
-  <data name="Label76.TabIndex" type="System.Int32, mscorlib">
-    <value>14</value>
-  </data>
-  <data name="Label76.Text" xml:space="preserve">
-    <value>ID</value>
-  </data>
-  <data name="&gt;&gt;Label76.Name" xml:space="preserve">
-    <value>Label76</value>
-  </data>
-  <data name="&gt;&gt;Label76.Type" xml:space="preserve">
-    <value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </data>
-  <data name="&gt;&gt;Label76.Parent" xml:space="preserve">
-    <value>$this</value>
-  </data>
-  <data name="&gt;&gt;Label76.ZOrder" xml:space="preserve">
-    <value>6</value>
-  </data>
   <data name="Label77.AutoSize" type="System.Boolean, mscorlib">
     <value>True</value>
   </data>
     <value>NoControl</value>
   </data>
   <data name="Label77.Location" type="System.Drawing.Point, System.Drawing">
-    <value>364, 130</value>
+    <value>23, 134</value>
   </data>
   <data name="Label77.Size" type="System.Drawing.Size, System.Drawing">
-    <value>42, 12</value>
+    <value>106, 12</value>
   </data>
   <data name="Label77.TabIndex" type="System.Int32, mscorlib">
-    <value>16</value>
+    <value>5</value>
   </data>
   <data name="Label77.Text" xml:space="preserve">
-    <value>APIKey</value>
+    <value>Bit.ly アクセストークン</value>
   </data>
   <data name="&gt;&gt;Label77.Name" xml:space="preserve">
     <value>Label77</value>
   <data name="&gt;&gt;Label77.ZOrder" xml:space="preserve">
     <value>7</value>
   </data>
-  <data name="TextBitlyId.Location" type="System.Drawing.Point, System.Drawing">
-    <value>273, 127</value>
+  <data name="ButtonBitlyAuthorize.Location" type="System.Drawing.Point, System.Drawing">
+    <value>429, 129</value>
+  </data>
+  <data name="ButtonBitlyAuthorize.Size" type="System.Drawing.Size, System.Drawing">
+    <value>67, 23</value>
   </data>
-  <data name="TextBitlyId.Size" type="System.Drawing.Size, System.Drawing">
-    <value>71, 19</value>
+  <data name="ButtonBitlyAuthorize.TabIndex" type="System.Int32, mscorlib">
+    <value>7</value>
   </data>
-  <data name="TextBitlyId.TabIndex" type="System.Int32, mscorlib">
-    <value>15</value>
+  <data name="ButtonBitlyAuthorize.Text" xml:space="preserve">
+    <value>認可</value>
   </data>
-  <data name="&gt;&gt;TextBitlyId.Name" xml:space="preserve">
-    <value>TextBitlyId</value>
+  <data name="&gt;&gt;ButtonBitlyAuthorize.Name" xml:space="preserve">
+    <value>ButtonBitlyAuthorize</value>
   </data>
-  <data name="&gt;&gt;TextBitlyId.Type" xml:space="preserve">
-    <value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  <data name="&gt;&gt;ButtonBitlyAuthorize.Type" xml:space="preserve">
+    <value>System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </data>
-  <data name="&gt;&gt;TextBitlyId.Parent" xml:space="preserve">
+  <data name="&gt;&gt;ButtonBitlyAuthorize.Parent" xml:space="preserve">
     <value>$this</value>
   </data>
-  <data name="&gt;&gt;TextBitlyId.ZOrder" xml:space="preserve">
-    <value>8</value>
+  <data name="&gt;&gt;ButtonBitlyAuthorize.ZOrder" xml:space="preserve">
+    <value>6</value>
   </data>
   <metadata name="$this.Localizable" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>