importer app

This commit is contained in:
Kyle Spearrin 2023-02-16 14:20:34 -05:00
parent ed7ece0f00
commit 4072b9df7b
No known key found for this signature in database
GPG Key ID: C43D1E5E1AF15714
31 changed files with 1652 additions and 0 deletions

217
.gitignore vendored Normal file
View File

@ -0,0 +1,217 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
# Visual Studo 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding addin-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
PublishProfiles/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
*.[Cc]ache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Other
project.lock.json
*.jfm
mail_dist/
*.refactorlog
*.scmp
src/Core/Properties/launchSettings.json
*.override.env
**/*.DS_Store
src/Admin/wwwroot/lib
src/Admin/wwwroot/css
.vscode/*
**/.vscode/*
bitwarden_license/src/Sso/wwwroot/lib
bitwarden_license/src/Sso/wwwroot/css
.github/test/build.secrets
**/CoverageOutput/
.idea/*
**/**.swp

14
Importer/App.xaml Normal file
View File

@ -0,0 +1,14 @@
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Bit.Importer"
x:Class="Bit.Importer.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

22
Importer/App.xaml.cs Normal file
View File

@ -0,0 +1,22 @@
namespace Bit.Importer;
public partial class App : Application
{
public App()
{
// Uncomment below to force light theme
// Current.UserAppTheme = AppTheme.Light;
InitializeComponent();
MainPage = new AppShell();
}
protected override Window CreateWindow(IActivationState activationState)
{
var window = base.CreateWindow(activationState);
window.Width = 650;
window.Height = 1100;
window.Title = "Bitwarden Importer";
return window;
}
}

15
Importer/AppShell.xaml Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Bit.Importer.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Bit.Importer"
Shell.FlyoutBehavior="Disabled">
<ShellContent
Title="Bitwarden Importer"
Icon="icon.svg"
ContentTemplate="{DataTemplate local:MainPage}"
Route="MainPage" />
</Shell>

View File

@ -0,0 +1,9 @@
namespace Bit.Importer;
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
}
}

62
Importer/Importer.csproj Normal file
View File

@ -0,0 +1,62 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-windows10.0.19041.0</TargetFrameworks>
<OutputType>Exe</OutputType>
<RootNamespace>Bit.Importer</RootNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<!-- Display name -->
<ApplicationTitle>Bitwarden Importer</ApplicationTitle>
<!-- App Identifier -->
<ApplicationId>com.bitwarden.importer</ApplicationId>
<ApplicationIdGuid>c5a31c67-9745-473c-b2ac-cd797bf4615b</ApplicationIdGuid>
<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
</PropertyGroup>
<!-- Workaround from here: https://github.com/dotnet/maui/issues/12080#issuecomment-1398635826 -->
<PropertyGroup>
<WindowsAppSdkDeploymentManagerInitialize>false</WindowsAppSdkDeploymentManagerInitialize>
</PropertyGroup>
<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#175DDC" />
<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#175DDC" BaseSize="128,128" />
<!-- Images -->
<MauiImage Include="Resources\Images\*" />
<!-- Custom Fonts -->
<MauiFont Include="Resources\Fonts\*" />
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>
<ItemGroup>
<None Remove="Resources\Images\icon.svg" />
<None Remove="Resources\Raw\bw-cli\bw-mac" />
<None Remove="Resources\Raw\bw-cli\bw-windows.exe" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CsvHelper" Version="30.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
<PackageReference Include="PasswordManagerAccess" Version="10.2.1" />
</ItemGroup>
</Project>

