iOS: Migrate to Swift

This is in preparation for moving to SwiftUI at some point.
This commit is contained in:
Jonathan Schleifer 2019-06-16 07:25:10 +02:00
parent e039b7d5f5
commit 83b21ee89e
No known key found for this signature in database
GPG key ID: 79D21189A2D4708D
25 changed files with 816 additions and 1266 deletions

View file

@ -1,27 +0,0 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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 UIKit;
@interface AboutController: UIViewController <UIWebViewDelegate>
@property (nonatomic, retain) IBOutlet UIWebView *webView;
@end

View file

@ -1,99 +0,0 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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 "AboutController.h"
static NSString *aboutHTMLTemplate =
@"<html>"
@"<head>"
@"<style type='text/css'>"
@"body {"
@" font-family: sans-serif;"
@"}"
@""
@"#title {"
@" font-size: 2.5em;"
@" font-weight: bold;"
@"}"
@""
@"#copyright {"
@" font-size: 0.9em;"
@" font-weight: bold;"
@"}"
@"</style>"
@"</head>"
@"<body>"
@"<div id='title'>"
@" scrypt-pwgen {version}"
@"</div>"
@"<div id='copyright'>"
@" Copyright © 2016 - 2019 Jonathan Schleifer"
@"</div>"
@"<p name='free_software'>"
@" scrypt-pwgen is free software and the source code is available at "
@" <a href='https://heap.zone/scrypt-pwgen/'>here</a>."
@"</p>"
@"<p name='objfw'>"
@" It makes use of the <a href='https://heap.zone/objfw/'>ObjFW</a> "
@"framework and also uses its scrypt implementation."
@"</p>"
@"</body>"
@"</html>";
@implementation AboutController
- (void)viewDidLoad
{
[super viewDidLoad];
self.automaticallyAdjustsScrollViewInsets = NO;
NSDictionary *infoDictionary = NSBundle.mainBundle.infoDictionary;
NSString *version = infoDictionary[@"CFBundleShortVersionString"];
NSString *aboutHTML = [aboutHTMLTemplate
stringByReplacingOccurrencesOfString: @"{version}"
withString: version];
[self.webView loadHTMLString: aboutHTML
baseURL: nil];
}
- (void)dealloc
{
[_webView release];
[super dealloc];
}
- (BOOL)webView: (UIWebView *)webView
shouldStartLoadWithRequest: (NSURLRequest *)request
navigationType: (UIWebViewNavigationType)navigationType
{
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
[UIApplication.sharedApplication openURL: request.URL
options: @{}
completionHandler: ^ (BOOL success) {
}];
return NO;
}
return YES;
}
@end

84
iOS/AboutController.swift Normal file
View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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 UIKit
class AboutController: UIViewController, UIWebViewDelegate {
@IBOutlet var webView: UIWebView?
override func viewDidLoad() {
super.viewDidLoad()
self.automaticallyAdjustsScrollViewInsets = false
let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"]
webView?.loadHTMLString(
"<html>" +
"<head>" +
"<style type='text/css'>" +
"body {" +
" font-family: sans-serif;" +
"}" +
"" +
"#title {" +
" font-size: 2.5em;" +
" font-weight: bold;" +
"}" +
"" +
"#copyright {" +
" font-size: 0.9em;" +
" font-weight: bold;" +
"}" +
"</style>" +
"</head>" +
"<body>" +
"<div id='title'>" +
" scrypt-pwgen \(version ?? "")" +
"</div>" +
"<div id='copyright'>" +
" Copyright © 2016 - 2019 Jonathan Schleifer" +
"</div>" +
"<p name='free_software'>" +
" scrypt-pwgen is free software and the source code is available" +
" at <a href='https://heap.zone/scrypt-pwgen/'>here</a>." +
"</p>" +
"<p name='objfw'>" +
" It makes use of the" +
" <a href='https://heap.zone/objfw/'>ObjFW</a> framework and" +
" also uses its scrypt implementation." +
"</p>" +
"</body>" +
"</html>", baseURL: nil)
}
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest,
navigationType: UIWebView.NavigationType) -> Bool {
if #available(iOS 10.0, *),
navigationType == UIWebView.NavigationType.linkClicked,
let url = request.url {
UIApplication.shared.open(url)
return false
}
return true
}
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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 UIKit;
#import "MainViewController.h"
@interface AddSiteController: UITableViewController <UITableViewDelegate>
@property (nonatomic, retain) IBOutlet UITextField *nameField;
@property (nonatomic, retain) IBOutlet UITextField *lengthField;
@property (nonatomic, retain) IBOutlet UISwitch *legacySwitch;
@property (nonatomic, copy) NSString *keyFile;
@property (nonatomic, retain) IBOutlet UILabel *keyFileLabel;
@property (retain) MainViewController *mainViewController;
- (IBAction)done: (id)sender;
- (IBAction)cancel: (id)sender;
@end

View file

@ -1,131 +0,0 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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_Bridge;
#import "AddSiteController.h"
#import "SelectKeyFileController.h"
static void
showAlert(UIViewController *controller, NSString *title, NSString *message)
{
UIAlertController *alert = [UIAlertController
alertControllerWithTitle: title
message: message
preferredStyle: UIAlertControllerStyleAlert];
[alert addAction:
[UIAlertAction actionWithTitle: @"OK"
style: UIAlertActionStyleDefault
handler: nil]];
[controller presentViewController: alert
animated: YES
completion: nil];
}
@implementation AddSiteController
- (void)dealloc
{
[_nameField release];
[_lengthField release];
[_legacySwitch release];
[_keyFile release];
[_keyFileLabel release];
[_mainViewController release];
[super dealloc];
}
- (void)tableView: (UITableView *)tableView
didSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath: indexPath
animated: YES];
if (indexPath.section == 1 && indexPath.row == 1)
[self performSegueWithIdentifier: @"selectKeyFile"
sender: self];
}
- (NSIndexPath *)tableView: (UITableView *)tableView
willSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
if (indexPath.section == 1 && indexPath.row == 1)
return indexPath;
return nil;
}
- (IBAction)done: (id)sender
{
OFString *name = self.nameField.text.OFObject;
OFString *lengthString = self.lengthField.text.OFObject;
bool lengthValid = true;
size_t length;
if (name.length == 0) {
showAlert(self, @"Name missing", @"Please enter a name.");
return;
}
@try {
length = (size_t)lengthString.decimalValue;
if (length < 3 || length > 64)
lengthValid = false;
} @catch (OFInvalidFormatException *e) {
lengthValid = false;
}
if (!lengthValid) {
showAlert(self, @"Invalid length",
@"Please enter a number between 3 and 64.");
return;
}
if ([self.mainViewController.siteStorage hasSite: name]) {
showAlert(self, @"Site Already Exists",
@"Please pick a name that does not exist yet.");
return;
}
[self.mainViewController.siteStorage setSite: name
length: length
legacy: self.legacySwitch.on
keyFile: self.keyFile.OFObject];
[self.mainViewController reset];
[self.navigationController popViewControllerAnimated: YES];
}
- (IBAction)cancel: (id)sender
{
[self.navigationController popViewControllerAnimated: YES];
}
- (void)prepareForSegue: (UIStoryboardSegue *)segue
sender: (id)sender
{
if ([segue.identifier isEqual: @"selectKeyFile"])
[segue.destinationViewController setAddSiteController: self];
}
@end

