/*
* Copyright (c) 2016, Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/?p=scrypt-pwgen.git
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
@import ObjFW;
@import ObjFW_Bridge;
#import "ShowDetailsController.h"
#import "SiteStorage.h"
#import "PasswordGenerator.h"
#import "NewPasswordGenerator.h"
#import "LegacyPasswordGenerator.h"
@interface ShowDetailsController ()
- (void)_generateWithCallback: (void(^)(NSMutableString*))block;
- (void)_generateAndCopy;
- (void)_generateAndShow;
@end
static void
clearNSMutableString(NSMutableString *string)
{
/*
* NSMutableString does not offer a way to zero the string.
* This is in the hope that setting a single character at an index just
* replaces that character in memory, and thus allows us to zero the
* password.
*/
for (NSUInteger i = 0 ; i < string.length; i++)
[string replaceCharactersInRange: NSMakeRange(i, 1)
withString: @" "];
}
@implementation ShowDetailsController
- (void)dealloc
{
[_name release];
[_nameField release];
[_lengthField release];
[_legacySwitch release];
[_passphraseField release];
[super dealloc];
}
- (void)viewWillAppear: (BOOL)animated
{
SiteStorage *siteStorage = self.mainViewController.siteStorage;
NSIndexPath *indexPath =
self.mainViewController.tableView.indexPathForSelectedRow;
[_name release];
_name = [siteStorage.sites[indexPath.row] retain];
_length = [siteStorage lengthForSite: _name];
_legacy = [siteStorage isSiteLegacy: _name];
self.nameField.text = [_name NSObject];
self.lengthField.text = [NSString stringWithFormat: @"%zu", _length];
self.legacySwitch.on = _legacy;
[self.mainViewController.tableView deselectRowAtIndexPath: indexPath
animated: YES];
}
- (void)viewDidAppear: (BOOL)animated
{
[self.passphraseField becomeFirstResponder];
}
- (BOOL)textFieldShouldReturn: (UITextField*)textField
{
[textField resignFirstResponder];
return NO;
}
- (void)tableView: (UITableView*)tableView
didSelectRowAtIndexPath: (NSIndexPath*)indexPath
{
[self.passphraseField resignFirstResponder];
[tableView deselectRowAtIndexPath: indexPath
animated: YES];
if (indexPath.section == 3) {
switch (indexPath.row) {
case 0:
[self _generateAndCopy];
break;
case 1:
[self _generateAndShow];
break;
}
}
}
- (void)_generateAndCopy
{
[self _generateWithCallback: ^ (NSMutableString *password) {
UIPasteboard *pasteBoard = [UIPasteboard generalPasteboard];
pasteBoard.string = password;
clearNSMutableString(password);
UIAlertController *alert = [UIAlertController
alertControllerWithTitle: @"Password Generated"
message: @"The password has been copied "
@"into the clipboard."
preferredStyle: UIAlertControllerStyleAlert];
[alert addAction:
[UIAlertAction actionWithTitle: @"OK"
style: UIAlertActionStyleDefault
handler: ^ (UIAlertAction *action) {
[self.navigationController popViewControllerAnimated: YES];
}]];
[self presentViewController: alert
animated: YES
completion: nil];
}];
}
- (void)_generateAndShow
{
[self _generateWithCallback: ^ (NSMutableString *password) {
UIAlertController *alert = [UIAlertController
alertControllerWithTitle: @"Generated Passphrase"
message: password
preferredStyle: UIAlertControllerStyleAlert];
[alert addAction:
[UIAlertAction actionWithTitle: @"OK"
style: UIAlertActionStyleDefault
handler: ^ (UIAlertAction *action) {
[self.navigationController
popViewControllerAnimated: YES];
}]];
[self presentViewController: alert
animated: YES
completion: ^ {
clearNSMutableString(password);
}];
}];
}
- (void)_generateWithCallback: (void(^)(NSMutableString*))block
{
UIStoryboard *mainStoryboard =
[UIStoryboard storyboardWithName: @"Main"
bundle: nil];
UIViewController *activityController = [mainStoryboard
instantiateViewControllerWithIdentifier: @"activityIndicator"];
[self.navigationController.view addSubview: activityController.view];
dispatch_async(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
id <PasswordGenerator> generator;
char *passphrase;
if (_legacy)
generator = [LegacyPasswordGenerator generator];
else
generator = [NewPasswordGenerator generator];
generator.site = _name;
generator.length = _length;
passphrase = of_strdup([self.passphraseField.text UTF8String]);
@try {
self.passphraseField.text = @"";
generator.passphrase = passphrase;
[generator derivePassword];
} @finally {
of_explicit_memset(passphrase, 0, strlen(passphrase));
free(passphrase);
}
NSMutableString *password = [NSMutableString
stringWithUTF8String: (char*)generator.output];
of_explicit_memset(generator.output, 0,
strlen((char*)generator.output));
activityController.view.hidden = YES;
block(password);
});
}
- (IBAction)remove: (id)sender
{
UIAlertController *alert = [UIAlertController
alertControllerWithTitle: @"Remove Site?"
message: @"Do you want to remove this site?"
preferredStyle: UIAlertControllerStyleAlert];
[alert addAction:
[UIAlertAction actionWithTitle: @"No"
style: UIAlertActionStyleCancel
handler: nil]];
[alert addAction:
[UIAlertAction actionWithTitle: @"Yes"
style: UIAlertActionStyleDestructive
handler: ^ (UIAlertAction *action) {
[self.mainViewController.siteStorage removeSite: _name];
[self.mainViewController.tableView reloadData];
[self.navigationController popViewControllerAnimated: YES];
}]];
[self presentViewController: alert
animated: YES
completion: nil];
}
@end