148
Importer/MainPage.xaml Normal file
View File

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.Importer.MainPage">
<ScrollView>
<VerticalStackLayout
Spacing="50"
Padding="20"
VerticalOptions="Start">
<VerticalStackLayout
Spacing="10"
Padding="0">
<Label
Text="The Bitwarden Importer Tool helps you easily move all of your data from an existing password management service into your Bitwarden account. Simply imput your credentials for Bitwarden and your old password management service into the form below and click the 'Import' button." />
<Label
Text="Click here to learn more"
TextColor="{StaticResource Primary}"
FontAttributes="Bold"
TextDecorations="Underline"
x:Name="LearnMore" />
</VerticalStackLayout>
<VerticalStackLayout
Spacing="20"
Padding="0">
<VerticalStackLayout
Spacing="5"
Padding="0">
<Label
Text="Bitwarden Server Url" />
<Entry
x:Name="BitwardenServerUrl" />
</VerticalStackLayout>
<VerticalStackLayout
Spacing="5"
Padding="0">
<HorizontalStackLayout
Spacing="10">
<Label
Text="Bitwarden API Key client_id" />
<Label
Text="Click here to get your API key"
TextColor="{StaticResource Primary}"
FontAttributes="Bold"
TextDecorations="Underline"
x:Name="ApiKeyLink1" />
</HorizontalStackLayout>
<Entry
x:Name="BitwardenApiKeyClientId" />
</VerticalStackLayout>
<VerticalStackLayout
Spacing="5"
Padding="0">
<HorizontalStackLayout
Spacing="10">
<Label
Text="Bitwarden API Key secret" />
<Label
Text="Click here to get your API key"
TextColor="{StaticResource Primary}"
FontAttributes="Bold"
TextDecorations="Underline"
x:Name="ApiKeyLink2"/>
</HorizontalStackLayout>
<Entry
x:Name="BitwardenApiKeySecret"
IsPassword="True" />
</VerticalStackLayout>
<VerticalStackLayout
Spacing="5"
Padding="0"
x:Name="BitwardenPasswordLayout">
<Label
Text="Bitwarden Master Password" />
<Entry
x:Name="BitwardenPassword"
IsPassword="True" />
</VerticalStackLayout>
<HorizontalStackLayout
x:Name="BitwardenKeyConnectorLayout">
<CheckBox
x:Name="BitwardenKeyConnector"
CheckedChanged="BitwardenKeyConnector_CheckedChanged"
VerticalOptions="Center" />
<Label
x:Name="BitwardenKeyConnectorLabel"
Text="My organization uses a SSO configuration that does not require a master password."
VerticalOptions="Center" />
</HorizontalStackLayout>
</VerticalStackLayout>
<VerticalStackLayout
Spacing="20"
Padding="0">
<VerticalStackLayout
Spacing="5"
Padding="0">
<Label
Text="Import From Service" />
<Picker
x:Name="Service" />
</VerticalStackLayout>
<VerticalStackLayout
Spacing="5"
Padding="0">
<Label
Text="LastPass Email" />
<Entry
x:Name="LastPassEmail" />
</VerticalStackLayout>
<VerticalStackLayout
Spacing="5"
Padding="0">
<Label
Text="LastPass Master Password" />
<Entry
x:Name="LastPassPassword"
IsPassword="True" />
</VerticalStackLayout>
</VerticalStackLayout>
<HorizontalStackLayout
Spacing="10"
Padding="0">
<Button
x:Name="SubmitButton"
Text="Import"
Clicked="OnButtonClicked"
HorizontalOptions="Center" />
<ActivityIndicator
x:Name="Loading"
IsRunning="false"
VerticalOptions="Center" />
<Label
x:Name="PleaseWait"
Text="Please wait..."
IsVisible="false"
VerticalOptions="Center" />
</HorizontalStackLayout>
</VerticalStackLayout>
</ScrollView>
</ContentPage>

307
Importer/MainPage.xaml.cs Normal file
View File

