mirror of
https://github.com/cyanfish/naps2.git
synced 2024-10-04 19:37:15 +03:00
Clean up oauth structure
This commit is contained in:
parent
dcbc429f8d
commit
32ffe839d8
@ -1,88 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using NAPS2.Config;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace NAPS2.ImportExport.Email.Imap
|
||||
{
|
||||
public partial class GmailApi : OauthApi, IOauthProvider
|
||||
{
|
||||
private const string OAUTH_SCOPE = "https://www.googleapis.com/auth/gmail.compose";
|
||||
|
||||
private readonly IUserConfigManager userConfigManager;
|
||||
|
||||
private OauthClientCreds creds;
|
||||
|
||||
public GmailApi(IUserConfigManager userConfigManager)
|
||||
{
|
||||
this.userConfigManager = userConfigManager;
|
||||
}
|
||||
|
||||
public override OauthToken Token => userConfigManager.Config.EmailSetup?.GmailToken;
|
||||
|
||||
public string UserId => userConfigManager.Config.EmailSetup?.GmailUser;
|
||||
|
||||
public bool HasClientCreds => Creds.ClientId != null;
|
||||
|
||||
private OauthClientCreds Creds
|
||||
{
|
||||
get
|
||||
{
|
||||
if (creds == null)
|
||||
{
|
||||
var credObj = JObject.Parse(Encoding.UTF8.GetString(ClientCreds.google_credentials));
|
||||
var installed = credObj.Value<JObject>("installed");
|
||||
creds = new OauthClientCreds(installed?.Value<string>("client_id"), installed?.Value<string>("client_secret"));
|
||||
}
|
||||
return creds;
|
||||
}
|
||||
}
|
||||
|
||||
public string OauthUrl(string state, string redirectUri)
|
||||
{
|
||||
// TODO: Check tls settings as in FDownloadProgress
|
||||
return "https://accounts.google.com/o/oauth2/v2/auth?"
|
||||
+ $"scope={OAUTH_SCOPE}&response_type=code&state={state}&redirect_uri={redirectUri}&client_id={Creds.ClientId}";
|
||||
}
|
||||
|
||||
public OauthToken AcquireToken(string code, string redirectUri)
|
||||
{
|
||||
var resp = Post("https://www.googleapis.com/oauth2/v4/token", new NameValueCollection
|
||||
{
|
||||
{"code", code},
|
||||
{"client_id", Creds.ClientId},
|
||||
{"client_secret", Creds.ClientSecret},
|
||||
{"redirect_uri", redirectUri},
|
||||
{"grant_type", "authorization_code"}
|
||||
});
|
||||
return new OauthToken
|
||||
{
|
||||
AccessToken = resp.Value<string>("access_token"),
|
||||
RefreshToken = resp.Value<string>("refresh_token"),
|
||||
Expiry = DateTime.Now.AddSeconds(resp.Value<int>("expires_in"))
|
||||
};
|
||||
}
|
||||
|
||||
public void RefreshToken()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string GetEmail()
|
||||
{
|
||||
var resp = Get("https://www.googleapis.com/gmail/v1/users/me/profile");
|
||||
return resp.Value<string>("emailAddress");
|
||||
}
|
||||
|
||||
public string UploadDraft(string messageRaw)
|
||||
{
|
||||
var resp = Post($"https://www.googleapis.com/upload/gmail/v1/users/{UserId}/drafts?uploadType=multipart", messageRaw, "message/rfc822");
|
||||
return resp.Value<string>("id");
|
||||
}
|
||||
}
|
||||
}
|
@ -12,12 +12,12 @@ namespace NAPS2.ImportExport.Email.Imap
|
||||
public class GmailEmailProvider : MimeEmailProvider
|
||||
{
|
||||
private readonly IUserConfigManager userConfigManager;
|
||||
private readonly GmailApi gmailApi;
|
||||
private readonly GmailOauthProvider gmailOauthProvider;
|
||||
|
||||
public GmailEmailProvider(IUserConfigManager userConfigManager, GmailApi gmailApi)
|
||||
public GmailEmailProvider(IUserConfigManager userConfigManager, GmailOauthProvider gmailOauthProvider)
|
||||
{
|
||||
this.userConfigManager = userConfigManager;
|
||||
this.gmailApi = gmailApi;
|
||||
this.gmailOauthProvider = gmailOauthProvider;
|
||||
}
|
||||
|
||||
protected string Host => "imap.gmail.com";
|
||||
@ -38,7 +38,7 @@ namespace NAPS2.ImportExport.Email.Imap
|
||||
|
||||
protected override void SendMimeMessage(MimeMessage message)
|
||||
{
|
||||
gmailApi.UploadDraft(message.ToString());//Convert.ToBase64String(Encoding.UTF8.GetBytes(message.ToString()))
|
||||
gmailOauthProvider.UploadDraft(message.ToString());//Convert.ToBase64String(Encoding.UTF8.GetBytes(message.ToString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
86
NAPS2.Core/ImportExport/Email/Imap/GmailOauthProvider.cs
Normal file
86
NAPS2.Core/ImportExport/Email/Imap/GmailOauthProvider.cs
Normal file
@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using NAPS2.Config;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace NAPS2.ImportExport.Email.Imap
|
||||
{
|
||||
public class GmailOauthProvider : OauthProvider
|
||||
{
|
||||
private readonly IUserConfigManager userConfigManager;
|
||||
|
||||
private OauthClientCreds creds;
|
||||
|
||||
public GmailOauthProvider(IUserConfigManager userConfigManager)
|
||||
{
|
||||
this.userConfigManager = userConfigManager;
|
||||
}
|
||||
|
||||
#region Authorization
|
||||
|
||||
public override OauthToken Token
|
||||
{
|
||||
get => userConfigManager.Config.EmailSetup?.GmailToken;
|
||||
protected set
|
||||
{
|
||||
userConfigManager.Config.EmailSetup = userConfigManager.Config.EmailSetup ?? new EmailSetup();
|
||||
userConfigManager.Config.EmailSetup.GmailToken = value;
|
||||
userConfigManager.Save();
|
||||
}
|
||||
}
|
||||
|
||||
public override string User
|
||||
{
|
||||
get => userConfigManager.Config.EmailSetup?.GmailUser;
|
||||
protected set
|
||||
{
|
||||
userConfigManager.Config.EmailSetup = userConfigManager.Config.EmailSetup ?? new EmailSetup();
|
||||
userConfigManager.Config.EmailSetup.GmailUser = value;
|
||||
userConfigManager.Save();
|
||||
}
|
||||
}
|
||||
|
||||
protected override OauthClientCreds Creds
|
||||
{
|
||||
get
|
||||
{
|
||||
if (creds == null)
|
||||
{
|
||||
var credObj = JObject.Parse(Encoding.UTF8.GetString(ClientCreds.google_credentials));
|
||||
var installed = credObj.Value<JObject>("installed");
|
||||
creds = new OauthClientCreds(installed?.Value<string>("client_id"), installed?.Value<string>("client_secret"));
|
||||
}
|
||||
return creds;
|
||||
}
|
||||
}
|
||||
|
||||
protected override string Scope => "https://www.googleapis.com/auth/gmail.compose";
|
||||
|
||||
protected override string CodeEndpoint => "https://accounts.google.com/o/oauth2/v2/auth";
|
||||
|
||||
protected override string TokenEndpoint => "https://www.googleapis.com/oauth2/v4/token";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Api Methods
|
||||
|
||||
protected override string GetUser()
|
||||
{
|
||||
var resp = GetAuthorized("https://www.googleapis.com/gmail/v1/users/me/profile");
|
||||
return resp.Value<string>("emailAddress");
|
||||
}
|
||||
|
||||
public string UploadDraft(string messageRaw)
|
||||
{
|
||||
var resp = PostAuthorized($"https://www.googleapis.com/upload/gmail/v1/users/{User}/drafts?uploadType=multipart", messageRaw, "message/rfc822");
|
||||
return resp.Value<string>("id");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NAPS2.ImportExport.Email.Imap
|
||||
{
|
||||
public interface IOauthProvider
|
||||
{
|
||||
OauthToken Token { get; }
|
||||
|
||||
string OauthUrl(string state, string redirectUri);
|
||||
|
||||
OauthToken AcquireToken(string code, string redirectUri);
|
||||
|
||||
void RefreshToken();
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace NAPS2.ImportExport.Email.Imap
|
||||
{
|
||||
public abstract class OauthApi
|
||||
{
|
||||
public abstract OauthToken Token { get; }
|
||||
|
||||
protected JObject Get(string url)
|
||||
{
|
||||
using (var client = ApiClient())
|
||||
{
|
||||
string response = client.DownloadString(url);
|
||||
return JObject.Parse(response);
|
||||
}
|
||||
}
|
||||
|
||||
protected JObject Post(string url, NameValueCollection values)
|
||||
{
|
||||
using (var client = ApiClient())
|
||||
{
|
||||
string response = Encoding.UTF8.GetString(client.UploadValues(url, "POST", values));
|
||||
return JObject.Parse(response);
|
||||
}
|
||||
}
|
||||
|
||||
protected JObject Post(string url, string body, string contentType)
|
||||
{
|
||||
using (var client = ApiClient())
|
||||
{
|
||||
client.Headers.Add("Content-Type", contentType);
|
||||
string response = client.UploadString(url, "POST", body);
|
||||
return JObject.Parse(response);
|
||||
}
|
||||
}
|
||||
|
||||
private WebClient ApiClient()
|
||||
{
|
||||
var client = new WebClient();
|
||||
var token = Token;
|
||||
if (token != null)
|
||||
{
|
||||
// TODO: Refresh mechanism
|
||||
client.Headers.Add("Authorization", $"Bearer {token.AccessToken}");
|
||||
}
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
namespace NAPS2.ImportExport.Email.Imap
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NAPS2.ImportExport.Email.Imap
|
||||
{
|
||||
internal class OauthClientCreds
|
||||
public class OauthClientCreds
|
||||
{
|
||||
public OauthClientCreds(string clientId, string clientSecret)
|
||||
{
|
||||
|
165
NAPS2.Core/ImportExport/Email/Imap/OauthProvider.cs
Normal file
165
NAPS2.Core/ImportExport/Email/Imap/OauthProvider.cs
Normal file
@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NAPS2.Util;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace NAPS2.ImportExport.Email.Imap
|
||||
{
|
||||
public abstract class OauthProvider
|
||||
{
|
||||
public abstract OauthToken Token { get; protected set; }
|
||||
|
||||
public abstract string User { get; protected set; }
|
||||
|
||||
protected abstract OauthClientCreds Creds { get; }
|
||||
|
||||
protected abstract string CodeEndpoint { get; }
|
||||
|
||||
protected abstract string TokenEndpoint { get; }
|
||||
|
||||
protected abstract string Scope { get; }
|
||||
|
||||
public void AcquireToken(CancellationToken cancelToken)
|
||||
{
|
||||
// Initialize state, port, and redirectUri
|
||||
byte[] buffer = new byte[16];
|
||||
SecureStorage.CryptoRandom.Value.GetBytes(buffer);
|
||||
string state = string.Join("", buffer.Select(b => b.ToString("x")));
|
||||
// There's a possible race condition here with the port, but meh
|
||||
int port = GetUnusedPort();
|
||||
var redirectUri = $"http://127.0.0.1:{port}/";
|
||||
|
||||
// Listen on the redirect uri for the code
|
||||
var listener = new HttpListener();
|
||||
listener.Prefixes.Add(redirectUri);
|
||||
listener.Start();
|
||||
|
||||
// Abort the listener if the user cancels
|
||||
cancelToken.Register(() => listener.Abort());
|
||||
cancelToken.ThrowIfCancellationRequested();
|
||||
// TODO: Catch exception on abort
|
||||
|
||||
// Open the user interface (which will redirect to our localhost listener)
|
||||
var url = $"{CodeEndpoint}?scope={Scope}&response_type=code&state={state}&redirect_uri={redirectUri}&client_id={Creds.ClientId}";
|
||||
Process.Start(url);
|
||||
|
||||
// Wait for the authorization code to be sent to the local socket
|
||||
string code;
|
||||
while (true)
|
||||
{
|
||||
var ctx = listener.GetContext();
|
||||
var queryString = ctx.Request.QueryString;
|
||||
|
||||
string responseString = "<script>location.href = 'about:blank';</script>";
|
||||
byte[] responseBytes = Encoding.UTF8.GetBytes(responseString);
|
||||
var response = ctx.Response;
|
||||
response.ContentLength64 = responseBytes.Length;
|
||||
response.OutputStream.Write(responseBytes, 0, responseBytes.Length);
|
||||
response.OutputStream.Close();
|
||||
|
||||
// Validate the state (standard oauth2 security)
|
||||
string requestState = queryString.Get("state");
|
||||
if (requestState == state)
|
||||
{
|
||||
// Yay, we got an authorization code
|
||||
code = queryString.Get("code");
|
||||
break;
|
||||
}
|
||||
}
|
||||
listener.Stop();
|
||||
cancelToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Trade the code in for a token
|
||||
var resp = PostAuthorized(TokenEndpoint, new NameValueCollection
|
||||
{
|
||||
{"code", code},
|
||||
{"client_id", Creds.ClientId},
|
||||
{"client_secret", Creds.ClientSecret},
|
||||
{"redirect_uri", redirectUri},
|
||||
{"grant_type", "authorization_code"}
|
||||
});
|
||||
Token = new OauthToken
|
||||
{
|
||||
AccessToken = resp.Value<string>("access_token"),
|
||||
RefreshToken = resp.Value<string>("refresh_token"),
|
||||
Expiry = DateTime.Now.AddSeconds(resp.Value<int>("expires_in"))
|
||||
};
|
||||
|
||||
// Get the user id
|
||||
User = GetUser();
|
||||
}
|
||||
|
||||
private static int GetUnusedPort()
|
||||
{
|
||||
var listener = new TcpListener(IPAddress.Any, 0);
|
||||
listener.Start();
|
||||
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||
listener.Stop();
|
||||
return port;
|
||||
}
|
||||
|
||||
protected abstract string GetUser();
|
||||
|
||||
public void RefreshToken()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected JObject Get(string url)
|
||||
{
|
||||
using (var client = new WebClient())
|
||||
{
|
||||
string response = client.DownloadString(url);
|
||||
return JObject.Parse(response);
|
||||
}
|
||||
}
|
||||
|
||||
protected JObject GetAuthorized(string url)
|
||||
{
|
||||
using (var client = AuthorizedClient())
|
||||
{
|
||||
string response = client.DownloadString(url);
|
||||
return JObject.Parse(response);
|
||||
}
|
||||
}
|
||||
|
||||
protected JObject PostAuthorized(string url, NameValueCollection values)
|
||||
{
|
||||
using (var client = AuthorizedClient())
|
||||
{
|
||||
string response = Encoding.UTF8.GetString(client.UploadValues(url, "POST", values));
|
||||
return JObject.Parse(response);
|
||||
}
|
||||
}
|
||||
|
||||
protected JObject PostAuthorized(string url, string body, string contentType)
|
||||
{
|
||||
using (var client = AuthorizedClient())
|
||||
{
|
||||
client.Headers.Add("Content-Type", contentType);
|
||||
string response = client.UploadString(url, "POST", body);
|
||||
return JObject.Parse(response);
|
||||
}
|
||||
}
|
||||
|
||||
private WebClient AuthorizedClient()
|
||||
{
|
||||
var client = new WebClient();
|
||||
var token = Token;
|
||||
if (token != null)
|
||||
{
|
||||
// TODO: Refresh mechanism
|
||||
client.Headers.Add("Authorization", $"Bearer {token.AccessToken}");
|
||||
}
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
@ -143,11 +143,10 @@
|
||||
<Compile Include="ImportExport\Email\EmailProviderType.cs" />
|
||||
<Compile Include="ImportExport\Email\EmailSetup.cs" />
|
||||
<Compile Include="ImportExport\Email\IEmailProviderFactory.cs" />
|
||||
<Compile Include="ImportExport\Email\Imap\GmailApi.cs" />
|
||||
<Compile Include="ImportExport\Email\Imap\GmailOauthProvider.cs" />
|
||||
<Compile Include="ImportExport\Email\Imap\GmailEmailProvider.cs" />
|
||||
<Compile Include="ImportExport\Email\Imap\IOauthProvider.cs" />
|
||||
<Compile Include="ImportExport\Email\Imap\MimeEmailProvider.cs" />
|
||||
<Compile Include="ImportExport\Email\Imap\OauthApi.cs" />
|
||||
<Compile Include="ImportExport\Email\Imap\OauthProvider.cs" />
|
||||
<Compile Include="ImportExport\Email\Imap\OauthClientCreds.cs" />
|
||||
<Compile Include="ImportExport\Email\Imap\OauthToken.cs" />
|
||||
<Compile Include="ImportExport\IAutoSave.cs" />
|
||||
|
10
NAPS2.Core/WinForms/FAuthorize.Designer.cs
generated
10
NAPS2.Core/WinForms/FAuthorize.Designer.cs
generated
@ -30,7 +30,6 @@
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FAuthorize));
|
||||
this.lblWaiting = new System.Windows.Forms.Label();
|
||||
this.linkTryAgain = new System.Windows.Forms.LinkLabel();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
@ -39,13 +38,6 @@
|
||||
resources.ApplyResources(this.lblWaiting, "lblWaiting");
|
||||
this.lblWaiting.Name = "lblWaiting";
|
||||
//
|
||||
// linkTryAgain
|
||||
//
|
||||
resources.ApplyResources(this.linkTryAgain, "linkTryAgain");
|
||||
this.linkTryAgain.Name = "linkTryAgain";
|
||||
this.linkTryAgain.TabStop = true;
|
||||
this.linkTryAgain.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkTryAgain_LinkClicked);
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
resources.ApplyResources(this.btnCancel, "btnCancel");
|
||||
@ -59,7 +51,6 @@
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.btnCancel;
|
||||
this.Controls.Add(this.btnCancel);
|
||||
this.Controls.Add(this.linkTryAgain);
|
||||
this.Controls.Add(this.lblWaiting);
|
||||
this.Name = "FAuthorize";
|
||||
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FAuthorize_FormClosed);
|
||||
@ -72,7 +63,6 @@
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Label lblWaiting;
|
||||
private System.Windows.Forms.LinkLabel linkTryAgain;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using NAPS2.ImportExport.Email.Imap;
|
||||
using NAPS2.Util;
|
||||
@ -19,9 +20,7 @@ namespace NAPS2.WinForms
|
||||
{
|
||||
private readonly ThreadFactory threadFactory;
|
||||
|
||||
private string state;
|
||||
private int port;
|
||||
private HttpListener listener;
|
||||
private CancellationTokenSource cancelTokenSource;
|
||||
|
||||
public FAuthorize(ThreadFactory threadFactory)
|
||||
{
|
||||
@ -31,92 +30,34 @@ namespace NAPS2.WinForms
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public IOauthProvider OauthProvider { get; set; }
|
||||
|
||||
public OauthToken Token { get; private set; }
|
||||
public OauthProvider OauthProvider { get; set; }
|
||||
|
||||
private void FAuthorize_Load(object sender, EventArgs e)
|
||||
{
|
||||
MaximumSize = new Size(Math.Max(lblWaiting.Width + 142, 272), Height);
|
||||
MinimumSize = new Size(Math.Max(lblWaiting.Width + 142, 272), Height);
|
||||
|
||||
InitState();
|
||||
OpenSocket();
|
||||
OpenOauthUrl();
|
||||
}
|
||||
|
||||
private void InitState()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
SecureStorage.CryptoRandom.Value.GetBytes(buffer);
|
||||
state = string.Join("", buffer.Select(b => b.ToString("x")));
|
||||
// There's a possible race condition here with the port, but meh
|
||||
port = GetUnusedPort();
|
||||
}
|
||||
|
||||
private void linkTryAgain_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
||||
{
|
||||
listener.Abort();
|
||||
OpenSocket();
|
||||
OpenOauthUrl();
|
||||
}
|
||||
|
||||
private void OpenSocket()
|
||||
{
|
||||
cancelTokenSource = new CancellationTokenSource();
|
||||
threadFactory.StartThread(() =>
|
||||
{
|
||||
listener = new HttpListener();
|
||||
listener.Prefixes.Add(RedirectUri);
|
||||
listener.Start();
|
||||
while (true)
|
||||
try
|
||||
{
|
||||
var ctx = listener.GetContext();
|
||||
var queryString = ctx.Request.QueryString;
|
||||
|
||||
string responseString = "<script>location.href = 'about:blank';</script>";
|
||||
byte[] responseBytes = Encoding.UTF8.GetBytes(responseString);
|
||||
var response = ctx.Response;
|
||||
response.ContentLength64 = responseBytes.Length;
|
||||
response.OutputStream.Write(responseBytes, 0, responseBytes.Length);
|
||||
response.OutputStream.Close();
|
||||
|
||||
string requestState = queryString.Get("state");
|
||||
if (requestState == state)
|
||||
OauthProvider.AcquireToken(cancelTokenSource.Token);
|
||||
Invoke(() =>
|
||||
{
|
||||
string code = queryString.Get("code");
|
||||
Token = OauthProvider.AcquireToken(code, RedirectUri);
|
||||
break;
|
||||
}
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
});
|
||||
}
|
||||
listener.Stop();
|
||||
|
||||
Invoke(() =>
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private string RedirectUri => $"http://127.0.0.1:{port}/";
|
||||
|
||||
private void OpenOauthUrl()
|
||||
{
|
||||
Process.Start(OauthProvider.OauthUrl(state, RedirectUri));
|
||||
}
|
||||
|
||||
private static int GetUnusedPort()
|
||||
{
|
||||
var listener = new TcpListener(IPAddress.Any, 0);
|
||||
listener.Start();
|
||||
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||
listener.Stop();
|
||||
return port;
|
||||
}
|
||||
|
||||
private void FAuthorize_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
listener?.Abort();
|
||||
cancelTokenSource?.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,7 @@
|
||||
</data>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="lblWaiting.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>12, 9</value>
|
||||
<value>12, 17</value>
|
||||
</data>
|
||||
<data name="lblWaiting.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>130, 13</value>
|
||||
@ -144,33 +144,6 @@
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>lblWaiting.ZOrder" xml:space="preserve">
|
||||
<value>2</value>
|
||||
</data>
|
||||
<data name="linkTryAgain.AutoSize" type="System.Boolean, mscorlib">
|
||||
<value>True</value>
|
||||
</data>
|
||||
<data name="linkTryAgain.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>12, 25</value>
|
||||
</data>
|
||||
<data name="linkTryAgain.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>51, 13</value>
|
||||
</data>
|
||||
<data name="linkTryAgain.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>1</value>
|
||||
</data>
|
||||
<data name="linkTryAgain.Text" xml:space="preserve">
|
||||
<value>Try again</value>
|
||||
</data>
|
||||
<data name=">>linkTryAgain.Name" xml:space="preserve">
|
||||
<value>linkTryAgain</value>
|
||||
</data>
|
||||
<data name=">>linkTryAgain.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.LinkLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>linkTryAgain.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>linkTryAgain.ZOrder" xml:space="preserve">
|
||||
<value>1</value>
|
||||
</data>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
@ -536,6 +509,6 @@
|
||||
<value>FAuthorize</value>
|
||||
</data>
|
||||
<data name=">>$this.Type" xml:space="preserve">
|
||||
<value>NAPS2.WinForms.FormBase, NAPS2.Core, Version=5.8.2.41756, Culture=neutral, PublicKeyToken=null</value>
|
||||
<value>NAPS2.WinForms.FormBase, NAPS2.Core, Version=5.8.2.37340, Culture=neutral, PublicKeyToken=null</value>
|
||||
</data>
|
||||
</root>
|
@ -15,17 +15,15 @@ namespace NAPS2.WinForms
|
||||
{
|
||||
public partial class FEmailProvider : FormBase
|
||||
{
|
||||
private readonly IEmailProviderFactory emailProviderFactory;
|
||||
private readonly GmailApi gmailApi;
|
||||
private readonly GmailOauthProvider gmailOauthProvider;
|
||||
|
||||
private List<EmailProviderWidget> providerWidgets;
|
||||
private string[] systemClientNames;
|
||||
private string defaultSystemClientName;
|
||||
|
||||
public FEmailProvider(IEmailProviderFactory emailProviderFactory, GmailApi gmailApi)
|
||||
public FEmailProvider(GmailOauthProvider gmailOauthProvider)
|
||||
{
|
||||
this.emailProviderFactory = emailProviderFactory;
|
||||
this.gmailApi = gmailApi;
|
||||
this.gmailOauthProvider = gmailOauthProvider;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
@ -93,14 +91,12 @@ namespace NAPS2.WinForms
|
||||
private void ChooseGmail()
|
||||
{
|
||||
var authForm = FormFactory.Create<FAuthorize>();
|
||||
authForm.OauthProvider = gmailApi;
|
||||
authForm.OauthProvider = gmailOauthProvider;
|
||||
authForm.ShowDialog();
|
||||
if (authForm.DialogResult == DialogResult.OK)
|
||||
{
|
||||
var setup = GetOrCreateSetup();
|
||||
setup.ProviderType = EmailProviderType.Gmail;
|
||||
setup.GmailToken = authForm.Token;
|
||||
setup.GmailUser = gmailApi.GetEmail();
|
||||
UserConfigManager.Save();
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
|
Loading…
Reference in New Issue
Block a user