VLCNetworkLoginDataSourceSavedLogins.m 7.9 KB

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