@ -0,0 +1,307 @@
using CsvHelper;
using PasswordManagerAccess.LastPass;
using System.Diagnostics;
using System.Globalization;
namespace Bit.Importer;
public partial class MainPage : ContentPage
{
private readonly List<string> _services = new() { "LastPass" };
private string _bitwardenCloudUrl = "https://bitwarden.com";
public MainPage()
{
InitializeComponent();
BitwardenServerUrl.Text = _bitwardenCloudUrl;
Service.ItemsSource = _services;
Service.SelectedIndex = 0;
var learnMoreTap = new TapGestureRecognizer();
learnMoreTap.Tapped += async (s, e) =>
{
await Browser.Default.OpenAsync(new Uri("https://bitwarden.com/help"),
BrowserLaunchMode.SystemPreferred);
};
LearnMore.GestureRecognizers.Add(learnMoreTap);
var apiKeyTap = new TapGestureRecognizer();
apiKeyTap.Tapped += async (s, e) =>
{
await Browser.Default.OpenAsync(new Uri("https://vault.bitwarden.com/#/settings/security/security-keys"),
BrowserLaunchMode.SystemPreferred);
};
ApiKeyLink1.GestureRecognizers.Add(apiKeyTap);
ApiKeyLink2.GestureRecognizers.Add(apiKeyTap);
}
private void BitwardenKeyConnector_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
// We don't need master passwqord if using key connector
BitwardenPasswordLayout.IsVisible = !BitwardenKeyConnector.IsChecked;
}
private async void OnButtonClicked(object sender, EventArgs e)
{
// Validate
if (!await ValidateInputsAsync())
{
return;
}
// Start loading state
Loading.IsRunning = true;
SubmitButton.IsEnabled = false;
PleaseWait.IsVisible = true;
// Run the import task
await Task.Run(ImportAsync);
}
private async Task<bool> ValidateInputsAsync()
{
if (string.IsNullOrWhiteSpace(BitwardenApiKeyClientId?.Text) ||
string.IsNullOrWhiteSpace(BitwardenApiKeySecret?.Text))
{
await DisplayAlert("Error", "Bitwarden API Key information is required.", "OK");
return false;
}
if (string.IsNullOrWhiteSpace(BitwardenPassword?.Text) &&
!BitwardenKeyConnector.IsChecked)
{
await DisplayAlert("Error", "Bitwarden master password is required.", "OK");
return false;
}
if (_services[Service.SelectedIndex] == "LastPass")
{
if (string.IsNullOrWhiteSpace(LastPassEmail?.Text) ||
string.IsNullOrWhiteSpace(LastPassPassword?.Text))
{
await DisplayAlert("Error", "LastPass Email and Master Password are required.", "OK");
return false;
}
}
return true;
}
private async Task ImportAsync()
{
await CleanupAsync();
if (_services[Service.SelectedIndex] == "LastPass")
{
var (lastpassSuccess, lastpassCsvPath) = await CreateLastpassCsvAsync();
if (!lastpassSuccess)
{
StopLoadingAndAlert(true,
"Unable to log into your LastPass account. Are your credentials correct?");
}
var cliSetupSuccess = false;
try
{
if (lastpassSuccess)
{
await SetupCliAsync();
cliSetupSuccess = true;
}
}
catch
{
StopLoadingAndAlert(true, "Unable to set up Bitwarden CLI.");
}
if (cliSetupSuccess && lastpassSuccess)
{
if (!string.IsNullOrWhiteSpace(BitwardenServerUrl?.Text) && BitwardenServerUrl.Text != "https://bitwarden.com")
{
var configServerSuccess = ConfigServerCli();
if (!configServerSuccess)
{
StopLoadingAndAlert(true, "Unable to configure Bitwarden server.");
return;
}
}
var (loginSuccess, sessionKey) = LogInCli();
if (!loginSuccess)
{
StopLoadingAndAlert(true,
"Unable to log into your Bitwarden account. Is your API key information correct?");
}
if (loginSuccess && string.IsNullOrWhiteSpace(sessionKey))
{
var (unlockSuccess, unlockSessionKey) = UnlockCli();
sessionKey = unlockSessionKey;
if (!unlockSuccess)
{
StopLoadingAndAlert(true,
"Unable to unlock your Bitwarden vault. Is your master password correct?");
}
}
if (!string.IsNullOrWhiteSpace(lastpassCsvPath) && !string.IsNullOrWhiteSpace(sessionKey))
{
var importSuccess = ImportCli("lastpasscsv", lastpassCsvPath, sessionKey);
if (importSuccess)
{
StopLoadingAndAlert(false, "Your import was successful!");
}
else
{
StopLoadingAndAlert(true, "Something went wrong with the import.");
}
}
}
}
await CleanupAsync();
}
private void StopLoadingAndAlert(bool error, string message)
{
Dispatcher.Dispatch(async () =>
{
// Stop the loading state
Loading.IsRunning = false;
SubmitButton.IsEnabled = true;
PleaseWait.IsVisible = false;
// Show alert
if (error)
{
await DisplayAlert("Error", message, "OK");
}
else
{
await DisplayAlert("Success", message, "OK");
}
});
}
private async Task<(bool, string)> CreateLastpassCsvAsync()
{
try
{
// Log in and get LastPass data
var ui = new Services.LastPass.Ui(this);
var clientInfo = new ClientInfo(
PasswordManagerAccess.LastPass.Platform.Desktop,
Guid.NewGuid().ToString().ToLower(),
"Importer");
var vault = Vault.Open(LastPassEmail?.Text, LastPassPassword?.Text, clientInfo, ui);
// Massage it to expected CSV format
var exportAccounts = vault.Accounts.Select(a => new Services.LastPass.ExportedAccount(a));
// Create CSV string
using var writer = new StringWriter();
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
csv.WriteRecords(exportAccounts);
csv.Flush();
var csvOutput = writer.ToString();
// Write CSV to temp disk
var lastpassCsvPath = Path.Combine(FileSystem.CacheDirectory, "lastpass-export.csv");
await File.WriteAllTextAsync(lastpassCsvPath, csvOutput);
return (true, lastpassCsvPath);
}
catch
{
return (false, null);
}
}
private bool ConfigServerCli()
{
var (exitCode, stdOut) = ExecCli($"config server {BitwardenServerUrl?.Text}");
return exitCode == 0;
}
private (bool, string) LogInCli()
{
var (exitCode, sessionKey) = ExecCli("login --apikey --raw", (process) =>
{
process.StartInfo.EnvironmentVariables["BW_CLIENTID"] = BitwardenApiKeyClientId?.Text;
process.StartInfo.EnvironmentVariables["BW_CLIENTSECRET"] = BitwardenApiKeySecret?.Text;
// Avoid BW_NOINTERACTION bug that is issuing invalid session key on api key login
process.StartInfo.EnvironmentVariables["BW_NOINTERACTION"] = "false";
});
return (exitCode == 0, sessionKey);
}
private (bool, string) UnlockCli()
{
var (exitCode, sessionKey) = ExecCli($"unlock {BitwardenPassword?.Text} --raw");
return (exitCode == 0, sessionKey);
}
private bool ImportCli(string importService, string importFilePath, string sessionKey)
{
var (importExitCode, importStdOut) = ExecCli($"import {importService} {importFilePath}", (process) =>
{
process.StartInfo.EnvironmentVariables["BW_SESSION"] = sessionKey;
});
return importExitCode == 0 && (importStdOut?.Contains("Imported") ?? false);
}
private async Task SetupCliAsync()
{
// Copy packaged CLI app to disk so that we can invoke it.
using var stream = await FileSystem.OpenAppPackageFileAsync("bw-cli/bw-windows.exe");
using var fileStream = File.Create(ResolveCliPath());
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(fileStream);
stream.Close();
fileStream.Close();
}
private (int, string) ExecCli(string args, Action<Process> processAction = null)
{
// Set up the process
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = ResolveCliPath(),
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
},
};
// Load standard env vars for this use case
process.StartInfo.EnvironmentVariables["BITWARDENCLI_APPDATA_DIR"] = FileSystem.CacheDirectory;
process.StartInfo.EnvironmentVariables["BW_NOINTERACTION"] = "true";
processAction?.Invoke(process);
process.Start();
var stdOut = "";
while (!process.StandardOutput.EndOfStream)
{
stdOut += process.StandardOutput.ReadLine();
}
process.StandardOutput.Close();
process.WaitForExit();
return (process.ExitCode, stdOut.Trim());
}
private string ResolveCliPath()
{
var bwCliFilename = DeviceInfo.Platform == DevicePlatform.WinUI ? "bw.exe" : "bw";
return Path.Combine(FileSystem.CacheDirectory, bwCliFilename);
}
private Task CleanupAsync()
{
File.Delete(Path.Combine(FileSystem.CacheDirectory, "data.json"));
File.Delete(Path.Combine(FileSystem.CacheDirectory, "lastpass-export.csv"));
return Task.FromResult(0);
}
}

