From ac3fdbc2cdab228ac40e0cbd1cfd9b98e1a7fe14 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 12 Jul 2017 23:09:44 -0400 Subject: [PATCH] download, decrypt and open attachment files --- src/Android/Android.csproj | 5 +- src/Android/MainApplication.cs | 2 +- src/Android/Properties/AndroidManifest.xml | 12 +++- src/Android/Resources/Resource.Designer.cs | 3 + src/Android/Resources/xml/filepaths.xml | 4 ++ src/Android/Services/ClipboardService.cs | 15 ----- src/Android/Services/DeviceActionService.cs | 63 +++++++++++++++++++ .../Services/IClipboardService.cs | 7 --- .../Abstractions/Services/ICryptoService.cs | 1 + .../Services/IDeviceActionService.cs | 8 +++ .../Abstractions/Services/ILoginService.cs | 1 + src/App/App.csproj | 2 +- .../Models/Page/VaultViewLoginPageModel.cs | 4 +- .../Pages/Tools/ToolsPasswordGeneratorPage.cs | 4 +- .../Vault/VaultAutofillListLoginsPage.cs | 4 +- src/App/Pages/Vault/VaultListLoginsPage.cs | 4 +- src/App/Pages/Vault/VaultViewLoginPage.cs | 27 ++++++-- src/App/Resources/AppResources.Designer.cs | 18 ++++++ src/App/Resources/AppResources.resx | 6 ++ src/App/Services/CryptoService.cs | 51 +++++++++++++-- src/App/Services/LoginService.cs | 33 +++++++++- src/App/Utilities/Crypto.cs | 44 +++++++++---- src/iOS/AppDelegate.cs | 2 +- src/iOS/Services/ClipboardService.cs | 14 ----- src/iOS/Services/DeviceActionService.cs | 20 ++++++ src/iOS/iOS.csproj | 2 +- 26 files changed, 285 insertions(+), 71 deletions(-) create mode 100644 src/Android/Resources/xml/filepaths.xml delete mode 100644 src/Android/Services/ClipboardService.cs create mode 100644 src/Android/Services/DeviceActionService.cs delete mode 100644 src/App/Abstractions/Services/IClipboardService.cs create mode 100644 src/App/Abstractions/Services/IDeviceActionService.cs delete mode 100644 src/iOS/Services/ClipboardService.cs create mode 100644 src/iOS/Services/DeviceActionService.cs diff --git a/src/Android/Android.csproj b/src/Android/Android.csproj index 740a83ff9..030c56350 100644 --- a/src/Android/Android.csproj +++ b/src/Android/Android.csproj @@ -323,7 +323,7 @@ - + @@ -872,6 +872,9 @@ + + + diff --git a/src/Android/MainApplication.cs b/src/Android/MainApplication.cs index 78e6f59b9..485a2bca3 100644 --- a/src/Android/MainApplication.cs +++ b/src/Android/MainApplication.cs @@ -207,7 +207,7 @@ namespace Bit.Android container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); - container.RegisterSingleton(); + container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); diff --git a/src/Android/Properties/AndroidManifest.xml b/src/Android/Properties/AndroidManifest.xml index 12298232d..7cbe1d085 100644 --- a/src/Android/Properties/AndroidManifest.xml +++ b/src/Android/Properties/AndroidManifest.xml @@ -12,5 +12,15 @@ - + + + + + diff --git a/src/Android/Resources/Resource.Designer.cs b/src/Android/Resources/Resource.Designer.cs index 519598826..553e91046 100644 --- a/src/Android/Resources/Resource.Designer.cs +++ b/src/Android/Resources/Resource.Designer.cs @@ -5296,6 +5296,9 @@ namespace Bit.Android // aapt resource value: 0x7f060000 public const int accessibilityservice = 2131099648; + // aapt resource value: 0x7f060001 + public const int filepaths = 2131099649; + static Xml() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); diff --git a/src/Android/Resources/xml/filepaths.xml b/src/Android/Resources/xml/filepaths.xml new file mode 100644 index 000000000..3ae5d6901 --- /dev/null +++ b/src/Android/Resources/xml/filepaths.xml @@ -0,0 +1,4 @@ + + + + diff --git a/src/Android/Services/ClipboardService.cs b/src/Android/Services/ClipboardService.cs deleted file mode 100644 index c2308d289..000000000 --- a/src/Android/Services/ClipboardService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Android.Content; -using Bit.App.Abstractions; -using Xamarin.Forms; - -namespace Bit.Android.Services -{ - public class ClipboardService : IClipboardService - { - public void CopyToClipboard(string text) - { - var clipboardManager = (ClipboardManager)Forms.Context.GetSystemService(Context.ClipboardService); - clipboardManager.Text = text; - } - } -} diff --git a/src/Android/Services/DeviceActionService.cs b/src/Android/Services/DeviceActionService.cs new file mode 100644 index 000000000..32351fa39 --- /dev/null +++ b/src/Android/Services/DeviceActionService.cs @@ -0,0 +1,63 @@ +using System; +using Android.Content; +using Bit.App.Abstractions; +using Xamarin.Forms; +using Java.IO; +using Android.Webkit; +using Plugin.CurrentActivity; +using System.IO; +using System.Diagnostics; +using Android.Support.V4.Content; + +namespace Bit.Android.Services +{ + public class DeviceActionService : IDeviceActionService + { + public void CopyToClipboard(string text) + { + var clipboardManager = (ClipboardManager)Forms.Context.GetSystemService(Context.ClipboardService); + clipboardManager.Text = text; + } + + public bool OpenFile(byte[] fileData, string id, string fileName) + { + var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName); + if(extension == null) + { + return false; + } + + var mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension.ToLower()); + if(mimeType == null) + { + return false; + } + + var cachePath = CrossCurrentActivity.Current.Activity.CacheDir; + var filePath = Path.Combine(cachePath.Path, fileName); + System.IO.File.WriteAllBytes(filePath, fileData); + var file = new Java.IO.File(cachePath, fileName); + try + { + var packageManager = CrossCurrentActivity.Current.Activity.PackageManager; + var testIntent = new Intent(Intent.ActionView); + testIntent.SetType(mimeType); + var list = packageManager.QueryIntentActivities(testIntent, + global::Android.Content.PM.PackageInfoFlags.MatchDefaultOnly); + if(list.Count > 0 && file.IsFile) + { + var intent = new Intent(Intent.ActionView); + var uri = FileProvider.GetUriForFile(CrossCurrentActivity.Current.Activity.ApplicationContext, + "com.x8bit.bitwarden.fileprovider", file); + intent.SetDataAndType(uri, mimeType); + intent.SetFlags(ActivityFlags.GrantReadUriPermission); + CrossCurrentActivity.Current.Activity.StartActivity(intent); + return true; + } + } + catch { } + + return false; + } + } +} diff --git a/src/App/Abstractions/Services/IClipboardService.cs b/src/App/Abstractions/Services/IClipboardService.cs deleted file mode 100644 index ba4d54c58..000000000 --- a/src/App/Abstractions/Services/IClipboardService.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Bit.App.Abstractions -{ - public interface IClipboardService - { - void CopyToClipboard(string text); - } -} diff --git a/src/App/Abstractions/Services/ICryptoService.cs b/src/App/Abstractions/Services/ICryptoService.cs index d59e2835e..2bd6457d5 100644 --- a/src/App/Abstractions/Services/ICryptoService.cs +++ b/src/App/Abstractions/Services/ICryptoService.cs @@ -19,6 +19,7 @@ namespace Bit.App.Abstractions void ClearKeys(); string Decrypt(CipherString encyptedValue, SymmetricCryptoKey key = null); byte[] DecryptToBytes(CipherString encyptedValue, SymmetricCryptoKey key = null); + byte[] DecryptToBytes(byte[] encyptedValue, SymmetricCryptoKey key = null); byte[] RsaDecryptToBytes(CipherString encyptedValue, byte[] privateKey); CipherString Encrypt(string plaintextValue, SymmetricCryptoKey key = null); SymmetricCryptoKey MakeKeyFromPassword(string password, string salt); diff --git a/src/App/Abstractions/Services/IDeviceActionService.cs b/src/App/Abstractions/Services/IDeviceActionService.cs new file mode 100644 index 000000000..6e4ce8f6a --- /dev/null +++ b/src/App/Abstractions/Services/IDeviceActionService.cs @@ -0,0 +1,8 @@ +namespace Bit.App.Abstractions +{ + public interface IDeviceActionService + { + void CopyToClipboard(string text); + bool OpenFile(byte[] fileData, string id, string fileName); + } +} diff --git a/src/App/Abstractions/Services/ILoginService.cs b/src/App/Abstractions/Services/ILoginService.cs index c4fa3b373..0e6167b9a 100644 --- a/src/App/Abstractions/Services/ILoginService.cs +++ b/src/App/Abstractions/Services/ILoginService.cs @@ -14,5 +14,6 @@ namespace Bit.App.Abstractions Task, IEnumerable>> GetAllAsync(string uriString); Task> SaveAsync(Login login); Task DeleteAsync(string id); + Task DownloadAndDecryptAttachmentAsync(SymmetricCryptoKey key, string url); } } diff --git a/src/App/App.csproj b/src/App/App.csproj index 78ad71328..ca9d541dd 100644 --- a/src/App/App.csproj +++ b/src/App/App.csproj @@ -51,7 +51,7 @@ - + diff --git a/src/App/Models/Page/VaultViewLoginPageModel.cs b/src/App/Models/Page/VaultViewLoginPageModel.cs index 99fad4de7..6b35b07ab 100644 --- a/src/App/Models/Page/VaultViewLoginPageModel.cs +++ b/src/App/Models/Page/VaultViewLoginPageModel.cs @@ -223,7 +223,8 @@ namespace Bit.App.Models.Page { Id = attachment.Id, Name = attachment.FileName?.Decrypt(login.OrganizationId), - Size = attachment.SizeName + Size = attachment.SizeName, + Url = attachment.Url }); } Attachments = attachments; @@ -239,6 +240,7 @@ namespace Bit.App.Models.Page public string Id { get; set; } public string Name { get; set; } public string Size { get; set; } + public string Url { get; set; } } } } diff --git a/src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs b/src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs index bd60ec64a..d19c40198 100644 --- a/src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs +++ b/src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs @@ -16,7 +16,7 @@ namespace Bit.App.Pages private readonly IUserDialogs _userDialogs; private readonly IPasswordGenerationService _passwordGenerationService; private readonly ISettings _settings; - private readonly IClipboardService _clipboardService; + private readonly IDeviceActionService _clipboardService; private readonly IGoogleAnalyticsService _googleAnalyticsService; private readonly Action _passwordValueAction; private readonly bool _fromAutofill; @@ -26,7 +26,7 @@ namespace Bit.App.Pages _userDialogs = Resolver.Resolve(); _passwordGenerationService = Resolver.Resolve(); _settings = Resolver.Resolve(); - _clipboardService = Resolver.Resolve(); + _clipboardService = Resolver.Resolve(); _googleAnalyticsService = Resolver.Resolve(); _passwordValueAction = passwordValueAction; _fromAutofill = fromAutofill; diff --git a/src/App/Pages/Vault/VaultAutofillListLoginsPage.cs b/src/App/Pages/Vault/VaultAutofillListLoginsPage.cs index 809f85eb4..84fe99386 100644 --- a/src/App/Pages/Vault/VaultAutofillListLoginsPage.cs +++ b/src/App/Pages/Vault/VaultAutofillListLoginsPage.cs @@ -19,7 +19,7 @@ namespace Bit.App.Pages { private readonly ILoginService _loginService; private readonly IDeviceInfoService _deviceInfoService; - private readonly IClipboardService _clipboardService; + private readonly IDeviceActionService _clipboardService; private readonly ISettingsService _settingsService; private CancellationTokenSource _filterResultsCancellationTokenSource; private readonly string _name; @@ -47,7 +47,7 @@ namespace Bit.App.Pages _loginService = Resolver.Resolve(); _deviceInfoService = Resolver.Resolve(); - _clipboardService = Resolver.Resolve(); + _clipboardService = Resolver.Resolve(); _settingsService = Resolver.Resolve(); UserDialogs = Resolver.Resolve(); GoogleAnalyticsService = Resolver.Resolve(); diff --git a/src/App/Pages/Vault/VaultListLoginsPage.cs b/src/App/Pages/Vault/VaultListLoginsPage.cs index 4b6dd1158..0d6d0ecf8 100644 --- a/src/App/Pages/Vault/VaultListLoginsPage.cs +++ b/src/App/Pages/Vault/VaultListLoginsPage.cs @@ -24,7 +24,7 @@ namespace Bit.App.Pages private readonly ILoginService _loginService; private readonly IUserDialogs _userDialogs; private readonly IConnectivity _connectivity; - private readonly IClipboardService _clipboardService; + private readonly IDeviceActionService _clipboardService; private readonly ISyncService _syncService; private readonly IPushNotification _pushNotification; private readonly IDeviceInfoService _deviceInfoService; @@ -41,7 +41,7 @@ namespace Bit.App.Pages _loginService = Resolver.Resolve(); _connectivity = Resolver.Resolve(); _userDialogs = Resolver.Resolve(); - _clipboardService = Resolver.Resolve(); + _clipboardService = Resolver.Resolve(); _syncService = Resolver.Resolve(); _pushNotification = Resolver.Resolve(); _deviceInfoService = Resolver.Resolve(); diff --git a/src/App/Pages/Vault/VaultViewLoginPage.cs b/src/App/Pages/Vault/VaultViewLoginPage.cs index ebc329a44..b4e513d07 100644 --- a/src/App/Pages/Vault/VaultViewLoginPage.cs +++ b/src/App/Pages/Vault/VaultViewLoginPage.cs @@ -16,14 +16,14 @@ namespace Bit.App.Pages private readonly string _loginId; private readonly ILoginService _loginService; private readonly IUserDialogs _userDialogs; - private readonly IClipboardService _clipboardService; + private readonly IDeviceActionService _deviceActionService; public VaultViewLoginPage(string loginId) { _loginId = loginId; _loginService = Resolver.Resolve(); _userDialogs = Resolver.Resolve(); - _clipboardService = Resolver.Resolve(); + _deviceActionService = Resolver.Resolve(); Init(); } @@ -194,9 +194,9 @@ namespace Bit.App.Pages AttachmentsSection = new TableSection(AppResources.Attachments); foreach(var attachment in Model.Attachments) { - AttachmentsSection.Add(new AttachmentViewCell(attachment, () => + AttachmentsSection.Add(new AttachmentViewCell(attachment, async () => { - + await SaveAttachmentAsync(attachment); })); } Table.Root.Add(AttachmentsSection); @@ -211,6 +211,23 @@ namespace Bit.App.Pages EditItem.Dispose(); } + private async Task SaveAttachmentAsync(VaultViewLoginPageModel.Attachment attachment) + { + var data = await _loginService.DownloadAndDecryptAttachmentAsync(null, attachment.Url); + if(data == null) + { + await _userDialogs.AlertAsync(AppResources.UnableToDownloadFile, null, AppResources.Ok); + return; + } + + var opened = _deviceActionService.OpenFile(data, attachment.Id, attachment.Name); + if(!opened) + { + await _userDialogs.AlertAsync(AppResources.UnableToOpenFile, null, AppResources.Ok); + return; + } + } + private void NotesCell_Tapped(object sender, EventArgs e) { Copy(Model.Notes, AppResources.Notes); @@ -218,7 +235,7 @@ namespace Bit.App.Pages private void Copy(string copyText, string alertLabel) { - _clipboardService.CopyToClipboard(copyText); + _deviceActionService.CopyToClipboard(copyText); _userDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel)); } diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs index c7768822d..39e230a4d 100644 --- a/src/App/Resources/AppResources.Designer.cs +++ b/src/App/Resources/AppResources.Designer.cs @@ -1987,6 +1987,24 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to Unable to download file.. + /// + public static string UnableToDownloadFile { + get { + return ResourceManager.GetString("UnableToDownloadFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unable to open this type of file on your device.. + /// + public static string UnableToOpenFile { + get { + return ResourceManager.GetString("UnableToOpenFile", resourceCulture); + } + } + /// /// Looks up a localized string similar to Unlock with {0}. /// diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx index fc3405956..39f64722d 100644 --- a/src/App/Resources/AppResources.resx +++ b/src/App/Resources/AppResources.resx @@ -908,4 +908,10 @@ Attachments + + Unable to download file. + + + Unable to open this type of file on your device. + \ No newline at end of file diff --git a/src/App/Services/CryptoService.cs b/src/App/Services/CryptoService.cs index 044a98856..4483b481c 100644 --- a/src/App/Services/CryptoService.cs +++ b/src/App/Services/CryptoService.cs @@ -297,18 +297,61 @@ namespace Bit.App.Services // Old encrypt-then-mac scheme, swap out the key if(_legacyEtmKey == null) { - _legacyEtmKey = new SymmetricCryptoKey(key.Key, Enums.EncryptionType.AesCbc128_HmacSha256_B64); + _legacyEtmKey = new SymmetricCryptoKey(key.Key, EncryptionType.AesCbc128_HmacSha256_B64); } key = _legacyEtmKey; } - if(encyptedValue.EncryptionType != key.EncryptionType) + return Crypto.AesCbcDecrypt(encyptedValue, key); + } + + public byte[] DecryptToBytes(byte[] encyptedValue, SymmetricCryptoKey key = null) + { + if(key == null) { - throw new ArgumentException("encType unavailable."); + key = EncKey ?? Key; } - return Crypto.AesCbcDecrypt(encyptedValue, key); + if(key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + if(encyptedValue == null || encyptedValue.Length == 0) + { + throw new ArgumentNullException(nameof(encyptedValue)); + } + + byte[] ct, iv, mac = null; + var encType = (EncryptionType)encyptedValue[0]; + switch(encType) + { + case EncryptionType.AesCbc128_HmacSha256_B64: + case EncryptionType.AesCbc256_HmacSha256_B64: + if(encyptedValue.Length <= 49) + { + throw new InvalidOperationException("Invalid value length."); + } + + iv = new ArraySegment(encyptedValue, 1, 16).ToArray(); + mac = new ArraySegment(encyptedValue, 17, 32).ToArray(); + ct = new ArraySegment(encyptedValue, 49, encyptedValue.Length - 49).ToArray(); + break; + case EncryptionType.AesCbc256_B64: + if(encyptedValue.Length <= 17) + { + throw new InvalidOperationException("Invalid value length."); + } + + iv = new ArraySegment(encyptedValue, 1, 16).ToArray(); + ct = new ArraySegment(encyptedValue, 17, encyptedValue.Length - 17).ToArray(); + break; + default: + throw new InvalidOperationException("Invalid encryption type."); + } + + return Crypto.AesCbcDecrypt(encType, ct, iv, mac, key); } public byte[] RsaDecryptToBytes(CipherString encyptedValue, byte[] privateKey) diff --git a/src/App/Services/LoginService.cs b/src/App/Services/LoginService.cs index dc5ab2aa9..8f890d597 100644 --- a/src/App/Services/LoginService.cs +++ b/src/App/Services/LoginService.cs @@ -7,6 +7,7 @@ using Bit.App.Models; using Bit.App.Models.Api; using Bit.App.Models.Data; using Xamarin.Forms; +using System.Net.Http; namespace Bit.App.Services { @@ -17,19 +18,22 @@ namespace Bit.App.Services private readonly IAuthService _authService; private readonly ILoginApiRepository _loginApiRepository; private readonly ISettingsService _settingsService; + private readonly ICryptoService _cryptoService; public LoginService( ILoginRepository loginRepository, IAttachmentRepository attachmentRepository, IAuthService authService, ILoginApiRepository loginApiRepository, - ISettingsService settingsService) + ISettingsService settingsService, + ICryptoService cryptoService) { _loginRepository = loginRepository; _attachmentRepository = attachmentRepository; _authService = authService; _loginApiRepository = loginApiRepository; _settingsService = settingsService; + _cryptoService = cryptoService; } public async Task GetByIdAsync(string id) @@ -217,6 +221,33 @@ namespace Bit.App.Services return response; } + public async Task DownloadAndDecryptAttachmentAsync(SymmetricCryptoKey key, string url) + { + using(var client = new HttpClient()) + { + try + { + var response = await client.GetAsync(new Uri(url)).ConfigureAwait(false); + if(!response.IsSuccessStatusCode) + { + return null; + } + + var data = await response.Content.ReadAsByteArrayAsync(); + if(data == null) + { + return null; + } + + return _cryptoService.DecryptToBytes(data, key); + } + catch + { + return null; + } + } + } + private string WebUriFromAndroidAppUri(string androidAppUriString) { if(!UriIsAndroidApp(androidAppUriString)) diff --git a/src/App/Utilities/Crypto.cs b/src/App/Utilities/Crypto.cs index 0d850ff35..ba6800352 100644 --- a/src/App/Utilities/Crypto.cs +++ b/src/App/Utilities/Crypto.cs @@ -1,4 +1,5 @@ -using Bit.App.Models; +using Bit.App.Enums; +using Bit.App.Models; using PCLCrypto; using System; using System.Collections.Generic; @@ -32,21 +33,41 @@ namespace Bit.App.Utilities public static byte[] AesCbcDecrypt(CipherString encyptedValue, SymmetricCryptoKey key) { - if(key == null) - { - throw new ArgumentNullException(nameof(key)); - } - if(encyptedValue == null) { throw new ArgumentNullException(nameof(encyptedValue)); } - if(key.MacKey != null && !string.IsNullOrWhiteSpace(encyptedValue.Mac)) + return AesCbcDecrypt(encyptedValue.EncryptionType, encyptedValue.CipherTextBytes, + encyptedValue.InitializationVectorBytes, encyptedValue.MacBytes, key); + } + + public static byte[] AesCbcDecrypt(EncryptionType type, byte[] ct, byte[] iv, byte[] mac, SymmetricCryptoKey key) + { + if(key == null) { - var computedMacBytes = ComputeMac(encyptedValue.CipherTextBytes, - encyptedValue.InitializationVectorBytes, key.MacKey); - if(!MacsEqual(key.MacKey, computedMacBytes, encyptedValue.MacBytes)) + throw new ArgumentNullException(nameof(key)); + } + + if(ct == null) + { + throw new ArgumentNullException(nameof(ct)); + } + + if(iv == null) + { + throw new ArgumentNullException(nameof(iv)); + } + + if(key.EncryptionType != type) + { + throw new InvalidOperationException(nameof(type)); + } + + if(key.MacKey != null && mac != null) + { + var computedMacBytes = ComputeMac(ct, iv, key.MacKey); + if(!MacsEqual(key.MacKey, computedMacBytes, mac)) { throw new InvalidOperationException("MAC failed."); } @@ -54,8 +75,7 @@ namespace Bit.App.Utilities var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7); var cryptoKey = provider.CreateSymmetricKey(key.EncKey); - var decryptedBytes = WinRTCrypto.CryptographicEngine.Decrypt(cryptoKey, encyptedValue.CipherTextBytes, - encyptedValue.InitializationVectorBytes); + var decryptedBytes = WinRTCrypto.CryptographicEngine.Decrypt(cryptoKey, ct, iv); return decryptedBytes; } diff --git a/src/iOS/AppDelegate.cs b/src/iOS/AppDelegate.cs index 81189b4a9..cc290f34d 100644 --- a/src/iOS/AppDelegate.cs +++ b/src/iOS/AppDelegate.cs @@ -254,7 +254,7 @@ namespace Bit.iOS container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); - container.RegisterSingleton(); + container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); diff --git a/src/iOS/Services/ClipboardService.cs b/src/iOS/Services/ClipboardService.cs deleted file mode 100644 index 8ac1f4d3a..000000000 --- a/src/iOS/Services/ClipboardService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Bit.App.Abstractions; -using UIKit; - -namespace Bit.iOS.Services -{ - public class ClipboardService : IClipboardService - { - public void CopyToClipboard(string text) - { - UIPasteboard clipboard = UIPasteboard.General; - clipboard.String = text; - } - } -} diff --git a/src/iOS/Services/DeviceActionService.cs b/src/iOS/Services/DeviceActionService.cs new file mode 100644 index 000000000..618edc608 --- /dev/null +++ b/src/iOS/Services/DeviceActionService.cs @@ -0,0 +1,20 @@ +using System; +using Bit.App.Abstractions; +using UIKit; + +namespace Bit.iOS.Services +{ + public class DeviceActionService : IDeviceActionService + { + public void CopyToClipboard(string text) + { + UIPasteboard clipboard = UIPasteboard.General; + clipboard.String = text; + } + + public bool OpenFile(byte[] fileData, string id, string fileName) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/iOS/iOS.csproj b/src/iOS/iOS.csproj index fbf076127..dd0e6dae7 100644 --- a/src/iOS/iOS.csproj +++ b/src/iOS/iOS.csproj @@ -232,7 +232,7 @@ - +