118
iOS/AddSiteController.swift Normal file
View file

@ -0,0 +1,118 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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 UIKit
import ObjFW
import ObjFW_Bridge
class AddSiteController: UITableViewController {
@IBOutlet var nameField: UITextField?
@IBOutlet var lengthField: UITextField?
@IBOutlet var legacySwitch: UISwitch?
public var keyFile: String?
@IBOutlet var keyFileLabel: UILabel?
public var mainViewController: MainViewController?
override func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if indexPath.section == 1 && indexPath.row == 1 {
self.performSegue(withIdentifier: "selectKeyFile", sender: self)
}
}
override func tableView(
_ tableView: UITableView,
willSelectRowAt indexPath: IndexPath
) -> IndexPath? {
if indexPath.section == 1 && indexPath.row == 1 {
return indexPath
}
return nil
}
private func showAlert(controller: UIViewController, title: String,
message: String) {
let alert = UIAlertController(title: title, message: message,
preferredStyle: .alert)
alert.addAction(
UIAlertAction(title: "OK", style: .default, handler: nil))
controller.present(alert, animated: true, completion: nil)
}
@IBAction func done(_ sender: Any) {
guard let name = nameField?.text?.ofObject else { return }
guard let lengthString = lengthField?.text?.ofObject else { return }
guard name.length > 0 else {
showAlert(controller: self, title: "Name missing",
message: "Please enter a name.")
return
}
var lengthValid = true
var length: size_t = 0
OFException.try({
length = lengthString.decimalValue
if length < 3 || length > 64 {
lengthValid = false
}
}, catch: { (OFException) in
lengthValid = false
})
guard lengthValid else {
showAlert(controller: self, title: "Invalid length",
message: "Please enter a number between 3 and 64.")
return
}
guard let siteStorage = mainViewController?.siteStorage else { return }
guard !siteStorage.hasSite(name) else {
showAlert(controller: self, title: "Site Already Exists",
message: "Please pick a name that does not exist yet.")
return
}
let keyFile = self.keyFile?.ofObject
siteStorage.setSite(name, length: length,
legacy: legacySwitch?.isOn ?? false,
keyFile: keyFile)
mainViewController?.reset()
navigationController?.popViewController(animated: true)
}
@IBAction func cancel(_ sender: Any) {
navigationController?.popViewController(animated: true)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "selectKeyFile" {
let controller = segue.destination as? SelectKeyFileController
controller?.addSiteController = self
}
}
}

View file

@ -1,27 +0,0 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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 UIKit;
@interface AppDelegate: UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@end

View file

@ -20,13 +20,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#import "AppDelegate.h"
import UIKit
@implementation AppDelegate
- (void)dealloc
{
[_window release];
[super dealloc];
@objc class AppDelegate: UIResponder, UIApplicationDelegate {
public var window: UIWindow?
}
@end

View file

@ -1,12 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BN3-Y7-zvx">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BN3-Y7-zvx">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@ -32,7 +31,7 @@
<!--Sites-->
<scene sceneID="raj-gx-00d">
<objects>
<viewController id="P19-6i-fpd" customClass="MainViewController" sceneMemberID="viewController">
<viewController id="P19-6i-fpd" customClass="MainViewController" customModule="scrypt_pwgen" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="XVy-9K-Bul"/>
<viewControllerLayoutGuide type="bottom" id="TZK-mv-9Bn"/>
@ -93,7 +92,7 @@
<!--About-->
<scene sceneID="rga-fS-ski">
<objects>
<viewController title="About" id="MZ3-iZ-Dsf" customClass="AboutController" sceneMemberID="viewController">
<viewController title="About" id="MZ3-iZ-Dsf" customClass="AboutController" customModule="scrypt_pwgen" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="8k0-QJ-gsC"/>
<viewControllerLayoutGuide type="bottom" id="NSX-G5-c03"/>
@ -130,7 +129,7 @@
<!--Add Site Controller-->
<scene sceneID="IxZ-dn-p6h">
<objects>
<tableViewController id="mTn-Td-fIF" customClass="AddSiteController" sceneMemberID="viewController">
<tableViewController id="mTn-Td-fIF" customClass="AddSiteController" customModule="scrypt_pwgen" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="cum-L6-K1B">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@ -242,7 +241,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Key file" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Uyw-CW-atj">
<rect key="frame" x="16" y="12" width="100" height="21"/>
<rect key="frame" x="16" y="11.5" width="100" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="100" id="5d9-re-aJA"/>
</constraints>
@ -306,7 +305,7 @@
<!--Select Key File Controller-->
<scene sceneID="fKq-Aa-JNA">
<objects>
<tableViewController id="4bs-rP-TxE" customClass="SelectKeyFileController" sceneMemberID="viewController">
<tableViewController id="4bs-rP-TxE" customClass="SelectKeyFileController" customModule="scrypt_pwgen" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="SYH-wm-J8Z">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@ -344,7 +343,7 @@
<!--Show Details Controller-->
<scene sceneID="rn9-fJ-mg7">
<objects>
<tableViewController id="ayJ-fs-aIU" customClass="ShowDetailsController" sceneMemberID="viewController">
<tableViewController id="ayJ-fs-aIU" customClass="ShowDetailsController" customModule="scrypt_pwgen" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="vWS-Yc-qQ5">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>

View file

@ -20,13 +20,12 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#import "HTTPServerDelegate.h"
import Foundation
import ObjFW
@implementation HTTPServerDelegate
- (void)server: (OFHTTPServer *)server
didReceiveRequest: (OFHTTPRequest *)request
response: (OFHTTPResponse *)response
{
of_log(@"%@", request);
class HTTPServerDelegate: OFObject, OFHTTPServerDelegate {
func server(_ server: OFHTTPServer, didReceive request: OFHTTPRequest,
requestBody: OFStream?, response: OFHTTPResponse) {
print("\(request)")
}
}
@end

View file

@ -1,35 +0,0 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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 UIKit;
#import "SiteStorage.h"
@interface MainViewController: UIViewController <UISearchBarDelegate,
UITableViewDelegate, UITableViewDataSource>
@property (retain) OFArray<OFString *> *sites;
@property (retain) SiteStorage *siteStorage;
@property (nonatomic, retain) IBOutlet UISearchBar *searchBar;
@property (nonatomic, retain) IBOutlet UITableView *tableView;
- (void)reset;
@end

View file

@ -1,103 +0,0 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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_Bridge;
#import "MainViewController.h"
#import "AddSiteController.h"
#import "ShowDetailsController.h"
@implementation MainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_siteStorage = [[SiteStorage alloc] init];
[self reset];
}
- (void)dealloc
{
[_sites release];
[_siteStorage release];
[_searchBar release];
[_tableView release];
[super dealloc];
}
- (void)reset
{
void *pool = objc_autoreleasePoolPush();
_searchBar.text = @"";
self.sites = [_siteStorage sitesWithFilter: nil];
[_tableView reloadData];
objc_autoreleasePoolPop(pool);
}
- (NSInteger)tableView: (UITableView *)tableView
numberOfRowsInSection: (NSInteger)section
{
return self.sites.count;
}
- (UITableViewCell *)tableView: (UITableView *)tableView
cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier: @"site"];
if (cell == nil)
cell = [[[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleDefault
reuseIdentifier: @"site"] autorelease];
cell.textLabel.text = self.sites[indexPath.row].NSObject;
return cell;
}
- (void)searchBar: (UISearchBar *)searchBar
textDidChange: (NSString *)searchText
{
self.sites = [_siteStorage sitesWithFilter: _searchBar.text.OFObject];
[_tableView reloadData];
}
- (void)tableView: (UITableView *)tableView
didSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
[self performSegueWithIdentifier: @"showDetails"
sender: self];
}
- (void)prepareForSegue: (UIStoryboardSegue *)segue
sender: (id)sender
{
if ([segue.identifier isEqual: @"addSite"] ||
[segue.identifier isEqual: @"showDetails"])
[segue.destinationViewController setMainViewController: self];
}
@end

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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 UIKit
import ObjFW
class MainViewController: UIViewController, UISearchBarDelegate,
UITableViewDelegate, UITableViewDataSource {
public var sites = OFArray<OFString>()
public var siteStorage = SiteStorage()
@IBOutlet var searchBar: UISearchBar?
@IBOutlet var tableView: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
self.reset()
}
func reset() {
searchBar?.text = ""
sites = siteStorage.sites(withFilter: nil)
tableView?.reloadData()
}
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return sites.count
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "site") ??
UITableViewCell(style: .default, reuseIdentifier: "site")
cell.textLabel?.text = sites[indexPath.row].nsObject
return cell
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
sites = siteStorage.sites(withFilter: searchBar.text?.ofObject)
tableView?.reloadData()
}
func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "showDetails", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.identifier {
case .some("addSite"):
let destination = segue.destination as? AddSiteController
destination?.mainViewController = self
case .some("showDetails"):
let destination = segue.destination as? ShowDetailsController
destination?.mainViewController = self
default:
break
}
}
}