24
Importer/MauiProgram.cs Normal file
View File

@ -0,0 +1,24 @@
using Microsoft.Extensions.Logging;
namespace Bit.Importer;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}

View File

@ -0,0 +1,9 @@
using Foundation;
namespace Bit.Importer;
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
</dict>
</plist>

View File

@ -0,0 +1,15 @@
using ObjCRuntime;
using UIKit;
namespace Bit.Importer;
public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
}

View File

@ -0,0 +1,8 @@
<maui:MauiWinUIApplication
x:Class="Bit.Importer.WinUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:maui="using:Microsoft.Maui"
xmlns:local="using:Bit.Importer.WinUI">
</maui:MauiWinUIApplication>

View File

@ -0,0 +1,24 @@
using Microsoft.UI.Xaml;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Bit.Importer.WinUI;
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : MauiWinUIApplication
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
}
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity Name="maui-package-name-placeholder" Publisher="CN=User Name" Version="0.0.0.0" />
<mp:PhoneIdentity PhoneProductId="569357BF-7B0D-4BDB-B134-0D3A5765BA50" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>$placeholder$</DisplayName>
<PublisherDisplayName>User Name</PublisherDisplayName>
<Logo>$placeholder$.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="$placeholder$"
Description="$placeholder$"
Square150x150Logo="$placeholder$.png"
Square44x44Logo="$placeholder$.png"
BackgroundColor="transparent">
<uap:DefaultTile Square71x71Logo="$placeholder$.png" Wide310x150Logo="$placeholder$.png" Square310x310Logo="$placeholder$.png" />
<uap:SplashScreen Image="$placeholder$.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="Importer.WinUI.app"/>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect:
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update
-->
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
</windowsSettings>
</application>
</assembly>

View File

