diff --git a/PowertoysRunTOTP.csproj b/Community.PowerToys.Run.Plugin.TOTP.csproj similarity index 92% rename from PowertoysRunTOTP.csproj rename to Community.PowerToys.Run.Plugin.TOTP.csproj index 5a2ab09..c3af436 100644 --- a/PowertoysRunTOTP.csproj +++ b/Community.PowerToys.Run.Plugin.TOTP.csproj @@ -9,7 +9,7 @@ False Debug;Release Zapic - 1.2.1 + $([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)plugin.json').Split(',')[5].Split(':')[1].Trim().Trim('"')) Powertoys Run TOTP Powertoys Run TOTP Plugin https://github.com/KawaiiZapic/PowertoysRunTOTP diff --git a/Config.cs b/Config.cs index 040480b..2de0667 100644 --- a/Config.cs +++ b/Config.cs @@ -2,9 +2,10 @@ using System.Text.Json; using System.Security.Cryptography; using System.Text; +using Wox.Infrastructure.Storage; -namespace PowertoysRunTOTP { - public class ConfigStruct { +namespace Community.PowerToys.Run.Plugin.TOTP { + public class OTPList { public class KeyEntry { public string Name { get; set; } public string Key { get; set; } @@ -18,55 +19,21 @@ public class KeyEntry { public static class Config { - private static readonly string DataDirectory = Environment.ExpandEnvironmentVariables("%LOCALAPPDATA%") + "\\Microsoft\\PowerToys\\PowerToys Run\\Settings\\Plugins\\TOTP\\"; - private static readonly string ConfigPath = DataDirectory + "Config.json"; - private static readonly int Version = 1; - - public static ConfigStruct LoadConfig() { - var fileInfo = new FileInfo(ConfigPath); - if (!fileInfo.Exists) { - if (!fileInfo.Directory!.Exists) { - Directory.CreateDirectory(fileInfo.Directory!.FullName); - } - var newFile = File.Create(ConfigPath); - var options = new JsonSerializerOptions { WriteIndented = true }; - var EmptyConfig = new ConfigStruct { - Entries = new List(), - Version = Version, - }; - JsonSerializer.Serialize(newFile, EmptyConfig, options); - newFile.Dispose(); - } - var file = File.OpenRead(ConfigPath); - try { - var result = JsonSerializer.Deserialize(file) ?? throw new Exception("Failed to load config: Result is null"); - return result; - } finally { - file.Dispose(); - } - - } - - public static List LoadKeyList() { - return LoadConfig().Entries; - } - public static void SaveConfig(ConfigStruct config) { - var fileInfo = new FileInfo(ConfigPath); - if (!fileInfo.Directory!.Exists) { - Directory.CreateDirectory(fileInfo.Directory!.FullName); - } - var file = File.Open(ConfigPath, FileMode.Create); - var options = new JsonSerializerOptions { WriteIndented = true }; - try { - JsonSerializer.Serialize(file, config, options); - } finally { - file.Dispose(); + public static List LoadKeyList() { + var nc = new PluginJsonStorage(); + var config = nc.Load(); + if (config.Entries == null) { + config.Version = 2; + config.Entries = new List(); + nc.Save(); } + return config.Entries; } - public static void SaveKeyList(List list) { - var config = LoadConfig(); + public static void SaveKeyList(List list) { + var nc = new PluginJsonStorage(); + var config = nc.Load(); foreach (var entry in list) { if (entry.IsEncrypted != true) { entry.Key = EncryptKey(entry.Key); @@ -74,7 +41,7 @@ public static void SaveKeyList(List list) { } } config.Entries = list; - SaveConfig(config); + nc.Save(); } public static string DecryptKey(string encrypted) { @@ -136,4 +103,39 @@ public static void Migrate() { } } + public static class ConfigMigratorV1 { + private static readonly string DataDirectoryV1 = Environment.ExpandEnvironmentVariables("%LOCALAPPDATA%") + "\\Microsoft\\PowerToys\\PowerToys Run\\Settings\\Plugins\\TOTP\\"; + private static readonly string ConfigPathV1 = DataDirectoryV1 + "Config.json"; + + public class OTPList { + public class KeyEntry { + public string Name { get; set; } + public string Key { get; set; } + public bool IsEncrypted { get; set; } + + } + + public int Version { get; set; } + public List Entries { get; set; } + } + + public static void Migrate() { + if (!new FileInfo(ConfigPathV1).Exists) + return; + + var FileV1 = File.Open(ConfigPathV1, FileMode.Open); + var ConfigV1 = JsonSerializer.Deserialize(FileV1) ?? throw new Exception("Config should not be null"); + + var ConfigV2 = new PluginJsonStorage(); + var config = ConfigV2.Load(); + config.Version = 2; + config.Entries = ConfigV1.Entries; + ConfigV2.Save(); + + FileV1.Close(); + File.Delete(ConfigPathV1); + Directory.Delete(DataDirectoryV1); + } + + } } diff --git a/Main.cs b/Main.cs index 3e9042a..37d2786 100644 --- a/Main.cs +++ b/Main.cs @@ -4,9 +4,8 @@ using System.Web; using System.Windows; using OtpNet; -using PowertoysRunTOTP; -namespace PowerToysRunTOTP { +namespace Community.PowerToys.Run.Plugin.TOTP { public class Main: IPlugin { public static string PluginID => "2FC51DBA9F0F42108E26602486C186C1"; @@ -34,14 +33,14 @@ public List Query(Query query) { Action = (e) => { try { var list = Config.LoadKeyList(); - list.Add(new ConfigStruct.KeyEntry { + list.Add(new OTPList.KeyEntry { Key = sercet, Name = name, IsEncrypted = false }); Config.SaveKeyList(list); } catch (Exception ex) { - MessageBox.Show(ex.Message + ex.StackTrace, "PowerToys TOTP Ran into error", MessageBoxButton.OK, MessageBoxImage.Exclamation); + MessageBox.Show(ex.Message + "\n" + ex.StackTrace, "PowerToys TOTP Ran into error", MessageBoxButton.OK, MessageBoxImage.Exclamation); } return true; } @@ -83,7 +82,7 @@ public List Query(Query query) { } else { name = item.Issuer + ": " + item.Name; } - list.Add(new ConfigStruct.KeyEntry{ + list.Add(new OTPList.KeyEntry{ Name = name, Key = key, IsEncrypted = false @@ -92,7 +91,7 @@ public List Query(Query query) { try { Config.SaveKeyList(list); } catch(Exception ex) { - MessageBox.Show(ex.Message + ex.StackTrace, "PowerToys TOTP Ran into error", MessageBoxButton.OK, MessageBoxImage.Exclamation);} + MessageBox.Show(ex.Message + "\n" + ex.StackTrace, "PowerToys TOTP Ran into error", MessageBoxButton.OK, MessageBoxImage.Exclamation);} return true; } } @@ -110,7 +109,7 @@ public List Query(Query query) { }; } } - List totpList; + List totpList; try { totpList = Config.LoadKeyList(); } catch (Exception ex) { @@ -174,6 +173,9 @@ public void Init(PluginInitContext context) { try { ConfigMigratorV0.Migrate(); } catch (Exception) { } + try { + ConfigMigratorV1.Migrate(); + } catch (Exception) { } } private void UpdateIconPath(Theme theme) { diff --git a/OtpauthMigration.cs b/OtpauthMigration.cs index 5877bd1..28acd7a 100644 --- a/OtpauthMigration.cs +++ b/OtpauthMigration.cs @@ -8,7 +8,7 @@ using pb = global::Google.Protobuf; using pbc = global::Google.Protobuf.Collections; using pbr = global::Google.Protobuf.Reflection; -namespace PowerToysRunTOTP +namespace Community.PowerToys.Run.Plugin.TOTP { /// Holder for reflection information generated from i/i.proto @@ -46,7 +46,7 @@ static IReflection() descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::PowerToysRunTOTP.Payload), global::PowerToysRunTOTP.Payload.Parser, new[]{ "OtpParameters", "Version", "BatchSize", "BatchIndex", "BatchId" }, null, new[]{ typeof(global::PowerToysRunTOTP.Payload.Types.Algorithm), typeof(global::PowerToysRunTOTP.Payload.Types.DigitCount), typeof(global::PowerToysRunTOTP.Payload.Types.OtpType) }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::PowerToysRunTOTP.Payload.Types.OtpParameters), global::PowerToysRunTOTP.Payload.Types.OtpParameters.Parser, new[]{ "Secret", "Name", "Issuer", "Algorithm", "Digits", "Type", "Counter" }, null, null, null, null)}) + new pbr::GeneratedClrTypeInfo(typeof(global::Community.PowerToys.Run.Plugin.TOTP.Payload), global::Community.PowerToys.Run.Plugin.TOTP.Payload.Parser, new[]{ "OtpParameters", "Version", "BatchSize", "BatchIndex", "BatchId" }, null, new[]{ typeof(global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.Algorithm), typeof(global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.DigitCount), typeof(global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.OtpType) }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.OtpParameters), global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.OtpParameters.Parser, new[]{ "Secret", "Name", "Issuer", "Algorithm", "Digits", "Type", "Counter" }, null, null, null, null)}) })); } #endregion @@ -68,7 +68,7 @@ public sealed partial class Payload : pb::IMessage [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::PowerToysRunTOTP.IReflection.Descriptor.MessageTypes[0]; } + get { return global::Community.PowerToys.Run.Plugin.TOTP.IReflection.Descriptor.MessageTypes[0]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -108,12 +108,12 @@ public Payload Clone() /// Field number for the "otp_parameters" field. public const int OtpParametersFieldNumber = 1; - private static readonly pb::FieldCodec _repeated_otpParameters_codec - = pb::FieldCodec.ForMessage(10, global::PowerToysRunTOTP.Payload.Types.OtpParameters.Parser); - private readonly pbc::RepeatedField otpParameters_ = new pbc::RepeatedField(); + private static readonly pb::FieldCodec _repeated_otpParameters_codec + = pb::FieldCodec.ForMessage(10, global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.OtpParameters.Parser); + private readonly pbc::RepeatedField otpParameters_ = new pbc::RepeatedField(); [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public pbc::RepeatedField OtpParameters + public pbc::RepeatedField OtpParameters { get { return otpParameters_; } } @@ -470,7 +470,7 @@ public sealed partial class OtpParameters : pb::IMessage [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::PowerToysRunTOTP.Payload.Descriptor.NestedTypes[0]; } + get { return global::Community.PowerToys.Run.Plugin.TOTP.Payload.Descriptor.NestedTypes[0]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -554,10 +554,10 @@ public string Issuer /// Field number for the "algorithm" field. public const int AlgorithmFieldNumber = 4; - private global::PowerToysRunTOTP.Payload.Types.Algorithm algorithm_ = global::PowerToysRunTOTP.Payload.Types.Algorithm.Unspecified; + private global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.Algorithm algorithm_ = global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.Algorithm.Unspecified; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::PowerToysRunTOTP.Payload.Types.Algorithm Algorithm + public global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.Algorithm Algorithm { get { return algorithm_; } set @@ -568,10 +568,10 @@ public string Issuer /// Field number for the "digits" field. public const int DigitsFieldNumber = 5; - private global::PowerToysRunTOTP.Payload.Types.DigitCount digits_ = global::PowerToysRunTOTP.Payload.Types.DigitCount.Unspecified; + private global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.DigitCount digits_ = global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.DigitCount.Unspecified; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::PowerToysRunTOTP.Payload.Types.DigitCount Digits + public global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.DigitCount Digits { get { return digits_; } set @@ -582,10 +582,10 @@ public string Issuer /// Field number for the "type" field. public const int TypeFieldNumber = 6; - private global::PowerToysRunTOTP.Payload.Types.OtpType type_ = global::PowerToysRunTOTP.Payload.Types.OtpType.Unspecified; + private global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.OtpType type_ = global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.OtpType.Unspecified; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::PowerToysRunTOTP.Payload.Types.OtpType Type + public global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.OtpType Type { get { return type_; } set @@ -645,9 +645,9 @@ public override int GetHashCode() if (Secret.Length != 0) hash ^= Secret.GetHashCode(); if (Name.Length != 0) hash ^= Name.GetHashCode(); if (Issuer.Length != 0) hash ^= Issuer.GetHashCode(); - if (Algorithm != global::PowerToysRunTOTP.Payload.Types.Algorithm.Unspecified) hash ^= Algorithm.GetHashCode(); - if (Digits != global::PowerToysRunTOTP.Payload.Types.DigitCount.Unspecified) hash ^= Digits.GetHashCode(); - if (Type != global::PowerToysRunTOTP.Payload.Types.OtpType.Unspecified) hash ^= Type.GetHashCode(); + if (Algorithm != global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.Algorithm.Unspecified) hash ^= Algorithm.GetHashCode(); + if (Digits != global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.DigitCount.Unspecified) hash ^= Digits.GetHashCode(); + if (Type != global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.OtpType.Unspecified) hash ^= Type.GetHashCode(); if (Counter != 0L) hash ^= Counter.GetHashCode(); if (_unknownFields != null) { @@ -724,17 +724,17 @@ public void WriteTo(pb::CodedOutputStream output) output.WriteRawTag(26); output.WriteString(Issuer); } - if (Algorithm != global::PowerToysRunTOTP.Payload.Types.Algorithm.Unspecified) + if (Algorithm != global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.Algorithm.Unspecified) { output.WriteRawTag(32); output.WriteEnum((int)Algorithm); } - if (Digits != global::PowerToysRunTOTP.Payload.Types.DigitCount.Unspecified) + if (Digits != global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.DigitCount.Unspecified) { output.WriteRawTag(40); output.WriteEnum((int)Digits); } - if (Type != global::PowerToysRunTOTP.Payload.Types.OtpType.Unspecified) + if (Type != global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.OtpType.Unspecified) { output.WriteRawTag(48); output.WriteEnum((int)Type); @@ -768,15 +768,15 @@ public int CalculateSize() { size += 1 + pb::CodedOutputStream.ComputeStringSize(Issuer); } - if (Algorithm != global::PowerToysRunTOTP.Payload.Types.Algorithm.Unspecified) + if (Algorithm != global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.Algorithm.Unspecified) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int)Algorithm); } - if (Digits != global::PowerToysRunTOTP.Payload.Types.DigitCount.Unspecified) + if (Digits != global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.DigitCount.Unspecified) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int)Digits); } - if (Type != global::PowerToysRunTOTP.Payload.Types.OtpType.Unspecified) + if (Type != global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.OtpType.Unspecified) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int)Type); } @@ -811,15 +811,15 @@ public void MergeFrom(OtpParameters other) { Issuer = other.Issuer; } - if (other.Algorithm != global::PowerToysRunTOTP.Payload.Types.Algorithm.Unspecified) + if (other.Algorithm != global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.Algorithm.Unspecified) { Algorithm = other.Algorithm; } - if (other.Digits != global::PowerToysRunTOTP.Payload.Types.DigitCount.Unspecified) + if (other.Digits != global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.DigitCount.Unspecified) { Digits = other.Digits; } - if (other.Type != global::PowerToysRunTOTP.Payload.Types.OtpType.Unspecified) + if (other.Type != global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.OtpType.Unspecified) { Type = other.Type; } @@ -906,17 +906,17 @@ public void MergeFrom(pb::CodedInputStream input) } case 32: { - Algorithm = (global::PowerToysRunTOTP.Payload.Types.Algorithm)input.ReadEnum(); + Algorithm = (global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.Algorithm)input.ReadEnum(); break; } case 40: { - Digits = (global::PowerToysRunTOTP.Payload.Types.DigitCount)input.ReadEnum(); + Digits = (global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.DigitCount)input.ReadEnum(); break; } case 48: { - Type = (global::PowerToysRunTOTP.Payload.Types.OtpType)input.ReadEnum(); + Type = (global::Community.PowerToys.Run.Plugin.TOTP.Payload.Types.OtpType)input.ReadEnum(); break; } case 56: diff --git a/PowertoysRunTOTP.sln b/PowertoysRunTOTP.sln index 154d7fc..2b6c97a 100644 --- a/PowertoysRunTOTP.sln +++ b/PowertoysRunTOTP.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32519.379 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowertoysRunTOTP", "PowertoysRunTOTP.csproj", "{77086A85-21D3-4278-919E-AC6E883B1D27}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Community.PowerToys.Run.Plugin.TOTP", "Community.PowerToys.Run.Plugin.TOTP.csproj", "{77086A85-21D3-4278-919E-AC6E883B1D27}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/README.md b/README.md index 39f83c9..56f7ea3 100644 --- a/README.md +++ b/README.md @@ -18,27 +18,30 @@ A plugin helps you to copy your two-factor verification code in Powertoys Run 1. Add account Plugin support standard OTPAuth URI (starts with `otpauth://`) and Google Authenticator Export URI(starts with `otpauth-migration://`), you can paste it to the PT Run input bar and you will see an option to add it to list. You can use any QRCode scanner to resolve QRCode to link, Accounts with same key in the list will not be added. -You can also add manually by editing config file in `%LOCALAPPDATA%\Microsoft\PowerToys\PowerToys Run\Settings\Plugins\TOTP\Config.json`: +You can also add manually by editing config file in `%LOCALAPPDATA%\Microsoft\PowerToys\PowerToys Run\Settings\Plugins\Community.PowerToys.Run.Plugin.TOTP\OTPList.json`: ```json - [ - { - "Name": "Github: Hello", - "Key": "12313", - "IsEncrypted": false - }, - { - "Name": "Twitter: @Hello", - "Key": "12313213", - "IsEncrypted": false - } - ] + { + "Version": 2, + "Entries": [ + { + "Name": "Github: Hello", + "Key": "12313", + "IsEncrypted": false + }, + { + "Name": "Twitter: @Hello", + "Key": "12313213", + "IsEncrypted": false + } + ] + } ``` Change to the config file will be applied immediately. Once the plugin loads the config again, **all unencrypted data will be encrypted**. 2. Delete account There is no way to delete accounts by GUI. -You delete an account by editing the config file in `%LOCALAPPDATA%\Microsoft\PowerToys\PowerToys Run\Settings\Plugins\TOTP\Config.json`. +You delete an account by editing the config file in `%LOCALAPPDATA%\Microsoft\PowerToys\PowerToys Run\Settings\Plugins\Community.PowerToys.Run.Plugin.TOTP\OTPList.json`. Change to the config file will be applied immediately. @@ -52,6 +55,6 @@ Encrypted data only can be decrypted in your current machine with the current ac 3. Use `ILRepack` to bundle all dependencies to a single file(Powertoys doesn't support load dll by the PTRun plugin). ``` # In publish directory - ~\.nuget\packages\ilrepack\2.0.18\tools\ILRepack.exe /lib:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.25" /out:PowertoysRunTOTP.dll .\PowertoysRunTOTP.dll .\Google.Protobuf.dll .\Otp.NET.dll + ~\.nuget\packages\ilrepack\2.0.18\tools\ILRepack.exe /lib:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.25" /out:Community.PowerToys.Run.Plugin.TOTP.dll .\Community.PowerToys.Run.Plugin.TOTP.dll .\Google.Protobuf.dll .\Otp.NET.dll ``` 4. Use ILRepack to generate `PowerToysRunTOTP.dll` to replace the original output. diff --git a/plugin.json b/plugin.json index 20597e7..db8f764 100644 --- a/plugin.json +++ b/plugin.json @@ -7,7 +7,7 @@ "Version": "1.2.1", "Language": "csharp", "Website": "https://github.com/KawaiiZapic/PowertoysRunTOTP", - "ExecuteFileName": "PowerToysRunTOTP.dll", + "ExecuteFileName": "Community.PowerToys.Run.Plugin.TOTP.dll", "IsGlobal": false, "IcoPathDark": "images\\icon-dark.png", "IcoPathLight": "images\\icon-light.png"