VLCNetworkLoginDataSourceSavedLogins.m 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*****************************************************************************
  2. * VLC for iOS
  3. *****************************************************************************
  4. * Copyright (c) 2016 VideoLAN. All rights reserved.
  5. * $Id$
  6. *
  7. * Authors: Vincent L. Cone <vincent.l.cone # tuta.io>
  8. *
  9. * Refer to the COPYING file of the official project for license.
  10. *****************************************************************************/
  11. #import "VLCNetworkLoginDataSourceSavedLogins.h"
  12. #import <XKKeychain/XKKeychainGenericPasswordItem.h>
  13. #import "VLCNetworkServerLoginInformation+Keychain.h"
  14. static NSString *const VLCNetworkLoginSavedLoginCellIdentifier = @"VLCNetworkLoginSavedLoginCell";
  15. @interface VLCNetworkLoginSavedLoginCell : UITableViewCell
  16. @end
  17. @interface VLCNetworkLoginDataSourceSavedLogins ()
  18. @property (nonatomic) NSMutableArray<NSString *> *serverList;
  19. @property (nonatomic, weak) UITableView *tableView;
  20. @end
  21. @implementation VLCNetworkLoginDataSourceSavedLogins
  22. @synthesize sectionIndex = _sectionIndex;
  23. - (instancetype)init
  24. {
  25. self = [super init];
  26. if (self) {
  27. _serverList = [NSMutableArray array];
  28. NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
  29. [notificationCenter addObserver:self
  30. selector:@selector(ubiquitousKeyValueStoreDidChange:)
  31. name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
  32. object:[NSUbiquitousKeyValueStore defaultStore]];
  33. NSUbiquitousKeyValueStore *ukvStore = [NSUbiquitousKeyValueStore defaultStore];
  34. [ukvStore synchronize];
  35. NSArray *ukvServerList = [ukvStore arrayForKey:kVLCStoredServerList];
  36. if (ukvServerList) {
  37. [_serverList addObjectsFromArray:ukvServerList];
  38. }
  39. [self migrateServerlistToCloudIfNeeded];
  40. }
  41. return self;
  42. }
  43. - (void)migrateServerlistToCloudIfNeeded
  44. {
  45. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  46. if (![defaults boolForKey:kVLCMigratedToUbiquitousStoredServerList]) {
  47. /* we need to migrate from previous, insecure storage fields */
  48. NSArray *ftpServerList = [defaults objectForKey:kVLCFTPServer];
  49. NSArray *ftpLoginList = [defaults objectForKey:kVLCFTPLogin];
  50. NSArray *ftpPasswordList = [defaults objectForKey:kVLCFTPPassword];
  51. NSUInteger count = ftpServerList.count;
  52. if (count > 0) {
  53. for (NSUInteger i = 0; i < count; i++) {
  54. XKKeychainGenericPasswordItem *keychainItem = [[XKKeychainGenericPasswordItem alloc] init];
  55. keychainItem.service = ftpServerList[i];
  56. keychainItem.account = ftpLoginList[i];
  57. keychainItem.secret.stringValue = ftpPasswordList[i];
  58. [keychainItem saveWithError:nil];
  59. [_serverList addObject:ftpServerList[i]];
  60. }
  61. }
  62. NSArray *plexServerList = [defaults objectForKey:kVLCPLEXServer];
  63. NSArray *plexPortList = [defaults objectForKey:kVLCPLEXPort];
  64. count = plexServerList.count;
  65. if (count > 0) {
  66. for (NSUInteger i = 0; i < count; i++) {
  67. [_serverList addObject:[NSString stringWithFormat:@"plex://%@:%@", plexServerList[i], plexPortList[i]]];
  68. }
  69. }
  70. NSUbiquitousKeyValueStore *ukvStore = [NSUbiquitousKeyValueStore defaultStore];
  71. [ukvStore setArray:_serverList forKey:kVLCStoredServerList];
  72. [ukvStore synchronize];
  73. [defaults setBool:YES forKey:kVLCMigratedToUbiquitousStoredServerList];
  74. }
  75. }
  76. - (void)ubiquitousKeyValueStoreDidChange:(NSNotification *)notification
  77. {
  78. /* TODO: don't blindly trust that the Cloud knows best */
  79. _serverList = [NSMutableArray arrayWithArray:[[NSUbiquitousKeyValueStore defaultStore] arrayForKey:kVLCStoredServerList]];
  80. // TODO: Vincent: array diff with insert and delete
  81. [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:self.sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];;
  82. }
  83. #pragma mark - API
  84. - (BOOL)saveLogin:(VLCNetworkServerLoginInformation *)login error:(NSError * _Nullable __autoreleasing *)error
  85. {
  86. NSError *innerError = nil;
  87. BOOL success = [login saveLoginInformationToKeychainWithError:&innerError];
  88. if(!success) {
  89. NSLog(@"Failed to save login with error: %@",innerError);
  90. if (error) {
  91. *error = innerError;
  92. }
  93. }
  94. // even if the save fails we want to add the server identifier to the iCloud list
  95. NSString *serviceIdentifier = [login keychainServiceIdentifier];
  96. [_serverList addObject:serviceIdentifier];
  97. NSUbiquitousKeyValueStore *ukvStore = [NSUbiquitousKeyValueStore defaultStore];
  98. [ukvStore setArray:_serverList forKey:kVLCStoredServerList];
  99. [ukvStore synchronize];
  100. // TODO: Vincent: add row directly instead of section reload
  101. [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:self.sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
  102. return success;
  103. }
  104. - (BOOL)deleteItemAtRow:(NSUInteger)row error:(NSError * _Nullable __autoreleasing *)error
  105. {
  106. NSString *serviceString = _serverList[row];
  107. NSError *innerError = nil;
  108. BOOL success = [XKKeychainGenericPasswordItem removeItemsForService:serviceString error:&innerError];
  109. if (!success) {
  110. NSLog(@"Failed to delete login with error: %@",innerError);
  111. }
  112. if (error) {
  113. *error = innerError;
  114. }
  115. [_serverList removeObject:serviceString];
  116. NSUbiquitousKeyValueStore *ukvStore = [NSUbiquitousKeyValueStore defaultStore];
  117. [ukvStore setArray:_serverList forKey:kVLCStoredServerList];
  118. [ukvStore synchronize];
  119. // TODO: Vincent: add row directly instead of section reload
  120. [self.tableView reloadData];
  121. return success;
  122. }
  123. #pragma mark -
  124. - (void)configureWithTableView:(UITableView *)tableView
  125. {
  126. [tableView registerClass:[VLCNetworkLoginSavedLoginCell class] forCellReuseIdentifier:VLCNetworkLoginSavedLoginCellIdentifier];
  127. self.tableView = tableView;
  128. }
  129. - (NSUInteger)numberOfRowsInTableView:(UITableView *)tableView
  130. {
  131. return self.serverList.count;
  132. }
  133. - (NSString *)cellIdentifierForRow:(NSUInteger)row
  134. {
  135. return VLCNetworkLoginSavedLoginCellIdentifier;
  136. }
  137. - (void)configureCell:(UITableViewCell *)cell forRow:(NSUInteger)row
  138. {
  139. NSString *serviceString = _serverList[row];
  140. NSURL *service = [NSURL URLWithString:serviceString];
  141. cell.textLabel.text = [NSString stringWithFormat:@"%@ [%@]", service.host, [service.scheme uppercaseString]];
  142. XKKeychainGenericPasswordItem *keychainItem = [XKKeychainGenericPasswordItem itemsForService:serviceString error:nil].firstObject;
  143. if (keychainItem) {
  144. cell.detailTextLabel.text = keychainItem.account;
  145. } else {
  146. cell.detailTextLabel.text = @"";
  147. }
  148. }
  149. - (void)commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRow:(NSUInteger)row
  150. {
  151. if (editingStyle == UITableViewCellEditingStyleDelete) {
  152. [self deleteItemAtRow:row error:nil];
  153. }
  154. }
  155. - (void)didSelectRow:(NSUInteger)row
  156. {
  157. [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:self.sectionIndex] animated:YES];
  158. VLCNetworkServerLoginInformation *login = [VLCNetworkServerLoginInformation loginInformationWithKeychainIdentifier:self.serverList[row]];
  159. [login loadLoginInformationFromKeychainWithError:nil];
  160. [self.delegate loginsDataSource:self selectedLogin:login];
  161. }
  162. @end
  163. @implementation VLCNetworkLoginSavedLoginCell
  164. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
  165. {
  166. self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
  167. if (self) {
  168. self.textLabel.textColor = [UIColor whiteColor];
  169. self.detailTextLabel.textColor = [UIColor VLCLightTextColor];
  170. self.backgroundColor = [UIColor VLCDarkBackgroundColor];
  171. }
  172. return self;
  173. }
  174. @end