@ -0,0 +1,8 @@
{
"profiles": {
"Windows Machine": {
"commandName": "MsixPackage",
"nativeDebugging": false
}
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="456" height="456" fill="#175DDC" />
</svg>

After

Width:  |  Height:  |  Size: 228 B

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Shield" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 390 462" style="enable-background:new 0 0 390 462;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path id="Identity" class="st0" d="M376.4,12.2c-3.7-3.7-8.1-5.6-13.1-5.6H26.7c-5.1,0-9.4,1.9-13.1,5.6C9.9,15.9,8,20.2,8,25.3
v224.4c0,16.7,3.3,33.4,9.8,49.8c6.5,16.5,14.6,31.1,24.3,43.8c9.6,12.8,21.1,25.2,34.5,37.2c13.3,12.1,25.7,22.1,37,30.1
c11.3,8,23.1,15.5,35.4,22.6c12.3,7.1,21,11.9,26.2,14.5c5.2,2.5,9.3,4.5,12.4,5.8c2.3,1.2,4.9,1.8,7.6,1.8c2.7,0,5.3-0.6,7.6-1.8
c3.1-1.4,7.3-3.3,12.4-5.8c5.2-2.5,13.9-7.4,26.2-14.5c12.3-7.1,24.1-14.7,35.4-22.6c11.3-8,23.6-18,37-30.1
c13.3-12.1,24.8-24.5,34.5-37.2c9.6-12.8,17.7-27.4,24.2-43.8c6.5-16.5,9.8-33.1,9.8-49.8V25.3C382,20.2,380.1,15.9,376.4,12.2z
M333,251.8C333,333,195,403,195,403V54.6h138C333,54.6,333,170.6,333,251.8z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
<style type="text/css">
.st0{fill:#175DDC;}
.st1{fill:#FFFFFF;}
</style>
<path id="Background" class="st0" d="M1024,864c0,88.4-71.6,160-160,160H160C71.6,1024,0,952.4,0,864V160C0,71.6,71.6,0,160,0h704
c88.4,0,160,71.6,160,160V864z"/>
<path id="Identity" class="st1" d="M829.8,128.6c-6.5-6.5-14.2-9.7-23-9.7H217.2c-8.9,0-16.5,3.2-23,9.7c-6.5,6.5-9.7,14.2-9.7,23
v393.1c0,29.3,5.7,58.4,17.1,87.3c11.4,28.8,25.6,54.4,42.5,76.8c16.9,22.3,37,44.1,60.4,65.3c23.4,21.2,45,38.7,64.7,52.7
c19.8,14,40.4,27.2,61.9,39.7c21.5,12.5,36.8,20.9,45.8,25.3c9,4.4,16.3,7.9,21.7,10.2c4.1,2,8.5,3.1,13.3,3.1c4.8,0,9.2-1,13.3-3.1
c5.5-2.4,12.7-5.8,21.8-10.2c9-4.4,24.3-12.9,45.8-25.3c21.5-12.5,42.1-25.7,61.9-39.7c19.8-14,41.4-31.6,64.8-52.7
c23.4-21.2,43.5-42.9,60.4-65.3c16.9-22.4,31-47.9,42.5-76.8c11.4-28.8,17.1-57.9,17.1-87.3V151.7
C839.6,142.8,836.3,135.1,829.8,128.6z M753.8,548.4c0,142.3-241.8,264.9-241.8,264.9V203.1h241.8
C753.8,203.1,753.8,406.1,753.8,548.4z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,15 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories). Deployment of the asset to your application
is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
These files will be deployed with you package and will be accessible using Essentials:
async Task LoadMauiAsset()
{
using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
using var reader = new StreamReader(stream);
var contents = reader.ReadToEnd();
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 609.7 386.2" style="enable-background:new 0 0 609.7 386.2;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M44.6,307.8c8.4,0,15,3.3,19.8,9.9c4.8,6.6,7.2,15.6,7.2,27.1c0,11.8-2.5,20.9-7.4,27.4
c-4.9,6.5-11.6,9.7-20.1,9.7c-8.4,0-15-3-19.7-9.1H23l-2.6,6.3c-0.4,0.9-1.3,1.5-2.3,1.5H7.3c-1.4,0-2.5-1.1-2.5-2.5v-94.4
c0-1.4,1.1-2.5,2.5-2.5h14.5c1.4,0,2.5,1.1,2.5,2.5v20.6c0,2.9-0.3,7.6-0.8,14.1h0.8C28.9,311.4,35.6,307.8,44.6,307.8z M38.3,323.4
c-4.8,0-8.3,1.5-10.5,4.4c-2.2,3-3.4,7.8-3.4,14.7v2.1c0,7.7,1.1,13.2,3.4,16.5c2.3,3.3,5.9,5,10.8,5c4,0,7.2-1.8,9.6-5.5
c2.4-3.7,3.5-9,3.5-16.1c0-7-1.2-12.3-3.6-15.8C45.7,325.2,42.4,323.4,38.3,323.4z M103.7,380.6H89.2c-1.4,0-2.5-1.1-2.5-2.5v-66.5
c0-1.4,1.1-2.5,2.5-2.5h14.5c1.4,0,2.5,1.1,2.5,2.5v66.5C106.2,379.5,105.1,380.6,103.7,380.6z M156.8,366.4c2.9,0,6.3-0.5,10.2-1.6
c1.1-0.3,2.1,0.5,2.1,1.6v11.2c0,0.7-0.4,1.3-1,1.5c-4.7,1.9-10.3,2.8-16.9,2.8c-7.8,0-13.5-2-17-5.9c-3.6-3.9-5.3-9.8-5.3-17.7
v-34.4h-7.7c-0.9,0-1.7-0.7-1.7-1.7v-6.4c0-0.1,0.1-0.3,0.2-0.4l10.5-6.4l5.2-14c0.2-0.6,0.9-1.1,1.6-1.1h9.7c0.9,0,1.7,0.7,1.7,1.7
v13.5h19.2c0.5,0,0.8,0.4,0.8,0.8v12.1c0,0.9-0.7,1.7-1.7,1.7h-18.3v34.4c0,2.8,0.8,4.8,2.3,6.1
C152.1,365.7,154.2,366.4,156.8,366.4z M247.2,380.6c-1.8,0-3.5-1.2-4-2.9l-14.2-43.5c-1-3.2-2.2-7.8-3.8-13.9h-0.4l-1.3,4.7
l-2.9,9.3l-14.5,43.4c-0.6,1.7-2.2,2.9-4,2.9l0,0c-1.9,0-3.6-1.3-4.1-3.1l-17.9-62.4c-0.6-2,0.9-3.9,3-3.9h0.3c1.4,0,2.6,0.9,3,2.3
l10.5,38c2.6,10,4.3,17.3,5.1,22h0.4c2.5-10,4.3-16.6,5.5-19.9l13.4-39.7c0.5-1.6,2.1-2.7,3.8-2.7l0,0c1.7,0,3.3,1.1,3.8,2.8
l12.7,39.5c3.1,10,4.9,16.6,5.6,19.9h0.4c0.3-2.8,2-10.2,5.1-22.2l10-37.6c0.4-1.3,1.6-2.3,3-2.3h0c2,0,3.5,1.9,3,3.9l-16.9,62.4
c-0.5,1.8-2.2,3.1-4.1,3.1H247.2z M326.9,380.6c-1.2,0-2.3-0.9-2.5-2.1l-1.3-8.8h-0.5c-3.5,4.5-7.1,7.6-10.8,9.5
c-3.7,1.9-8,2.8-13.1,2.8c-6.8,0-12.1-1.7-15.9-5.2c-3.4-3.1-5.2-7.3-5.6-12.5c-0.5-6.9,2.4-13.7,8.1-17.6
c5.7-3.9,13.9-5.9,24.8-6.1l13.2-0.4v-4.6c0-6.6-1.3-11.6-4-14.9c-2.7-3.4-7-5-13-5c-5.6,0-11.3,1.3-17.3,4c-1.5,0.7-3.3,0-3.9-1.5
l0,0c-0.6-1.5,0.1-3.2,1.6-3.9c6.7-2.8,13.3-4.2,19.9-4.2c7.6,0,13.3,2,17.1,5.9c3.8,4,5.7,10.1,5.7,18.5v43.7
C329.4,379.5,328.3,380.6,326.9,380.6L326.9,380.6z M299.2,376.1c7.4,0,13.3-2.1,17.5-6.4c4.3-4.2,6.4-10.1,6.4-17.7v-6.8l-12.1,0.5
c-9.8,0.5-16.7,2-20.9,4.6c-4.2,2.6-6.2,6.6-6.2,12c0,4.3,1.3,7.7,4,10.1C290.6,374.9,294.4,376.1,299.2,376.1z M378.7,309.8
c2,0,4.1,0.1,6.3,0.4c1.7,0.2,2.9,1.9,2.5,3.6v0c-0.3,1.6-1.9,2.6-3.5,2.4c-2-0.3-4-0.5-6.1-0.5c-5.9,0-10.8,2.5-14.6,7.5
c-3.8,5-5.7,11.4-5.7,19v35.2c0,1.7-1.4,3.2-3.2,3.2h0c-1.7,0-3.2-1.4-3.2-3.2v-63.5c0-1.5,1.2-2.8,2.8-2.8l0,0
c1.5,0,2.7,1.1,2.7,2.6l0.5,9.9h0.4c2.9-5.1,5.9-8.7,9.1-10.8C370.2,310.9,374.1,309.8,378.7,309.8z M425.9,309.8
c5,0,9.4,0.9,13,2.7c3.7,1.8,6.9,5,9.8,9.4h0.4c-0.3-5.4-0.4-10.6-0.4-15.8v-21.8c0-1.7,1.4-3.1,3.1-3.1l0,0c1.7,0,3.1,1.4,3.1,3.1
v94c0,1.2-1,2.2-2.2,2.2l0,0c-1.1,0-2-0.8-2.2-1.9l-1.3-8.7h-0.5c-5.3,7.9-12.9,11.9-22.7,11.9c-9.6,0-16.9-3-22-8.9
c-5.1-6-7.6-14.7-7.6-26.1c0-12,2.5-21.2,7.5-27.5C409,313,416.3,309.8,425.9,309.8z M425.9,315.6c-7.6,0-13.3,2.7-17.1,8
c-3.8,5.3-5.7,13.1-5.7,23.2c0,19.7,7.6,29.5,22.9,29.5c7.8,0,13.6-2.3,17.2-6.8c3.7-4.6,5.5-12.1,5.5-22.6v-1.1
c0-10.7-1.8-18.5-5.4-23.2C439.8,317.9,433.9,315.6,425.9,315.6z M506.4,381.9c-10.1,0-18-3.1-23.6-9.3c-5.6-6.2-8.5-14.9-8.5-26.1
c0-11.1,2.7-19.9,8.2-26.6c5.5-6.7,12.8-10,22-10c8.2,0,14.6,2.9,19.4,8.6c4.7,5.7,7.1,13.5,7.1,23.3v5.1h-50
c0.1,9.5,2.3,16.8,6.7,21.8c4.4,5,10.6,7.5,18.7,7.5c4,0,7.4-0.3,10.4-0.8c2.2-0.4,4.8-1.1,7.8-2.2c1.8-0.6,3.6,0.7,3.6,2.6l0,0
c0,1.1-0.7,2.1-1.7,2.5c-3.3,1.3-6.3,2.2-9.1,2.8C514,381.6,510.4,381.9,506.4,381.9z M504.5,315.4c-6.7,0-12.1,2.2-16.1,6.6
c-4,4.4-6.4,10.8-7.1,19.1h42.9c0-8-1.7-14.3-5.2-18.9C515.5,317.7,510.7,315.4,504.5,315.4z M601.7,380.6c-1.7,0-3.1-1.4-3.1-3.1
v-41.9c0-7-1.5-12.1-4.4-15.2c-2.9-3.2-7.5-4.8-13.7-4.8c-8.3,0-14.4,2.1-18.2,6.3c-3.9,4.2-5.8,11-5.8,20.4v35.2
c0,1.7-1.4,3.1-3.1,3.1h-0.1c-1.7,0-3.1-1.4-3.1-3.1V314c0-1.6,1.3-2.9,2.9-2.9l0,0c1.4,0,2.6,1.1,2.8,2.5l0.9,7h0.4
c4.5-7.2,12.5-10.9,24.1-10.9c15.8,0,23.6,8.5,23.6,25.4v42.3C604.8,379.2,603.4,380.6,601.7,380.6L601.7,380.6z M96.5,280.2
L96.5,280.2c-6.3,0-11.4,4.9-11.4,10.9v0.9c0,6,5.1,10.9,11.4,10.9h0c6.3,0,11.4-4.9,11.4-10.9v-0.9
C107.9,285.1,102.8,280.2,96.5,280.2z M409.5,7.5c-2.1-2.1-4.7-3.2-7.6-3.2H207.7c-2.9,0-5.5,1.1-7.6,3.2c-2.1,2.1-3.2,4.7-3.2,7.6
v129.5c0,9.7,1.9,19.2,5.6,28.7c3.8,9.5,8.4,17.9,14,25.3c5.6,7.4,12.2,14.5,19.9,21.5c7.7,7,14.8,12.8,21.3,17.4
c6.5,4.6,13.3,9,20.4,13.1c7.1,4.1,12.1,6.9,15.1,8.3c3,1.5,5.4,2.6,7.2,3.4c1.3,0.7,2.8,1,4.4,1c1.6,0,3-0.3,4.4-1
c1.8-0.8,4.2-1.9,7.2-3.4c3-1.5,8-4.2,15.1-8.3c7.1-4.1,13.9-8.5,20.4-13.1c6.5-4.6,13.6-10.4,21.3-17.4c7.7-7,14.3-14.1,19.9-21.5
c5.6-7.4,10.2-15.8,14-25.3c3.8-9.5,5.6-19.1,5.6-28.7V15.1C412.7,12.2,411.6,9.7,409.5,7.5z M384.5,145.8
c0,46.9-79.6,87.2-79.6,87.2v-201h79.6C384.5,32.1,384.5,98.9,384.5,145.8z"/>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xaml-comp compile="true" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Color x:Key="Primary">#175DDC</Color>
<Color x:Key="Secondary">#DFD8F7</Color>
<Color x:Key="Tertiary">#2CDDE9</Color>
<Color x:Key="White">White</Color>
<Color x:Key="Black">Black</Color>
<Color x:Key="Gray100">#E1E1E1</Color>
<Color x:Key="Gray200">#C8C8C8</Color>
<Color x:Key="Gray300">#ACACAC</Color>
<Color x:Key="Gray400">#919191</Color>
<Color x:Key="Gray500">#6E6E6E</Color>
<Color x:Key="Gray600">#404040</Color>
<Color x:Key="Gray900">#212121</Color>
<Color x:Key="Gray950">#141414</Color>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource Primary}"/>
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource Secondary}"/>
<SolidColorBrush x:Key="TertiaryBrush" Color="{StaticResource Tertiary}"/>
<SolidColorBrush x:Key="WhiteBrush" Color="{StaticResource White}"/>
<SolidColorBrush x:Key="BlackBrush" Color="{StaticResource Black}"/>
<SolidColorBrush x:Key="Gray100Brush" Color="{StaticResource Gray100}"/>
<SolidColorBrush x:Key="Gray200Brush" Color="{StaticResource Gray200}"/>
<SolidColorBrush x:Key="Gray300Brush" Color="{StaticResource Gray300}"/>
<SolidColorBrush x:Key="Gray400Brush" Color="{StaticResource Gray400}"/>
<SolidColorBrush x:Key="Gray500Brush" Color="{StaticResource Gray500}"/>
<SolidColorBrush x:Key="Gray600Brush" Color="{StaticResource Gray600}"/>
<SolidColorBrush x:Key="Gray900Brush" Color="{StaticResource Gray900}"/>
<SolidColorBrush x:Key="Gray950Brush" Color="{StaticResource Gray950}"/>
<Color x:Key="Yellow100Accent">#F7B548</Color>
<Color x:Key="Yellow200Accent">#FFD590</Color>
<Color x:Key="Yellow300Accent">#FFE5B9</Color>
<Color x:Key="Cyan100Accent">#28C2D1</Color>
<Color x:Key="Cyan200Accent">#7BDDEF</Color>
<Color x:Key="Cyan300Accent">#C3F2F4</Color>
<Color x:Key="Blue100Accent">#3E8EED</Color>
<Color x:Key="Blue200Accent">#72ACF1</Color>
<Color x:Key="Blue300Accent">#A7CBF6</Color>
</ResourceDictionary>

View File

@ -0,0 +1,405 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xaml-comp compile="true" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Style TargetType="ActivityIndicator">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="IndicatorView">
<Setter Property="IndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}"/>
<Setter Property="SelectedIndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray100}}"/>
</Style>
<Style TargetType="Border">
<Setter Property="Stroke" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="StrokeShape" Value="Rectangle"/>
<Setter Property="StrokeThickness" Value="1"/>
</Style>
<Style TargetType="BoxView">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="Button">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Primary}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Padding" Value="14,10"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="DatePicker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Editor">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Entry">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Gray100}, Dark={StaticResource Gray900}}" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray500}}" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Frame">
<Setter Property="HasShadow" Value="False" />
<Setter Property="BorderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="CornerRadius" Value="8" />
</Style>
<Style TargetType="ImageButton">
<Setter Property="Opacity" Value="1" />
<Setter Property="BorderColor" Value="Transparent"/>
<Setter Property="BorderWidth" Value="0"/>
<Setter Property="CornerRadius" Value="0"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Opacity" Value="0.5" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Label">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="ListView">
<Setter Property="SeparatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="RefreshControlColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="Picker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Gray100}, Dark={StaticResource Gray900}}" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="ProgressBar">
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="RadioButton">
<Setter Property="BackgroundColor" Value="Transparent"/>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="RefreshView">
<Setter Property="RefreshColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="SearchBar">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="CancelButtonColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SearchHandler">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Shadow">
<Setter Property="Radius" Value="15" />
<Setter Property="Opacity" Value="0.5" />
<Setter Property="Brush" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource White}}" />
<Setter Property="Offset" Value="10,10" />
</Style>
<Style TargetType="Slider">
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SwipeItem">
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
</Style>
<Style TargetType="Switch">
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="ThumbColor" Value="{StaticResource White}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="On">
<VisualState.Setters>
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Secondary}, Dark={StaticResource Gray200}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Off">
<VisualState.Setters>
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray400}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="TimePicker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent"/>
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Page" ApplyToDerivedTypes="True">
<Setter Property="Padding" Value="0"/>
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
</Style>
<Style TargetType="Shell" ApplyToDerivedTypes="True">
<Setter Property="Shell.BackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource Gray950}}" />
<Setter Property="Shell.ForegroundColor" Value="{OnPlatform WinUI={StaticResource Primary}, Default={StaticResource White}}" />
<Setter Property="Shell.TitleColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource White}}" />
<Setter Property="Shell.DisabledColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="Shell.UnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray200}}" />
<Setter Property="Shell.NavBarHasShadow" Value="False" />
<Setter Property="Shell.TabBarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
<Setter Property="Shell.TabBarForegroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarTitleColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarUnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource Gray950}}" />
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
<Setter Property="IconColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="TabbedPage">
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray950}}" />
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="UnselectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="SelectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,38 @@
using CsvHelper.Configuration.Attributes;
using PasswordManagerAccess.LastPass;
namespace Bit.Importer.Services.LastPass;
public class ExportedAccount
{
public ExportedAccount() { }
public ExportedAccount(Account account)
{
Url = account.Url;
Username = account.Username;
Password = account.Password;
// Totp not supported
// Extra = account.Notes;
Name = account.Name;
Grouping = account.Path == "(none)" ? null : account.Path;
// Fav = account.Favorite == "1" ? 1 : 0;
}
[Name("url")]
public string Url { get; set; }
[Name("username")]
public string Username { get; set; }
[Name("password")]
public string Password { get; set; }
[Name("totp")]
public string Totp { get; set; }
[Name("extra")]
public string Extra { get; set; }
[Name("name")]
public string Name { get; set; }
[Name("grouping")]
public string Grouping { get; set; }
[Name("fav")]
public int Fav { get; set; }
}