View file

@ -1,41 +0,0 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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 UIKit;
#import "AddSiteController.h"
#import "HTTPServerDelegate.h"
@interface SelectKeyFileController: UITableViewController <UITableViewDelegate,
UITableViewDataSource>
{
NSArray<NSString *> *_keyFiles;
OFHTTPServer *_HTTPServer;
HTTPServerDelegate *_HTTPServerDelegate;
OFThread *_HTTPServerThread;
}
@property (retain) AddSiteController *addSiteController;
- (IBAction)upload: (id)sender;
@end

View file

@ -1,152 +0,0 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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_Bridge;
#import "HTTPServerDelegate.h"
#import "SelectKeyFileController.h"
@implementation SelectKeyFileController
- (void)viewDidLoad
{
NSString *documentDirectory;
NSArray<NSString *> *keyFiles;
NSError *error;
[super viewDidLoad];
if ((documentDirectory = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES).firstObject) == nil) {
NSLog(@"Could not get key files: No documents directory");
[self.navigationController popViewControllerAnimated: YES];
return;
}
keyFiles = [NSFileManager.defaultManager
contentsOfDirectoryAtPath: documentDirectory
error: &error];
if (keyFiles == nil) {
NSLog(@"Could not get key files: %@", error);
[self.navigationController popViewControllerAnimated: YES];
return;
}
_keyFiles = [[keyFiles sortedArrayUsingSelector:
@selector(compare:)] retain];
_HTTPServer = [[OFHTTPServer alloc] init];
@autoreleasepool {
_HTTPServer.host = @"127.0.0.1".OFObject;
}
_HTTPServerDelegate = [[HTTPServerDelegate alloc] init];
_HTTPServer.delegate = _HTTPServerDelegate;
_HTTPServerThread = [[OFThread alloc] init];
[_HTTPServerThread start];
}
- (void)dealloc
{
[_keyFiles release];
[_HTTPServerThread.runLoop stop];
[_HTTPServerThread join];
[_HTTPServerThread release];
[_HTTPServer release];
[_HTTPServerDelegate release];
[super dealloc];
}
- (NSInteger)tableView: (UITableView *)tableView
numberOfRowsInSection: (NSInteger)section
{
return _keyFiles.count + 1;
}
- (UITableViewCell *)tableView: (UITableView *)tableView
cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier: @"keyFile"];
if (cell == nil)
cell = [[[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleDefault
reuseIdentifier: @"keyFile"] autorelease];
cell.textLabel.text =
(indexPath.row > 0 ? _keyFiles[indexPath.row - 1] : @"None");
return cell;
}
- (void)tableView: (UITableView *)tableView
didSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
self.addSiteController.keyFile =
(indexPath.row > 0 ? _keyFiles[indexPath.row - 1] : nil);
self.addSiteController.keyFileLabel.text =
(indexPath.row > 0 ? _keyFiles[indexPath.row - 1] : @"None");
[self.navigationController popViewControllerAnimated: YES];
}
- (void)upload: (id)sender
{
[_HTTPServerThread.runLoop addTimer: [OFTimer
scheduledTimerWithTimeInterval: 0
repeats: false
block: ^ (OFTimer *timer) {
NSString *message;
UIAlertController *alert;
_HTTPServer.port = 0;
[_HTTPServer start];
message = [NSString stringWithFormat:
@"Navigate to http://%@:%u/ with your browser.\n\n"
@"Press OK when done.",
_HTTPServer.host.NSObject, _HTTPServer.port];
alert = [UIAlertController
alertControllerWithTitle: @"Server Running"
message: message
preferredStyle: UIAlertControllerStyleAlert];
[alert addAction:
[UIAlertAction actionWithTitle: @"OK"
style: UIAlertActionStyleDefault
handler: nil]];
dispatch_sync(dispatch_get_main_queue(), ^ {
[self presentViewController: alert
animated: YES
completion: ^ {
[_HTTPServer stop];
}];
});
}]];
}
@end

