123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- /*****************************************************************************
- * VLC for iOS
- *****************************************************************************
- * Copyright (c) 2016 VideoLAN. All rights reserved.
- * $Id$
- *
- * Authors: Vincent L. Cone <vincent.l.cone # tuta.io>
- *
- * Refer to the COPYING file of the official project for license.
- *****************************************************************************/
- #import "VLCNetworkLoginDataSourceSavedLogins.h"
- #import <XKKeychain/XKKeychainGenericPasswordItem.h>
- #import "VLCNetworkServerLoginInformation+Keychain.h"
- #import "VLC-Swift.h"
- static NSString *const VLCNetworkLoginSavedLoginCellIdentifier = @"VLCNetworkLoginSavedLoginCell";
- @interface VLCNetworkLoginSavedLoginCell : UITableViewCell
- @end
- @interface VLCNetworkLoginDataSourceSavedLogins ()
- @property (nonatomic) NSMutableArray<NSString *> *serverList;
- @property (nonatomic, weak) UITableView *tableView;
- @end
- @implementation VLCNetworkLoginDataSourceSavedLogins
- @synthesize sectionIndex = _sectionIndex;
- - (instancetype)init
- {
- self = [super init];
- if (self) {
- _serverList = [NSMutableArray array];
- NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
- [notificationCenter addObserver:self
- selector:@selector(ubiquitousKeyValueStoreDidChange:)
- name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
- object:[NSUbiquitousKeyValueStore defaultStore]];
- NSUbiquitousKeyValueStore *ukvStore = [NSUbiquitousKeyValueStore defaultStore];
- [ukvStore synchronize];
- NSArray *ukvServerList = [ukvStore arrayForKey:kVLCStoredServerList];
- if (ukvServerList) {
- [_serverList addObjectsFromArray:ukvServerList];
- }
- [self migrateServerlistToCloudIfNeeded];
- }
- return self;
- }
- - (void)migrateServerlistToCloudIfNeeded
- {
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
- if (![defaults boolForKey:kVLCMigratedToUbiquitousStoredServerList]) {
- /* we need to migrate from previous, insecure storage fields */
- NSArray *ftpServerList = [defaults objectForKey:kVLCFTPServer];
- NSArray *ftpLoginList = [defaults objectForKey:kVLCFTPLogin];
- NSArray *ftpPasswordList = [defaults objectForKey:kVLCFTPPassword];
- NSUInteger count = ftpServerList.count;
- if (count > 0) {
- for (NSUInteger i = 0; i < count; i++) {
- XKKeychainGenericPasswordItem *keychainItem = [[XKKeychainGenericPasswordItem alloc] init];
- keychainItem.service = ftpServerList[i];
- keychainItem.account = ftpLoginList[i];
- keychainItem.secret.stringValue = ftpPasswordList[i];
- [keychainItem saveWithError:nil];
- [_serverList addObject:ftpServerList[i]];
- }
- }
- NSArray *plexServerList = [defaults objectForKey:kVLCPLEXServer];
- NSArray *plexPortList = [defaults objectForKey:kVLCPLEXPort];
- count = plexServerList.count;
- if (count > 0) {
- for (NSUInteger i = 0; i < count; i++) {
- [_serverList addObject:[NSString stringWithFormat:@"plex://%@:%@", plexServerList[i], plexPortList[i]]];
- }
- }
- NSUbiquitousKeyValueStore *ukvStore = [NSUbiquitousKeyValueStore defaultStore];
- [ukvStore setArray:_serverList forKey:kVLCStoredServerList];
- [ukvStore synchronize];
- [defaults setBool:YES forKey:kVLCMigratedToUbiquitousStoredServerList];
- }
- }
- - (void)ubiquitousKeyValueStoreDidChange:(NSNotification *)notification
- {
- /* TODO: don't blindly trust that the Cloud knows best */
- _serverList = [NSMutableArray arrayWithArray:[[NSUbiquitousKeyValueStore defaultStore] arrayForKey:kVLCStoredServerList]];
- // TODO: Vincent: array diff with insert and delete
- [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:self.sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];;
- }
- #pragma mark - API
- - (BOOL)saveLogin:(VLCNetworkServerLoginInformation *)login error:(NSError * _Nullable __autoreleasing *)error
- {
- NSError *innerError = nil;
- BOOL success = [login saveLoginInformationToKeychainWithError:&innerError];
- if(!success) {
- NSLog(@"Failed to save login with error: %@",innerError);
- if (error) {
- *error = innerError;
- }
- }
- // even if the save fails we want to add the server identifier to the iCloud list
- NSString *serviceIdentifier = [login keychainServiceIdentifier];
- [_serverList addObject:serviceIdentifier];
- NSUbiquitousKeyValueStore *ukvStore = [NSUbiquitousKeyValueStore defaultStore];
- [ukvStore setArray:_serverList forKey:kVLCStoredServerList];
- [ukvStore synchronize];
- // TODO: Vincent: add row directly instead of section reload
- [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:self.sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
- return success;
- }
- - (BOOL)deleteItemAtRow:(NSUInteger)row error:(NSError * _Nullable __autoreleasing *)error
- {
- NSString *serviceString = _serverList[row];
- NSError *innerError = nil;
- BOOL success = [XKKeychainGenericPasswordItem removeItemsForService:serviceString error:&innerError];
- if (!success) {
- NSLog(@"Failed to delete login with error: %@",innerError);
- }
- if (error) {
- *error = innerError;
- }
- [_serverList removeObject:serviceString];
- NSUbiquitousKeyValueStore *ukvStore = [NSUbiquitousKeyValueStore defaultStore];
- [ukvStore setArray:_serverList forKey:kVLCStoredServerList];
- [ukvStore synchronize];
- // TODO: Vincent: add row directly instead of section reload
- [self.tableView reloadData];
- return success;
- }
- #pragma mark -
- - (void)configureWithTableView:(UITableView *)tableView
- {
- [tableView registerClass:[VLCNetworkLoginSavedLoginCell class] forCellReuseIdentifier:VLCNetworkLoginSavedLoginCellIdentifier];
- self.tableView = tableView;
- }
- - (NSUInteger)numberOfRowsInTableView:(UITableView *)tableView
- {
- return self.serverList.count;
- }
- - (NSString *)cellIdentifierForRow:(NSUInteger)row
- {
- return VLCNetworkLoginSavedLoginCellIdentifier;
- }
- - (void)configureCell:(UITableViewCell *)cell forRow:(NSUInteger)row
- {
- NSString *serviceString = _serverList[row];
- NSURL *service = [NSURL URLWithString:serviceString];
- cell.textLabel.text = [NSString stringWithFormat:@"%@ [%@]", service.host, [service.scheme uppercaseString]];
- XKKeychainGenericPasswordItem *keychainItem = [XKKeychainGenericPasswordItem itemsForService:serviceString error:nil].firstObject;
- if (keychainItem) {
- cell.detailTextLabel.text = keychainItem.account;
- } else {
- cell.detailTextLabel.text = @"";
- }
- }
- - (void)commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRow:(NSUInteger)row
- {
- if (editingStyle == UITableViewCellEditingStyleDelete) {
- [self deleteItemAtRow:row error:nil];
- }
- }
- - (void)didSelectRow:(NSUInteger)row
- {
- [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:self.sectionIndex] animated:YES];
- VLCNetworkServerLoginInformation *login = [VLCNetworkServerLoginInformation loginInformationWithKeychainIdentifier:self.serverList[row]];
- [login loadLoginInformationFromKeychainWithError:nil];
- [self.delegate loginsDataSource:self selectedLogin:login];
- }
- @end
- @implementation VLCNetworkLoginSavedLoginCell
- - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
- {
- self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
- if (self) {
- self.textLabel.textColor = PresentationTheme.current.colors.cellTextColor;
- self.detailTextLabel.textColor = PresentationTheme.current.colors.lightTextColor;
- self.backgroundColor = PresentationTheme.current.colors.background;
- }
- return self;
- }
- @end
|