View File

@ -0,0 +1,65 @@
using PasswordManagerAccess.Common;
using PasswordManagerAccess.LastPass.Ui;
namespace Bit.Importer.Services.LastPass;
public class Ui : IUi
{
private readonly MainPage _page;
public Ui(MainPage page)
{
_page = page;
}
public OtpResult ProvideGoogleAuthPasscode()
{
return new OtpResult(PromptCode("Enter your authenticator two-step login code."), false);
}
public OtpResult ProvideMicrosoftAuthPasscode()
{
return new OtpResult(PromptCode("Enter your authenticator two-step login code."), false);
}
public OtpResult ProvideYubikeyPasscode()
{
return new OtpResult(PromptCode("Enter your Yubikey code."), false);
}
public OobResult ApproveLastPassAuth()
{
return OobResult.ContinueWithPasscode(PromptCode("Enter passcode from LastPass authenticator."), false);
}
public OobResult ApproveDuo()
{
return OobResult.ContinueWithPasscode(PromptCode("Enter passcode from Duo."), false);
}
public DuoChoice ChooseDuoFactor(DuoDevice[] devices)
{
var task = _page.Dispatcher.DispatchAsync(() =>
_page.DisplayActionSheet("Choose a Duo device", "Cancel", null, devices.Select(d => d.Name).ToArray()));
var actionSelection = task.GetAwaiter().GetResult();
var device = devices.FirstOrDefault(d => d.Name == actionSelection);
return new DuoChoice(device, DuoFactor.Passcode, false);
}
public string ProvideDuoPasscode(DuoDevice device)
{
return PromptCode("Enter your Duo passcode.");
}
public void UpdateDuoStatus(DuoStatus status, string text)
{
// Not sure what this is for.
}
private string PromptCode(string message)
{
var task = _page.Dispatcher.DispatchAsync(() =>
_page.DisplayPromptAsync("LastPass Two-step Login", message, "Submit"));
return task.GetAwaiter().GetResult();
}
}

27
bitwarden-importer.sln Normal file
View File

@ -0,0 +1,27 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31611.283
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Importer", "Importer\Importer.csproj", "{46E1189D-8B50-4C55-A443-F4B1BBFF7504}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{46E1189D-8B50-4C55-A443-F4B1BBFF7504}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{46E1189D-8B50-4C55-A443-F4B1BBFF7504}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46E1189D-8B50-4C55-A443-F4B1BBFF7504}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{46E1189D-8B50-4C55-A443-F4B1BBFF7504}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46E1189D-8B50-4C55-A443-F4B1BBFF7504}.Release|Any CPU.Build.0 = Release|Any CPU
{46E1189D-8B50-4C55-A443-F4B1BBFF7504}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {61F7FB11-1E47-470C-91E2-47F8143E1572}
EndGlobalSection
EndGlobal