View file

@ -0,0 +1,124 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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 UIKit
class SelectKeyFileController: UITableViewController {
public var addSiteController: AddSiteController?
private var keyFiles: [String] = []
private var httpServer: OFHTTPServer
private var httpServerDelegate: HTTPServerDelegate
private var httpServerThread: OFThread
required init?(coder aDecoder: NSCoder) {
httpServer = OFHTTPServer()
httpServer.host = "127.0.0.1".ofObject
httpServerDelegate = HTTPServerDelegate()
httpServer.delegate = self.httpServerDelegate
httpServerThread = OFThread()
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
guard let documentDirectory = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true).first
else {
print("Could not get key files: No documents directory")
navigationController?.popViewController(animated: true)
return
}
do {
keyFiles = try FileManager.default.contentsOfDirectory(
atPath: documentDirectory).sorted()
} catch let error as NSError {
print("Could not get key files: \(error)")
navigationController?.popViewController(animated: true)
return
}
httpServerThread.start()
}
deinit {
httpServerThread.runLoop.stop()
httpServerThread.join()
}
override func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return keyFiles.count + 1
}
override func tableView(
_ tableView: UITableView,
cellForRowAt indexPath: IndexPath
) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "keyFile") ??
UITableViewCell(style: .default, reuseIdentifier: "keyFile")
cell.textLabel?.text =
indexPath.row > 0 ? keyFiles[indexPath.row - 1] : "None"
return cell
}
override func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
addSiteController?.keyFile =
indexPath.row > 0 ? keyFiles[indexPath.row - 1] : nil
addSiteController?.keyFileLabel?.text =
indexPath.row > 0 ? keyFiles[indexPath.row - 1] : "None"
self.navigationController?.popViewController(animated: true)
}
@IBAction func upload(_ sender: Any?) {
let timer = OFTimer.scheduledTimer(withTimeInterval: 0,
repeats: false) { (OFTimer) in
self.httpServer.port = 0
self.httpServer.start()
let message =
"Navigate to http://\(self.httpServer.host!.nsObject):" +
"\(self.httpServer.port)/ in your browser.\n\n" +
"Press OK when done."
let alert = UIAlertController(title: "Server Running",
message: message,
preferredStyle: .alert)
alert.addAction(
UIAlertAction(title: "OK", style: .default, handler: nil))
DispatchQueue.main.sync {
self.present(alert, animated: true) {
self.httpServer.stop()
}
}
}
httpServerThread.runLoop.add(timer)
}
}

View file

@ -1,44 +0,0 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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 UIKit;
#import "MainViewController.h"
@interface ShowDetailsController: UITableViewController <UITableViewDelegate,
UITextFieldDelegate>
{
OFString *_name;
size_t _length;
bool _legacy;
OFString *_keyFile;
}
@property (retain, nonatomic) IBOutlet UITextField *nameField;
@property (retain, nonatomic) IBOutlet UITextField *lengthField;
@property (retain, nonatomic) IBOutlet UISwitch *legacySwitch;
@property (retain, nonatomic) IBOutlet UITextField *keyFileField;
@property (retain, nonatomic) IBOutlet UITextField *passphraseField;
@property (retain) MainViewController *mainViewController;
- (IBAction)remove: (id)sender;
@end

View file

@ -1,260 +0,0 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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_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];
[_keyFile release];
[_keyFileField release];
[_passphraseField release];
[_mainViewController release];
[super dealloc];
}
- (void)viewWillAppear: (BOOL)animated
{
SiteStorage *siteStorage;
NSIndexPath *indexPath;
[super viewWillAppear: animated];
siteStorage = self.mainViewController.siteStorage;
indexPath = self.mainViewController.tableView.indexPathForSelectedRow;
[_name release];
_name = [self.mainViewController.sites[indexPath.row] copy];
_length = [siteStorage lengthForSite: _name];
_legacy = [siteStorage isSiteLegacy: _name];
_keyFile = [[siteStorage keyFileForSite: _name] copy];
self.nameField.text = _name.NSObject;
self.lengthField.text = [NSString stringWithFormat: @"%zu", _length];
self.legacySwitch.on = _legacy;
self.keyFileField.text = _keyFile.NSObject;
[self.mainViewController.tableView deselectRowAtIndexPath: indexPath
animated: YES];
}
- (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
{
id <PasswordGenerator> generator;
char *passphrase;
UIStoryboard *mainStoryboard;
UIViewController *activityController;
if (_legacy)
generator = [LegacyPasswordGenerator generator];
else
generator = [NewPasswordGenerator generator];
generator.site = _name;
generator.length = _length;
if (_keyFile != nil) {
NSString *documentDirectory;
OFString *keyFilePath;
if ((documentDirectory = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES).firstObject) ==
nil) {
NSLog(@"Could not get key files: No documents "
@"directory");
return;
}
keyFilePath = [documentDirectory.OFObject
stringByAppendingPathComponent: _keyFile];
generator.keyFile = [OFMutableData
dataWithContentsOfFile: keyFilePath];
}
passphrase = of_strdup(self.passphraseField.text.UTF8String);
generator.passphrase = passphrase;
mainStoryboard = [UIStoryboard storyboardWithName: @"Main"
bundle: nil];
activityController = [mainStoryboard
instantiateViewControllerWithIdentifier: @"activityIndicator"];
[self.navigationController.view addSubview: activityController.view];
dispatch_async(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
@try {
[generator derivePassword];
} @finally {
if (generator.keyFile != nil)
of_explicit_memset(
(void *)generator.keyFile.items, 0,
generator.keyFile.count);
of_explicit_memset(passphrase, 0, strlen(passphrase));
free(passphrase);
}
NSMutableString *password = [[[NSMutableString alloc]
initWithBytes: (char *)generator.output
length: generator.length
encoding: NSUTF8StringEncoding] autorelease];
of_explicit_memset(generator.output, 0, generator.length);
dispatch_sync(dispatch_get_main_queue(), ^ {
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 reset];
[self.navigationController popViewControllerAnimated: YES];
}]];
[self presentViewController: alert
animated: YES
completion: nil];
}
@end

View file

@ -0,0 +1,203 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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 UIKit
import ObjFW
import ObjFW_Bridge
class ShowDetailsController: UITableViewController, UITextFieldDelegate {
@IBOutlet var nameField: UITextField?
@IBOutlet var lengthField: UITextField?
@IBOutlet var legacySwitch: UISwitch?
@IBOutlet var keyFileField: UITextField?
@IBOutlet var passphraseField: UITextField?
public var mainViewController: MainViewController?
private var name: OFString = "".ofObject
private var length: size_t = 0
private var isLegacy: Bool = false
private var keyFile: OFString? = nil
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let mainViewController = self.mainViewController else { return }
guard let tableView = mainViewController.tableView else { return }
let siteStorage = mainViewController.siteStorage
guard let indexPath = tableView.indexPathForSelectedRow else { return }
name = mainViewController.sites[indexPath.row]
length = siteStorage.length(forSite: name)
isLegacy = siteStorage.isLegacy(site: name)
keyFile = siteStorage.keyFile(forSite: name)
nameField?.text = name.nsObject
lengthField?.text = "\(length)"
legacySwitch?.isOn = isLegacy
keyFileField?.text = keyFile?.nsObject
tableView.deselectRow(at: indexPath, animated: true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return false
}
static private func clearNSMutableString(_ string: NSMutableString) {
/*
* 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 i in 0..<string.length {
string.replaceCharacters(in: NSRange(location: i, length: 1),
with: " ")
}
}
override func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
passphraseField?.resignFirstResponder()
tableView.deselectRow(at: indexPath, animated: true)
if indexPath.section == 3 {
switch indexPath.row {
case 0:
self.generateAndCopy()
case 1:
self.generateAndShow()
default:
break
}
}
}
private func generateAndCopy() {
self.generateWithCallback { (password: NSMutableString) in
let pasteboard = UIPasteboard.general
pasteboard.string = password as String
ShowDetailsController.clearNSMutableString(password)
let message = "The password has been copied into the clipboard."
let alert = UIAlertController(title: "Password Generated",
message: message,
preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default) {
(UIAlertAction) in
self.navigationController?.popViewController(animated: true)
}
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
}
private func generateAndShow() {
self.generateWithCallback { (password: NSMutableString) in
let alert = UIAlertController(title: "Generated Passphrase",
message: password as String,
preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default) {
(UIAlertAction) in
self.navigationController?.popViewController(animated: true)
}
alert.addAction(action)
self.present(alert, animated: true) {
ShowDetailsController.clearNSMutableString(password)
}
}
}
private func generateWithCallback(_ block: (_: NSMutableString) -> ()) {
let generator: PasswordGenerator = isLegacy ?
LegacyPasswordGenerator() : NewPasswordGenerator()
generator.site = name
generator.length = length
if let keyFile = keyFile {
guard let documentDirectory = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true).first
else {
print("Could not get key files: No documents directory")
return
}
let keyFilePath = documentDirectory.ofObject.appending(keyFile)
generator.keyFile = OFMutableData(contentsOfFile: keyFilePath)
}
let passphraseText = (passphraseField?.text ?? "") as NSString
let passphrase = of_strdup(passphraseText.utf8String!)!
generator.passphrase = UnsafePointer<CChar>(passphrase)
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let activityController = mainStoryboard.instantiateViewController(
withIdentifier: "activityIndicator")
navigationController?.view.addSubview(activityController.view)
DispatchQueue.global(qos: .default).async {
OFException.try({
generator.derivePassword()
}, finally: {
if let keyFile = generator.keyFile as? OFMutableData {
of_explicit_memset(keyFile.mutableItems, 0, keyFile.count)
}
of_explicit_memset(passphrase, 0, strlen(passphrase))
free(passphrase)
})
}
let password = NSMutableString(bytes: generator.output,
length: generator.length,
encoding: String.Encoding.utf8.rawValue)!
of_explicit_memset(generator.output, 0, generator.length)
DispatchQueue.main.sync {
activityController.view.isHidden = true
block(password)
}
}
@IBAction func remove(_ sender: Any?) {
let message = "Do you want to remove this site?"
let alert = UIAlertController(title: "Remove Site?",
message: message,
preferredStyle: .alert)
alert.addAction(
UIAlertAction(title: "No", style: .cancel, handler: nil))
let yesAction = UIAlertAction(title: "Yes", style: .destructive) {
(UIAlertAction) in
self.mainViewController?.siteStorage.removeSite(self.name)
self.mainViewController?.reset()
self.navigationController?.popViewController(animated: true)
}
alert.addAction(yesAction)
self.present(alert, animated: true, completion: nil)
}
}

View file

@ -1,43 +0,0 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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;
@interface SiteStorage: OFObject
{
OFString *_path;
OFMutableDictionary<OFString *, OFDictionary<OFNumber *, id> *>
*_storage;
OFArray *_sites;
}
- (OFArray<OFString *> *)sitesWithFilter: (OFString *)filter;
- (bool)hasSite: (OFString *)name;
- (size_t)lengthForSite: (OFString *)name;
- (bool)isSiteLegacy: (OFString *)name;
- (OFString *)keyFileForSite: (OFString *)name;
- (void)setSite: (OFString *)site
length: (size_t)length
legacy: (bool)legacy
keyFile: (OFString *)keyFile;
- (void)removeSite: (OFString *)name;
@end

View file

@ -1,185 +0,0 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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 "SiteStorage.h"
@interface SiteStorage ()
- (void)_update;
@end
static OFNumber *lengthField, *legacyField, *keyFileField;
@implementation SiteStorage
+ (void)initialize
{
lengthField = [[OFNumber alloc] initWithUInt8: 0];
legacyField = [[OFNumber alloc] initWithUInt8: 1];
keyFileField = [[OFNumber alloc] initWithUInt8: 2];
}
- (instancetype)init
{
self = [super init];
@try {
@autoreleasepool {
OFFileManager *fileManager =
OFFileManager.defaultManager;
OFString *userDataPath = OFSystemInfo.userDataPath;
if (![fileManager directoryExistsAtPath: userDataPath])
[fileManager
createDirectoryAtPath: userDataPath];
_path = [[userDataPath stringByAppendingPathComponent:
@"sites.msgpack"] copy];
@try {
_storage = [[OFData dataWithContentsOfFile:
_path].messagePackValue mutableCopy];
} @catch (id e) {
_storage = [[OFMutableDictionary alloc] init];
}
_sites = [_storage.allKeys.sortedArray retain];
}
} @catch (id e) {
[self release];
@throw e;
}
return self;
}
- (void)dealloc
{
[_path release];
[_storage release];
[_sites release];
[super dealloc];
}
- (OFArray<OFString *> *)sitesWithFilter: (OFString *)filter
{
OFArray<OFString *> *sites;
@autoreleasepool {
/*
* FIXME: We need case folding here, but there is no method for
* it yet.
*/
filter = filter.lowercaseString;
sites = [_storage.allKeys.sortedArray
filteredArrayUsingBlock: ^ (OFString *name, size_t index) {
if (filter == nil)
return true;
return [name.lowercaseString containsString: filter];
}];
[sites retain];
}
return [sites autorelease];
}
- (bool)hasSite: (OFString *)name
{
return (_storage[name] != nil);
}
- (size_t)lengthForSite: (OFString *)name
{
OFDictionary<OFNumber *, id> *site = _storage[name];
if (site == nil)
@throw [OFInvalidArgumentException exception];
return [site[lengthField] sizeValue];
}
- (bool)isSiteLegacy: (OFString *)name
{
OFDictionary<OFNumber *, id> *site = _storage[name];
if (site == nil)
@throw [OFInvalidArgumentException exception];
return [site[legacyField] boolValue];
}
- (OFString *)keyFileForSite: (OFString *)name
{
OFDictionary<OFNumber *, id> *site = _storage[name];
OFString *keyFile;
if (site == nil)
@throw [OFInvalidArgumentException exception];
keyFile = site[keyFileField];
if ([keyFile isEqual: [OFNull null]])
return nil;
return keyFile;
}
- (void)setSite: (OFString *)site
length: (size_t)length
legacy: (bool)legacy
keyFile: (OFString *)keyFile
{
@autoreleasepool {
OFMutableDictionary *siteDictionary =
[OFMutableDictionary dictionary];
siteDictionary[lengthField] = [OFNumber numberWithSize: length];
siteDictionary[legacyField] = [OFNumber numberWithBool: legacy];
siteDictionary[keyFileField] = keyFile;
[siteDictionary makeImmutable];
_storage[site] = siteDictionary;
[self _update];
}
}
- (void)removeSite: (OFString *)name
{
[_storage removeObjectForKey: name];
[self _update];
}
- (void)_update
{
@autoreleasepool {
[_storage.messagePackRepresentation writeToFile: _path];
[_sites release];
_sites = [_storage.allKeys.sortedArray retain];
}
}
@end

138
iOS/SiteStorage.swift Normal file
View file

@ -0,0 +1,138 @@
/*
* Copyright (c) 2016 - 2019 Jonathan Schleifer <js@heap.zone>
*
* https://heap.zone/git/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
class SiteStorage: OFObject {
private typealias Storage =
OFMutableDictionary<OFString, OFDictionary<OFNumber, AnyObject>>
private static let lengthField = OFNumber(uInt8: 0)
private static let legacyField = OFNumber(uInt8: 1)
private static let keyFileField = OFNumber(uInt8: 2)
private var path: OFString
private var storage: Storage
private var sites: OFArray<OFString>
override init() {
let fileManager = OFFileManager.default
let userDataPath = OFSystemInfo.userDataPath!
if !fileManager.directoryExists(atPath: userDataPath) {
fileManager.createDirectory(atPath: userDataPath)
}
let path = userDataPath.appendingPathComponent(
OFString(utf8String: "sites.msgpack"))
var storage: Storage? = nil
OFException.try({
storage = OFData(contentsOfFile: path).messagePackValue as? Storage
}, catch: { (OFException) in
storage = OFMutableDictionary()
})
self.path = path
self.storage = storage!
self.sites = self.storage.allKeys.sorted
}
func sites(withFilter filter: OFString?) -> OFArray<OFString> {
// FIXME: We need case folding here, but there is no method for it yet.
let filter = filter?.lowercase
return storage.allKeys.sorted.filteredArray({
(name: Any, index: size_t) -> Bool in
if filter == nil {
return true
}
let name = name as! OFString
return name.lowercase.contains(filter!)
})
}
func hasSite(_ name: OFString) -> Bool {
return (storage[name] != nil)
}
func length(forSite name: OFString) -> size_t {
guard let site = storage[name] else {
OFInvalidArgumentException().throw()
abort()
}
return (site[SiteStorage.lengthField] as! OFNumber).sizeValue
}
func isLegacy(site name: OFString) -> Bool {
guard let site = storage[name] else {
OFInvalidArgumentException().throw()
abort()
}
return (site[SiteStorage.legacyField] as! OFNumber).boolValue
}
func keyFile(forSite name: OFString) -> OFString? {
guard let site = storage[name] else {
OFInvalidArgumentException().throw()
abort()
}
let keyFile = site[SiteStorage.keyFileField]
if keyFile is OFNull {
return nil
}
return keyFile as? OFString
}
func setSite(_ name: OFString, length: size_t, legacy: Bool,
keyFile: OFString?) {
let siteDictionary = OFMutableDictionary<OFNumber, AnyObject>()
siteDictionary.setObject(OFNumber(size: length),
forKey: SiteStorage.lengthField)
siteDictionary.setObject(OFNumber(bool: legacy),
forKey: SiteStorage.legacyField)
if keyFile != nil {
siteDictionary.setObject(keyFile!, forKey: SiteStorage.keyFileField)
}
siteDictionary.makeImmutable()
storage.setObject(siteDictionary, forKey: name)
self.update()
}
func removeSite(_ name: OFString) {
self.storage.removeObject(forKey: name)
self.update()
}
private func update() {
storage.messagePackRepresentation.write(toFile: path)
sites = storage.allKeys.sorted
}
}

View file

@ -20,7 +20,6 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
@import ObjFW;
@interface HTTPServerDelegate: OFObject <OFHTTPServerDelegate>
@end
#import "PasswordGenerator.h"
#import "NewPasswordGenerator.h"
#import "LegacyPasswordGenerator.h"

View file

@ -24,7 +24,7 @@
#import <ObjFW/ObjFW.h>
#import <ObjFW_Bridge/ObjFW_Bridge.h>
#import "AppDelegate.h"
#import "scrypt_pwgen-Swift.h"
@interface OFAppDelegate: OFObject <OFApplicationDelegate>
@end

View file

@ -7,26 +7,26 @@
objects = {
/* Begin PBXBuildFile section */
4B0719251DAA78D80065997A /* ShowDetailsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B0719241DAA78D80065997A /* ShowDetailsController.m */; };
4B2E52E11DA942840040D091 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E52E01DA942840040D091 /* main.m */; };
4B2E52E41DA942840040D091 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E52E31DA942840040D091 /* AppDelegate.m */; };
4B2E52E71DA942840040D091 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E52E61DA942840040D091 /* MainViewController.m */; };
4B2E52EA1DA942840040D091 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4B2E52E81DA942840040D091 /* Main.storyboard */; };
4B2E52EC1DA942840040D091 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4B2E52EB1DA942840040D091 /* Assets.xcassets */; };
4B2E52EF1DA942840040D091 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4B2E52ED1DA942840040D091 /* LaunchScreen.storyboard */; };
4B82D1151DAAAFCE00F32B2F /* AboutController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B82D1141DAAAFCE00F32B2F /* AboutController.m */; };
4B31D80922B58F0F00494B15 /* SiteStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B31D80822B58F0F00494B15 /* SiteStorage.swift */; };
4B5BCEF922B5B94C00E551BD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5BCEF822B5B94C00E551BD /* AppDelegate.swift */; };
4B5BCEFB22B5CF3200E551BD /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5BCEFA22B5CF3200E551BD /* MainViewController.swift */; };
4B5BCEFD22B5D98800E551BD /* SelectKeyFileController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5BCEFC22B5D98800E551BD /* SelectKeyFileController.swift */; };
4B5BCEFF22B5E36900E551BD /* ShowDetailsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5BCEFE22B5E36900E551BD /* ShowDetailsController.swift */; };
4B93656E22B5ADA00099DD08 /* HTTPServerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B93656D22B5ADA00099DD08 /* HTTPServerDelegate.swift */; };
4B93657022B5AE2C0099DD08 /* AboutController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B93656F22B5AE2C0099DD08 /* AboutController.swift */; };
4B93657222B5B1FB0099DD08 /* AddSiteController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B93657122B5B1FB0099DD08 /* AddSiteController.swift */; };
4B9525251F96BB900095F259 /* ObjFW.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B9525231F96BB820095F259 /* ObjFW.framework */; };
4B9525261F96BB900095F259 /* ObjFW_Bridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B9525241F96BB820095F259 /* ObjFW_Bridge.framework */; };
4B9525291F994CD30095F259 /* ObjFW.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4B9525231F96BB820095F259 /* ObjFW.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
4B95252A1F9953350095F259 /* ObjFW_Bridge.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4B9525241F96BB820095F259 /* ObjFW_Bridge.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
4B9EB1152008167200EB66F2 /* HTTPServerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B9EB1142008167200EB66F2 /* HTTPServerDelegate.m */; settings = {COMPILER_FLAGS = "-fconstant-string-class=OFConstantString -fno-constant-cfstrings"; }; };
4BA115D21DA9432D007ED4EA /* LegacyPasswordGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BA115CE1DA9432D007ED4EA /* LegacyPasswordGenerator.m */; settings = {COMPILER_FLAGS = "-fconstant-string-class=OFConstantString -fno-constant-cfstrings"; }; };
4BA115D31DA9432D007ED4EA /* NewPasswordGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BA115D01DA9432D007ED4EA /* NewPasswordGenerator.m */; settings = {COMPILER_FLAGS = "-fconstant-string-class=OFConstantString -fno-constant-cfstrings"; }; };
4BA115D61DA94390007ED4EA /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BA115D51DA94390007ED4EA /* UIKit.framework */; };
4BB3CDFD1DA9764300FEE5ED /* AddSiteController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BB3CDFC1DA9764300FEE5ED /* AddSiteController.m */; };
4BC29DD51FCB5FAE00A1E786 /* SelectKeyFileController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BC29DD41FCB5FAE00A1E786 /* SelectKeyFileController.m */; };
4BF4ADEA1DA9A3DB0073B995 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BF4ADE91DA9A3DB0073B995 /* Foundation.framework */; };
4BF4ADED1DA9A6B00073B995 /* SiteStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BF4ADEC1DA9A6B00073B995 /* SiteStorage.m */; settings = {COMPILER_FLAGS = "-fconstant-string-class=OFConstantString -fno-constant-cfstrings"; }; };
4BF4C3A022B602F50034FCED /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BF4C39F22B602F50034FCED /* main.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -45,37 +45,30 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
4B0719231DAA78D80065997A /* ShowDetailsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShowDetailsController.h; sourceTree = "<group>"; };
4B0719241DAA78D80065997A /* ShowDetailsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShowDetailsController.m; sourceTree = "<group>"; };
4B2E52DC1DA942840040D091 /* scrypt-pwgen.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "scrypt-pwgen.app"; sourceTree = BUILT_PRODUCTS_DIR; };
4B2E52E01DA942840040D091 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
4B2E52E21DA942840040D091 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
4B2E52E31DA942840040D091 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
4B2E52E51DA942840040D091 /* MainViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = "<group>"; };
4B2E52E61DA942840040D091 /* MainViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = "<group>"; };
4B2E52E91DA942840040D091 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
4B2E52EB1DA942840040D091 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
4B2E52EE1DA942840040D091 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
4B2E52F01DA942840040D091 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
4B82D1131DAAAFCE00F32B2F /* AboutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutController.h; sourceTree = "<group>"; };
4B82D1141DAAAFCE00F32B2F /* AboutController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutController.m; sourceTree = "<group>"; };
4B31D80822B58F0F00494B15 /* SiteStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteStorage.swift; sourceTree = "<group>"; };
4B38148322B5ED01005C27B2 /* bridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bridge.h; sourceTree = "<group>"; };
4B5BCEF822B5B94C00E551BD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
4B5BCEFA22B5CF3200E551BD /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
4B5BCEFC22B5D98800E551BD /* SelectKeyFileController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectKeyFileController.swift; sourceTree = "<group>"; };
4B5BCEFE22B5E36900E551BD /* ShowDetailsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowDetailsController.swift; sourceTree = "<group>"; };
4B93656D22B5ADA00099DD08 /* HTTPServerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPServerDelegate.swift; sourceTree = "<group>"; };
4B93656F22B5AE2C0099DD08 /* AboutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutController.swift; sourceTree = "<group>"; };
4B93657122B5B1FB0099DD08 /* AddSiteController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteController.swift; sourceTree = "<group>"; };
4B9525231F96BB820095F259 /* ObjFW.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ObjFW.framework; path = ObjFW/Frameworks/ObjFW.framework; sourceTree = "<group>"; };
4B9525241F96BB820095F259 /* ObjFW_Bridge.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ObjFW_Bridge.framework; path = ObjFW/Frameworks/ObjFW_Bridge.framework; sourceTree = "<group>"; };
4B9EB1142008167200EB66F2 /* HTTPServerDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTTPServerDelegate.m; sourceTree = "<group>"; };
4B9EB1162008168400EB66F2 /* HTTPServerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTTPServerDelegate.h; sourceTree = "<group>"; };
4BA115CD1DA9432D007ED4EA /* LegacyPasswordGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LegacyPasswordGenerator.h; path = ../LegacyPasswordGenerator.h; sourceTree = "<group>"; };
4BA115CE1DA9432D007ED4EA /* LegacyPasswordGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LegacyPasswordGenerator.m; path = ../LegacyPasswordGenerator.m; sourceTree = "<group>"; };
4BA115CF1DA9432D007ED4EA /* NewPasswordGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NewPasswordGenerator.h; path = ../NewPasswordGenerator.h; sourceTree = "<group>"; };
4BA115D01DA9432D007ED4EA /* NewPasswordGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NewPasswordGenerator.m; path = ../NewPasswordGenerator.m; sourceTree = "<group>"; };
4BA115D11DA9432D007ED4EA /* PasswordGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PasswordGenerator.h; path = ../PasswordGenerator.h; sourceTree = "<group>"; };
4BA115D51DA94390007ED4EA /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
4BB3CDFB1DA9764300FEE5ED /* AddSiteController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddSiteController.h; sourceTree = "<group>"; };
4BB3CDFC1DA9764300FEE5ED /* AddSiteController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddSiteController.m; sourceTree = "<group>"; };
4BC29DD41FCB5FAE00A1E786 /* SelectKeyFileController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SelectKeyFileController.m; sourceTree = "<group>"; };
4BC29DD61FCB5FC400A1E786 /* SelectKeyFileController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SelectKeyFileController.h; sourceTree = "<group>"; };
4BF4ADE91DA9A3DB0073B995 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
4BF4ADEB1DA9A6B00073B995 /* SiteStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SiteStorage.h; sourceTree = "<group>"; };
4BF4ADEC1DA9A6B00073B995 /* SiteStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SiteStorage.m; sourceTree = "<group>"; };
4BF4C39F22B602F50034FCED /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -115,26 +108,19 @@
isa = PBXGroup;
children = (
4B2E52EB1DA942840040D091 /* Assets.xcassets */,
4B82D1131DAAAFCE00F32B2F /* AboutController.h */,
4B82D1141DAAAFCE00F32B2F /* AboutController.m */,
4BB3CDFB1DA9764300FEE5ED /* AddSiteController.h */,
4BB3CDFC1DA9764300FEE5ED /* AddSiteController.m */,
4B2E52E21DA942840040D091 /* AppDelegate.h */,
4B2E52E31DA942840040D091 /* AppDelegate.m */,
4B9EB1162008168400EB66F2 /* HTTPServerDelegate.h */,
4B9EB1142008167200EB66F2 /* HTTPServerDelegate.m */,
4B93656F22B5AE2C0099DD08 /* AboutController.swift */,
4B93657122B5B1FB0099DD08 /* AddSiteController.swift */,
4B5BCEF822B5B94C00E551BD /* AppDelegate.swift */,
4B93656D22B5ADA00099DD08 /* HTTPServerDelegate.swift */,
4B2E52F01DA942840040D091 /* Info.plist */,
4B2E52ED1DA942840040D091 /* LaunchScreen.storyboard */,
4B2E52E81DA942840040D091 /* Main.storyboard */,
4B2E52E51DA942840040D091 /* MainViewController.h */,
4B2E52E61DA942840040D091 /* MainViewController.m */,
4BC29DD61FCB5FC400A1E786 /* SelectKeyFileController.h */,
4BC29DD41FCB5FAE00A1E786 /* SelectKeyFileController.m */,
4B0719231DAA78D80065997A /* ShowDetailsController.h */,
4B0719241DAA78D80065997A /* ShowDetailsController.m */,
4BF4ADEB1DA9A6B00073B995 /* SiteStorage.h */,
4BF4ADEC1DA9A6B00073B995 /* SiteStorage.m */,
4B2E52E01DA942840040D091 /* main.m */,
4B5BCEFA22B5CF3200E551BD /* MainViewController.swift */,
4B5BCEFC22B5D98800E551BD /* SelectKeyFileController.swift */,
4B5BCEFE22B5E36900E551BD /* ShowDetailsController.swift */,
4B31D80822B58F0F00494B15 /* SiteStorage.swift */,
4B38148322B5ED01005C27B2 /* bridge.h */,
4BF4C39F22B602F50034FCED /* main.m */,
);
name = iOS;
sourceTree = "<group>";
@ -195,6 +181,7 @@
4B2E52DB1DA942840040D091 = {
CreatedOnToolsVersion = 8.0;
DevelopmentTeam = MXKNFCKFL6;
LastSwiftMigration = 1020;
ProvisioningStyle = Automatic;
};
};
@ -204,6 +191,7 @@
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
@ -235,17 +223,17 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4B82D1151DAAAFCE00F32B2F /* AboutController.m in Sources */,
4BC29DD51FCB5FAE00A1E786 /* SelectKeyFileController.m in Sources */,
4BB3CDFD1DA9764300FEE5ED /* AddSiteController.m in Sources */,
4B2E52E41DA942840040D091 /* AppDelegate.m in Sources */,
4B9EB1152008167200EB66F2 /* HTTPServerDelegate.m in Sources */,
4B5BCEF922B5B94C00E551BD /* AppDelegate.swift in Sources */,
4B93656E22B5ADA00099DD08 /* HTTPServerDelegate.swift in Sources */,
4B5BCEFB22B5CF3200E551BD /* MainViewController.swift in Sources */,
4B93657222B5B1FB0099DD08 /* AddSiteController.swift in Sources */,
4BA115D21DA9432D007ED4EA /* LegacyPasswordGenerator.m in Sources */,
4B2E52E71DA942840040D091 /* MainViewController.m in Sources */,
4BA115D31DA9432D007ED4EA /* NewPasswordGenerator.m in Sources */,
4B0719251DAA78D80065997A /* ShowDetailsController.m in Sources */,
4BF4ADED1DA9A6B00073B995 /* SiteStorage.m in Sources */,
4B2E52E11DA942840040D091 /* main.m in Sources */,
4B31D80922B58F0F00494B15 /* SiteStorage.swift in Sources */,
4B5BCEFD22B5D98800E551BD /* SelectKeyFileController.swift in Sources */,
4B5BCEFF22B5E36900E551BD /* ShowDetailsController.swift in Sources */,
4BF4C3A022B602F50034FCED /* main.m in Sources */,
4B93657022B5AE2C0099DD08 /* AboutController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -390,8 +378,12 @@
);
HEADER_SEARCH_PATHS = ObjFW/include;
INFOPLIST_FILE = Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "zone.heap.scrypt-pwgen.ios";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = bridge.h;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@ -411,8 +403,11 @@
);
HEADER_SEARCH_PATHS = ObjFW/include;
INFOPLIST_FILE = Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "zone.heap.scrypt-pwgen.ios";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = bridge.h;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;