rtlduino rtl8710af gcc base version

This commit is contained in:
RtlduinoMan 2016-09-12 17:16:56 +08:00
parent 7675bdb95f
commit 9c0c9edf61
2097 changed files with 779974 additions and 2 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:WiGadget.xcodeproj">
</FileRef>
</Workspace>

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
<false/>
<key>IDESourceControlProjectIdentifier</key>
<string>B152F20E-B611-4502-BF05-5E99353FEEF4</string>
<key>IDESourceControlProjectName</key>
<string>WiGadget</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>02adf5fe-d9db-4368-aa7a-e3d91ea887ce++505</key>
<string>https://subversion.assembla.com/svn/wigadget-ios/trunk</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>WiGadget.xcodeproj</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>02adf5fe-d9db-4368-aa7a-e3d91ea887ce++505</key>
<string>../..</string>
</dict>
<key>IDESourceControlProjectRepositoryRootDictionary</key>
<dict>
<key>02adf5fe-d9db-4368-aa7a-e3d91ea887ce++505</key>
<string>https://subversion.assembla.com/svn/wigadget-ios</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>https://subversion.assembla.com/svn/wigadget-ios/trunk/WiGadget.xcodeproj</string>
<key>IDESourceControlProjectVersion</key>
<integer>111</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>02adf5fe-d9db-4368-aa7a-e3d91ea887ce++505</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.subversion</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>02adf5fe-d9db-4368-aa7a-e3d91ea887ce++505</string>
<key>IDESourceControlWCCName</key>
<string>WiGadget</string>
</dict>
</array>
</dict>
</plist>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
</Bucket>

View file

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F390C9551B60C5D5009BB14F"
BuildableName = "WiGadget.app"
BlueprintName = "WiGadget"
ReferencedContainer = "container:WiGadget.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F390C96A1B60C5D5009BB14F"
BuildableName = "WiGadgetTests.xctest"
BlueprintName = "WiGadgetTests"
ReferencedContainer = "container:WiGadget.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F390C96A1B60C5D5009BB14F"
BuildableName = "WiGadgetTests.xctest"
BlueprintName = "WiGadgetTests"
ReferencedContainer = "container:WiGadget.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F390C9551B60C5D5009BB14F"
BuildableName = "WiGadget.app"
BlueprintName = "WiGadget"
ReferencedContainer = "container:WiGadget.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F390C9551B60C5D5009BB14F"
BuildableName = "WiGadget.app"
BlueprintName = "WiGadget"
ReferencedContainer = "container:WiGadget.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F390C9551B60C5D5009BB14F"
BuildableName = "WiGadget.app"
BlueprintName = "WiGadget"
ReferencedContainer = "container:WiGadget.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>WiGadget.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>F390C9551B60C5D5009BB14F</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>F390C96A1B60C5D5009BB14F</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,48 @@
//
// AmebaDevice.swift
// bonjour
//
// Created by WU JINZHOU on 11/8/15.
// Copyright (c) 2015 WU JINZHOU. All rights reserved.
//
import Foundation
class Ameba {
var name:String = R.not_assigned
var ip:String = R.not_assigned
var mac:String = R.not_assigned
var port:String = R.not_assigned
var control_type:String = R.not_assigned
var pair_state:String = R.not_assigned
var link_state:String = R.offline
var firebase_app_id:String = R.not_assigned
var key:String = R.not_assigned
var description:String = R.not_assigned
init(resolvedTxtRecords:[String:String]){
self.name = resolvedTxtRecords[R.key_service_name]!
self.mac = resolvedTxtRecords[R.key_service_mac]!
self.ip = resolvedTxtRecords[R.key_service_ip]!
self.port = resolvedTxtRecords[R.key_service_port]!
self.control_type = resolvedTxtRecords[R.key_service_control_type]!
self.pair_state = resolvedTxtRecords[R.key_service_pair_state]!
}
init(device_mac:String,service_name:String,control_type:String,shared_key:String,firebase_app_id:String,description:String,pair_state:String,link_state:String){
self.mac = device_mac
self.name = service_name
self.control_type = control_type
self.key = shared_key
self.firebase_app_id = firebase_app_id
self.description = description
self.pair_state = pair_state
self.link_state = link_state
}
func printInfo(){
Log.i("* name: \(name) ip: \(ip) mac: \(mac) port: \(port) control_type: \(control_type) pair_state: \(pair_state) link_state: \(link_state) firebase_app_id: \(firebase_app_id) description: \(description) key: \(key)")
}
}

View file

@ -0,0 +1,231 @@
//
// AmebaList.swift
// WiGadget
//
// Created by WU JINZHOU on 15/8/15.
// Copyright (c) 2015 WU JINZHOU. All rights reserved.
//
import Foundation
protocol SavedListDelegate:class {
func onSavedListUpdated(savedList:[Ameba])
}
protocol NewfoundListDelegate:class{
func onNewfoundListUpdated(newfoundList:[Ameba])
}
class AmebaList {
static var trackingList = [Ameba]()
static var savedList = [Ameba]()
static var newfoundList = [Ameba]()
static var amebaJSONArray = [JSON]()
static var linkTarget:Ameba?
//static weak var trackingListDataSource:TrackingListDelegate?
static weak var savedListDataSource:SavedListDelegate?
static weak var newfoundListDataSource:NewfoundListDelegate?
//MARK: update
class func update(ameba:Ameba) {
//update trackingList
trackingListAddAmeba(ameba)
//update savedList
if savedListLookup(ameba) != R.not_found {
if ameba.pair_state == R.not_paired {
savedListRemoveAmeba(ameba)
//update newfoundList
ameba.link_state = R.online
newfoundListAddAmeba(ameba)
}
}
else{
//update newfoundList
ameba.link_state = R.online
newfoundListAddAmeba(ameba)
}
printInfo()
}
//MARK: trackingList
class func trackingListRemoveAmeba(ameba:Ameba) {
trackingList = trackingList.filter({$0.mac != ameba.mac})
}
class func trackingListAddAmeba(ameba:Ameba) {
let macList = trackingList.map({$0.mac})
if let idx = macList.indexOf(ameba.mac) {
trackingList[idx] = ameba
}
else{
trackingList.append(ameba)
}
}
class func trackingListGetAmebaByMac(mac:String) -> Ameba? {
let tempList = trackingList.filter({ $0.mac == mac})
if tempList.count != 0 {
return tempList[0]
}
return nil
}
//MARK: savedList
class func savedListLookup(ameba:Ameba) -> Int {
for i in 0 ..< savedList.count {
if savedList[i].mac == ameba.mac {
return i
}
}
return R.not_found
}
class func savedListRemoveAmeba(ameba:Ameba){
savedList = savedList.filter({$0.mac != ameba.mac})
save()
savedListDataSource?.onSavedListUpdated(savedList)
}
class func savedListAddAmeba(ameba:Ameba){
let macList = savedList.map({$0.mac})
if let idx = macList.indexOf(ameba.mac) {
savedList[idx] = ameba
}
else {
savedList.append(ameba)
}
save()
savedListDataSource?.onSavedListUpdated(savedList)
}
class func savedListGetAmebaByMac(mac:String) -> Ameba? {
let tempList = savedList.filter({$0.mac == mac})
if tempList.count != 0 {
return tempList[0]
}
return nil
}
//MARK: newfoundList
class func newfoundListRemoveAmeba(ameba:Ameba){
newfoundList = newfoundList.filter({$0.mac != ameba.mac})
newfoundListDataSource?.onNewfoundListUpdated(newfoundList)
}
class func newfoundListAddAmeba(ameba:Ameba){
let macList = newfoundList.map({$0.mac})
if let idx = macList.indexOf(ameba.mac) {
newfoundList[idx] = ameba
}
else {
newfoundList.append(ameba)
}
newfoundListDataSource?.onNewfoundListUpdated(newfoundList)
}
class func newfoundListGetAmebaByMac(mac:String) -> Ameba? {
let tempList = newfoundList.filter({$0.mac == mac})
if tempList.count != 0 {
return tempList[0]
}
return nil
}
//MARK: pair
class func pair(ameba:Ameba){
newfoundListRemoveAmeba(ameba)
savedListAddAmeba(ameba)
}
//MARK: unpair
class func unpair(ameba:Ameba){
savedListRemoveAmeba(ameba)
}
//MARK: save
class func save(){
amebaJSONArray = []
for ameba in savedList {
let json = [R.key_service_mac:ameba.mac,
R.key_service_name:ameba.name,
R.key_service_control_type:ameba.control_type,
R.key_service_shared_key:ameba.key,
R.key_service_firebase_app_id:ameba.firebase_app_id,
R.key_service_description:ameba.description,
R.key_service_pair_state:ameba.pair_state,
R.key_link_state:ameba.link_state] as JSON
amebaJSONArray.append(json)
}
let data_to_save = JSON(amebaJSONArray).rawString(NSUTF8StringEncoding, options: [])
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setValue(data_to_save, forKey: R.key_ameba_data)
defaults.synchronize()
Log.i("\(amebaJSONArray.count) amebas saved to user defaults")
}
//MARK: load
class func load(){
let defaults = NSUserDefaults.standardUserDefaults()
let dataStr = defaults.valueForKey(R.key_ameba_data) as? String ?? R.not_assigned
amebaJSONArray = JSON(data: dataStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!).arrayValue
Log.v("amebaJSONArray.count: \(amebaJSONArray.count)")
for json in amebaJSONArray {
let deviceMac = json[R.key_service_mac].stringValue
let serviceName = json[R.key_service_name].stringValue
let controlType = json[R.key_service_control_type].stringValue
let sharedAESKey = json[R.key_service_shared_key].stringValue
let firebaseAppId = json[R.key_service_firebase_app_id].stringValue
let deviceDescription = json[R.key_service_description].stringValue
let pairState = json[R.key_service_pair_state].stringValue
let linkState = json[R.key_link_state].stringValue
let ameba = Ameba(device_mac: deviceMac,
service_name: serviceName,
control_type: controlType,
shared_key: sharedAESKey,
firebase_app_id: firebaseAppId,
description: deviceDescription,
pair_state: pairState,
link_state:linkState)
savedListAddAmeba(ameba)
Log.i("ameba \(deviceMac) added to savedList from JSON array")
}
}
class func printInfo() {
Log.v("---- ameba lists ----")
Log.i("trackingList: ")
for a in trackingList {
a.printInfo()
}
Log.i("savedList: ")
for a in savedList {
a.printInfo()
}
Log.i("newfoundList: ")
for a in newfoundList {
a.printInfo()
}
}
}

View file

@ -0,0 +1,46 @@
//
// AppDelegate.swift
// WiGadget
//
// Created by WU JINZHOU on 23/7/15.
// Copyright (c) 2015 WU JINZHOU. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="8191" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_launcher_676x1054" translatesAutoresizingMaskIntoConstraints="NO" id="0Rm-qn-MZy">
<rect key="frame" x="123" y="57.5" width="234" height="365"/>
<constraints>
<constraint firstAttribute="width" secondItem="0Rm-qn-MZy" secondAttribute="height" multiplier="676:1054" id="0hO-Y7-S8R"/>
<constraint firstAttribute="height" constant="365" id="3eA-sW-NjO"/>
</constraints>
</imageView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="0Rm-qn-MZy" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="TOs-d6-khG"/>
<constraint firstItem="0Rm-qn-MZy" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="Zp8-jB-Qrx"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</objects>
<resources>
<image name="ic_launcher_676x1054" width="676" height="1054"/>
</resources>
</document>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,61 @@
//
// BonjourBrowser.swift
// bonjour
//
// Created by WU JINZHOU on 31/7/15.
// Copyright (c) 2015 WU JINZHOU. All rights reserved.
//
import UIKit
class BonjourBrowser:NSObject,NSNetServiceBrowserDelegate,NSNetServiceDelegate {
var nsb : NSNetServiceBrowser!
var serviceList = [NSNetService]()
let resolve_time_out:NSTimeInterval = R.bonjour_service_resolve_time_out
func browse (serviceName:String,domain:String) {
Log.v("\(serviceName)")
self.serviceList.removeAll()
self.nsb = NSNetServiceBrowser()
self.nsb.delegate = self
self.nsb.searchForServicesOfType(serviceName,inDomain: domain)
}
func netServiceBrowser(aNetServiceBrowser: NSNetServiceBrowser, didFindService aNetService: NSNetService, moreComing: Bool) {
self.serviceList.append(aNetService)
if !moreComing {
for service in self.serviceList {
if service.port == R.not_found {
Log.v("name: \(service.name) , type: \(service.type)")
service.delegate = self
service.resolveWithTimeout(resolve_time_out)
}
}
}
}
func netService(sender: NSNetService, didUpdateTXTRecordData data: NSData) {
Log.v("\(sender)")
sender.resolveWithTimeout(resolve_time_out)
}
func netServiceDidResolveAddress(sender: NSNetService) {
var txtRecords = [String:String]()
//class func dictionaryFromTXTRecordData(_ txtData: NSData) -> [String : NSData]
for (k,v) in NSNetService.dictionaryFromTXTRecordData(sender.TXTRecordData()!){
txtRecords.updateValue(NSString(data: v , encoding: NSASCIIStringEncoding)! as String , forKey: k )
}
Log.v("TXTRecord:\(txtRecords)")
let ameba = Ameba(resolvedTxtRecords: txtRecords)
AmebaList.update(ameba)
sender.startMonitoring()
}
}

View file

@ -0,0 +1,121 @@
//
// Crypto.swift
// WiGadget
//
// Created by WU JINZHOU on 27/8/15.
// Copyright (c) 2015 WU JINZHOU. All rights reserved.
//
import Foundation
class Crypto {
static var curve25519_private_key = [UInt8]()
static var curve25519_base_point = R.curve25519_base_point
static var curve25519_my_public_key = [UInt8](count: 32, repeatedValue: 0)
static var curve25519_his_public_key = [UInt8]()
static var curve25519_shared_key = [UInt8](count: 32, repeatedValue: 0)
static var aes_128_key = [UInt8]()
static var aes_iv = [UInt8](R.aes_iv.utf8)
static var aes_block_size = aes_iv.count
//generate curve25519 public key
class func makeCurve25519PublicKey() -> String {
for _ in 0 ..< 32 {
curve25519_private_key.append(UInt8(arc4random() % 256))
}
curve25519_donna(&curve25519_my_public_key, &curve25519_private_key, &curve25519_base_point)
return "\(curve25519_my_public_key)"
}
//generate curve25519 shared key & AES shared key
class func makePSK(hisCurve25519PublicKey:String) -> String {
let strArr = hisCurve25519PublicKey.characters.split { $0 == "," }.map { String($0) }
for item in strArr {
let components = item.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet)
let part = components.joinWithSeparator("")
if let intVal = Int(part) {
curve25519_his_public_key.append(UInt8(intVal))
}
}
curve25519_donna(&curve25519_shared_key, &curve25519_private_key, &curve25519_his_public_key)
aes_128_key = [UInt8](curve25519_shared_key[0...15])
return "\(aes_128_key)"
}
//aes128 encryption
class func encrypt(plainText:String,key:String) -> String {
var k8 = [UInt8]()
let strArr = key.characters.split { $0 == "," }.map { String($0) }
for item in strArr {
let components = item.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet)
let part = components.joinWithSeparator("")
if let intVal = Int(part) {
k8.append(UInt8(intVal))
}
}
var pt8 = [UInt8](plainText.utf8)
//zero padding
let r = pt8.count % aes_block_size
for _ in 0 ..< (aes_block_size - r) {
pt8.append(0)
}
var ct8 = pt8 //allocate mem for ct8
AES128_CBC_encrypt_buffer(&ct8, &pt8, UInt32(pt8.count), &k8, &aes_iv)
return "\(ct8)"
}
class func decrypt(cipherText:String,key:String) -> String {
var k8 = [UInt8]()
var strArr = key.characters.split { $0 == "," }.map { String($0) }
for item in strArr {
let components = item.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet)
let part = components.joinWithSeparator("")
if let intVal = Int(part) {
k8.append(UInt8(intVal))
}
}
var ct8 = [UInt8]()
strArr = cipherText.characters.split { $0 == "," }.map { String($0) }
for item in strArr {
let components = item.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet)
let part = components.joinWithSeparator("")
if let intVal = Int(part) {
ct8.append(UInt8(intVal))
}
}
var pt8 = ct8 //allocate mem for pt8
AES128_CBC_decrypt_buffer(&pt8, &ct8, UInt32(ct8.count), &k8, &aes_iv)
pt8 = pt8.filter({$0 != 0})
let pt = NSString(bytes: pt8, length: pt8.count, encoding: NSUTF8StringEncoding) as! String
return pt
}
}

View file

@ -0,0 +1,860 @@
/* Copyright 2008, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* 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.
*
* curve25519-donna: Curve25519 elliptic curve, public key function
*
* http://code.google.com/p/curve25519-donna/
*
* Adam Langley <agl@imperialviolet.org>
*
* Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>
*
* More information about curve25519 can be found here
* http://cr.yp.to/ecdh.html
*
* djb's sample implementation of curve25519 is written in a special assembly
* language called qhasm and uses the floating point registers.
*
* This is, almost, a clean room reimplementation from the curve25519 paper. It
* uses many of the tricks described therein. Only the crecip function is taken
* from the sample implementation. */
#include <string.h>
#include <stdint.h>
#ifdef _MSC_VER
#define inline __inline
#endif
typedef uint8_t u8;
typedef int32_t s32;
typedef int64_t limb;
/* Field element representation:
*
* Field elements are written as an array of signed, 64-bit limbs, least
* significant first. The value of the field element is:
* x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ...
*
* i.e. the limbs are 26, 25, 26, 25, ... bits wide. */
/* Sum two numbers: output += in */
static void fsum(limb *output, const limb *in) {
unsigned i;
for (i = 0; i < 10; i += 2) {
output[0+i] = output[0+i] + in[0+i];
output[1+i] = output[1+i] + in[1+i];
}
}
/* Find the difference of two numbers: output = in - output
* (note the order of the arguments!). */
static void fdifference(limb *output, const limb *in) {
unsigned i;
for (i = 0; i < 10; ++i) {
output[i] = in[i] - output[i];
}
}
/* Multiply a number by a scalar: output = in * scalar */
static void fscalar_product(limb *output, const limb *in, const limb scalar) {
unsigned i;
for (i = 0; i < 10; ++i) {
output[i] = in[i] * scalar;
}
}
/* Multiply two numbers: output = in2 * in
*
* output must be distinct to both inputs. The inputs are reduced coefficient
* form, the output is not.
*
* output[x] <= 14 * the largest product of the input limbs. */
static void fproduct(limb *output, const limb *in2, const limb *in) {
output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]);
output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) +
((limb) ((s32) in2[1])) * ((s32) in[0]);
output[2] = 2 * ((limb) ((s32) in2[1])) * ((s32) in[1]) +
((limb) ((s32) in2[0])) * ((s32) in[2]) +
((limb) ((s32) in2[2])) * ((s32) in[0]);
output[3] = ((limb) ((s32) in2[1])) * ((s32) in[2]) +
((limb) ((s32) in2[2])) * ((s32) in[1]) +
((limb) ((s32) in2[0])) * ((s32) in[3]) +
((limb) ((s32) in2[3])) * ((s32) in[0]);
output[4] = ((limb) ((s32) in2[2])) * ((s32) in[2]) +
2 * (((limb) ((s32) in2[1])) * ((s32) in[3]) +
((limb) ((s32) in2[3])) * ((s32) in[1])) +
((limb) ((s32) in2[0])) * ((s32) in[4]) +
((limb) ((s32) in2[4])) * ((s32) in[0]);
output[5] = ((limb) ((s32) in2[2])) * ((s32) in[3]) +
((limb) ((s32) in2[3])) * ((s32) in[2]) +
((limb) ((s32) in2[1])) * ((s32) in[4]) +
((limb) ((s32) in2[4])) * ((s32) in[1]) +
((limb) ((s32) in2[0])) * ((s32) in[5]) +
((limb) ((s32) in2[5])) * ((s32) in[0]);
output[6] = 2 * (((limb) ((s32) in2[3])) * ((s32) in[3]) +
((limb) ((s32) in2[1])) * ((s32) in[5]) +
((limb) ((s32) in2[5])) * ((s32) in[1])) +
((limb) ((s32) in2[2])) * ((s32) in[4]) +
((limb) ((s32) in2[4])) * ((s32) in[2]) +
((limb) ((s32) in2[0])) * ((s32) in[6]) +
((limb) ((s32) in2[6])) * ((s32) in[0]);
output[7] = ((limb) ((s32) in2[3])) * ((s32) in[4]) +
((limb) ((s32) in2[4])) * ((s32) in[3]) +
((limb) ((s32) in2[2])) * ((s32) in[5]) +
((limb) ((s32) in2[5])) * ((s32) in[2]) +
((limb) ((s32) in2[1])) * ((s32) in[6]) +
((limb) ((s32) in2[6])) * ((s32) in[1]) +
((limb) ((s32) in2[0])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[0]);
output[8] = ((limb) ((s32) in2[4])) * ((s32) in[4]) +
2 * (((limb) ((s32) in2[3])) * ((s32) in[5]) +
((limb) ((s32) in2[5])) * ((s32) in[3]) +
((limb) ((s32) in2[1])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[1])) +
((limb) ((s32) in2[2])) * ((s32) in[6]) +
((limb) ((s32) in2[6])) * ((s32) in[2]) +
((limb) ((s32) in2[0])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[0]);
output[9] = ((limb) ((s32) in2[4])) * ((s32) in[5]) +
((limb) ((s32) in2[5])) * ((s32) in[4]) +
((limb) ((s32) in2[3])) * ((s32) in[6]) +
((limb) ((s32) in2[6])) * ((s32) in[3]) +
((limb) ((s32) in2[2])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[2]) +
((limb) ((s32) in2[1])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[1]) +
((limb) ((s32) in2[0])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[0]);
output[10] = 2 * (((limb) ((s32) in2[5])) * ((s32) in[5]) +
((limb) ((s32) in2[3])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[3]) +
((limb) ((s32) in2[1])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[1])) +
((limb) ((s32) in2[4])) * ((s32) in[6]) +
((limb) ((s32) in2[6])) * ((s32) in[4]) +
((limb) ((s32) in2[2])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[2]);
output[11] = ((limb) ((s32) in2[5])) * ((s32) in[6]) +
((limb) ((s32) in2[6])) * ((s32) in[5]) +
((limb) ((s32) in2[4])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[4]) +
((limb) ((s32) in2[3])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[3]) +
((limb) ((s32) in2[2])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[2]);
output[12] = ((limb) ((s32) in2[6])) * ((s32) in[6]) +
2 * (((limb) ((s32) in2[5])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[5]) +
((limb) ((s32) in2[3])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[3])) +
((limb) ((s32) in2[4])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[4]);
output[13] = ((limb) ((s32) in2[6])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[6]) +
((limb) ((s32) in2[5])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[5]) +
((limb) ((s32) in2[4])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[4]);
output[14] = 2 * (((limb) ((s32) in2[7])) * ((s32) in[7]) +
((limb) ((s32) in2[5])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[5])) +
((limb) ((s32) in2[6])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[6]);
output[15] = ((limb) ((s32) in2[7])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[7]) +
((limb) ((s32) in2[6])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[6]);
output[16] = ((limb) ((s32) in2[8])) * ((s32) in[8]) +
2 * (((limb) ((s32) in2[7])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[7]));
output[17] = ((limb) ((s32) in2[8])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[8]);
output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]);
}
/* Reduce a long form to a short form by taking the input mod 2^255 - 19.
*
* On entry: |output[i]| < 14*2^54
* On exit: |output[0..8]| < 280*2^54 */
static void freduce_degree(limb *output) {
/* Each of these shifts and adds ends up multiplying the value by 19.
*
* For output[0..8], the absolute entry value is < 14*2^54 and we add, at
* most, 19*14*2^54 thus, on exit, |output[0..8]| < 280*2^54. */
output[8] += output[18] << 4;
output[8] += output[18] << 1;
output[8] += output[18];
output[7] += output[17] << 4;
output[7] += output[17] << 1;
output[7] += output[17];
output[6] += output[16] << 4;
output[6] += output[16] << 1;
output[6] += output[16];
output[5] += output[15] << 4;
output[5] += output[15] << 1;
output[5] += output[15];
output[4] += output[14] << 4;
output[4] += output[14] << 1;
output[4] += output[14];
output[3] += output[13] << 4;
output[3] += output[13] << 1;
output[3] += output[13];
output[2] += output[12] << 4;
output[2] += output[12] << 1;
output[2] += output[12];
output[1] += output[11] << 4;
output[1] += output[11] << 1;
output[1] += output[11];
output[0] += output[10] << 4;
output[0] += output[10] << 1;
output[0] += output[10];
}
#if (-1 & 3) != 3
#error "This code only works on a two's complement system"
#endif
/* return v / 2^26, using only shifts and adds.
*
* On entry: v can take any value. */
static inline limb
div_by_2_26(const limb v)
{
/* High word of v; no shift needed. */
const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
/* Set to all 1s if v was negative; else set to 0s. */
const int32_t sign = ((int32_t) highword) >> 31;
/* Set to 0x3ffffff if v was negative; else set to 0. */
const int32_t roundoff = ((uint32_t) sign) >> 6;
/* Should return v / (1<<26) */
return (v + roundoff) >> 26;
}
/* return v / (2^25), using only shifts and adds.
*
* On entry: v can take any value. */
static inline limb
div_by_2_25(const limb v)
{
/* High word of v; no shift needed*/
const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
/* Set to all 1s if v was negative; else set to 0s. */
const int32_t sign = ((int32_t) highword) >> 31;
/* Set to 0x1ffffff if v was negative; else set to 0. */
const int32_t roundoff = ((uint32_t) sign) >> 7;
/* Should return v / (1<<25) */
return (v + roundoff) >> 25;
}
/* Reduce all coefficients of the short form input so that |x| < 2^26.
*
* On entry: |output[i]| < 280*2^54 */
static void freduce_coefficients(limb *output) {
unsigned i;
output[10] = 0;
for (i = 0; i < 10; i += 2) {
limb over = div_by_2_26(output[i]);
/* The entry condition (that |output[i]| < 280*2^54) means that over is, at
* most, 280*2^28 in the first iteration of this loop. This is added to the
* next limb and we can approximate the resulting bound of that limb by
* 281*2^54. */
output[i] -= over << 26;
output[i+1] += over;
/* For the first iteration, |output[i+1]| < 281*2^54, thus |over| <
* 281*2^29. When this is added to the next limb, the resulting bound can
* be approximated as 281*2^54.
*
* For subsequent iterations of the loop, 281*2^54 remains a conservative
* bound and no overflow occurs. */
over = div_by_2_25(output[i+1]);
output[i+1] -= over << 25;
output[i+2] += over;
}
/* Now |output[10]| < 281*2^29 and all other coefficients are reduced. */
output[0] += output[10] << 4;
output[0] += output[10] << 1;
output[0] += output[10];
output[10] = 0;
/* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19*281*2^29
* So |over| will be no more than 2^16. */
{
limb over = div_by_2_26(output[0]);
output[0] -= over << 26;
output[1] += over;
}
/* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 2^16 < 2^26. The
* bound on |output[1]| is sufficient to meet our needs. */
}
/* A helpful wrapper around fproduct: output = in * in2.
*
* On entry: |in[i]| < 2^27 and |in2[i]| < 2^27.
*
* output must be distinct to both inputs. The output is reduced degree
* (indeed, one need only provide storage for 10 limbs) and |output[i]| < 2^26. */
static void
fmul(limb *output, const limb *in, const limb *in2) {
limb t[19];
fproduct(t, in, in2);
/* |t[i]| < 14*2^54 */
freduce_degree(t);
freduce_coefficients(t);
/* |t[i]| < 2^26 */
memcpy(output, t, sizeof(limb) * 10);
}
/* Square a number: output = in**2
*
* output must be distinct from the input. The inputs are reduced coefficient
* form, the output is not.
*
* output[x] <= 14 * the largest product of the input limbs. */
static void fsquare_inner(limb *output, const limb *in) {
output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]);
output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]);
output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) +
((limb) ((s32) in[0])) * ((s32) in[2]));
output[3] = 2 * (((limb) ((s32) in[1])) * ((s32) in[2]) +
((limb) ((s32) in[0])) * ((s32) in[3]));
output[4] = ((limb) ((s32) in[2])) * ((s32) in[2]) +
4 * ((limb) ((s32) in[1])) * ((s32) in[3]) +
2 * ((limb) ((s32) in[0])) * ((s32) in[4]);
output[5] = 2 * (((limb) ((s32) in[2])) * ((s32) in[3]) +
((limb) ((s32) in[1])) * ((s32) in[4]) +
((limb) ((s32) in[0])) * ((s32) in[5]));
output[6] = 2 * (((limb) ((s32) in[3])) * ((s32) in[3]) +
((limb) ((s32) in[2])) * ((s32) in[4]) +
((limb) ((s32) in[0])) * ((s32) in[6]) +
2 * ((limb) ((s32) in[1])) * ((s32) in[5]));
output[7] = 2 * (((limb) ((s32) in[3])) * ((s32) in[4]) +
((limb) ((s32) in[2])) * ((s32) in[5]) +
((limb) ((s32) in[1])) * ((s32) in[6]) +
((limb) ((s32) in[0])) * ((s32) in[7]));
output[8] = ((limb) ((s32) in[4])) * ((s32) in[4]) +
2 * (((limb) ((s32) in[2])) * ((s32) in[6]) +
((limb) ((s32) in[0])) * ((s32) in[8]) +
2 * (((limb) ((s32) in[1])) * ((s32) in[7]) +
((limb) ((s32) in[3])) * ((s32) in[5])));
output[9] = 2 * (((limb) ((s32) in[4])) * ((s32) in[5]) +
((limb) ((s32) in[3])) * ((s32) in[6]) +
((limb) ((s32) in[2])) * ((s32) in[7]) +
((limb) ((s32) in[1])) * ((s32) in[8]) +
((limb) ((s32) in[0])) * ((s32) in[9]));
output[10] = 2 * (((limb) ((s32) in[5])) * ((s32) in[5]) +
((limb) ((s32) in[4])) * ((s32) in[6]) +
((limb) ((s32) in[2])) * ((s32) in[8]) +
2 * (((limb) ((s32) in[3])) * ((s32) in[7]) +
((limb) ((s32) in[1])) * ((s32) in[9])));
output[11] = 2 * (((limb) ((s32) in[5])) * ((s32) in[6]) +
((limb) ((s32) in[4])) * ((s32) in[7]) +
((limb) ((s32) in[3])) * ((s32) in[8]) +
((limb) ((s32) in[2])) * ((s32) in[9]));
output[12] = ((limb) ((s32) in[6])) * ((s32) in[6]) +
2 * (((limb) ((s32) in[4])) * ((s32) in[8]) +
2 * (((limb) ((s32) in[5])) * ((s32) in[7]) +
((limb) ((s32) in[3])) * ((s32) in[9])));
output[13] = 2 * (((limb) ((s32) in[6])) * ((s32) in[7]) +
((limb) ((s32) in[5])) * ((s32) in[8]) +
((limb) ((s32) in[4])) * ((s32) in[9]));
output[14] = 2 * (((limb) ((s32) in[7])) * ((s32) in[7]) +
((limb) ((s32) in[6])) * ((s32) in[8]) +
2 * ((limb) ((s32) in[5])) * ((s32) in[9]));
output[15] = 2 * (((limb) ((s32) in[7])) * ((s32) in[8]) +
((limb) ((s32) in[6])) * ((s32) in[9]));
output[16] = ((limb) ((s32) in[8])) * ((s32) in[8]) +
4 * ((limb) ((s32) in[7])) * ((s32) in[9]);
output[17] = 2 * ((limb) ((s32) in[8])) * ((s32) in[9]);
output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]);
}
/* fsquare sets output = in^2.
*
* On entry: The |in| argument is in reduced coefficients form and |in[i]| <
* 2^27.
*
* On exit: The |output| argument is in reduced coefficients form (indeed, one
* need only provide storage for 10 limbs) and |out[i]| < 2^26. */
static void
fsquare(limb *output, const limb *in) {
limb t[19];
fsquare_inner(t, in);
/* |t[i]| < 14*2^54 because the largest product of two limbs will be <
* 2^(27+27) and fsquare_inner adds together, at most, 14 of those
* products. */
freduce_degree(t);
freduce_coefficients(t);
/* |t[i]| < 2^26 */
memcpy(output, t, sizeof(limb) * 10);
}
/* Take a little-endian, 32-byte number and expand it into polynomial form */
static void
fexpand(limb *output, const u8 *input) {
#define F(n,start,shift,mask) \
output[n] = ((((limb) input[start + 0]) | \
((limb) input[start + 1]) << 8 | \
((limb) input[start + 2]) << 16 | \
((limb) input[start + 3]) << 24) >> shift) & mask;
F(0, 0, 0, 0x3ffffff);
F(1, 3, 2, 0x1ffffff);
F(2, 6, 3, 0x3ffffff);
F(3, 9, 5, 0x1ffffff);
F(4, 12, 6, 0x3ffffff);
F(5, 16, 0, 0x1ffffff);
F(6, 19, 1, 0x3ffffff);
F(7, 22, 3, 0x1ffffff);
F(8, 25, 4, 0x3ffffff);
F(9, 28, 6, 0x1ffffff);
#undef F
}
#if (-32 >> 1) != -16
#error "This code only works when >> does sign-extension on negative numbers"
#endif
/* s32_eq returns 0xffffffff iff a == b and zero otherwise. */
static s32 s32_eq(s32 a, s32 b) {
a = ~(a ^ b);
a &= a << 16;
a &= a << 8;
a &= a << 4;
a &= a << 2;
a &= a << 1;
return a >> 31;
}
/* s32_gte returns 0xffffffff if a >= b and zero otherwise, where a and b are
* both non-negative. */
static s32 s32_gte(s32 a, s32 b) {
a -= b;
/* a >= 0 iff a >= b. */
return ~(a >> 31);
}
/* Take a fully reduced polynomial form number and contract it into a
* little-endian, 32-byte array.
*
* On entry: |input_limbs[i]| < 2^26 */
static void
fcontract(u8 *output, limb *input_limbs) {
int i;
int j;
s32 input[10];
s32 mask;
/* |input_limbs[i]| < 2^26, so it's valid to convert to an s32. */
for (i = 0; i < 10; i++) {
input[i] = (s32)input_limbs[i];
}
for (j = 0; j < 2; ++j) {
for (i = 0; i < 9; ++i) {
if ((i & 1) == 1) {
/* This calculation is a time-invariant way to make input[i]
* non-negative by borrowing from the next-larger limb. */
const s32 mask = input[i] >> 31;
const s32 carry = -((input[i] & mask) >> 25);
input[i] = input[i] + (carry << 25);
input[i+1] = input[i+1] - carry;
} else {
const s32 mask = input[i] >> 31;
const s32 carry = -((input[i] & mask) >> 26);
input[i] = input[i] + (carry << 26);
input[i+1] = input[i+1] - carry;
}
}
/* There's no greater limb for input[9] to borrow from, but we can multiply
* by 19 and borrow from input[0], which is valid mod 2^255-19. */
{
const s32 mask = input[9] >> 31;
const s32 carry = -((input[9] & mask) >> 25);
input[9] = input[9] + (carry << 25);
input[0] = input[0] - (carry * 19);
}
/* After the first iteration, input[1..9] are non-negative and fit within
* 25 or 26 bits, depending on position. However, input[0] may be
* negative. */
}
/* The first borrow-propagation pass above ended with every limb
except (possibly) input[0] non-negative.
If input[0] was negative after the first pass, then it was because of a
carry from input[9]. On entry, input[9] < 2^26 so the carry was, at most,
one, since (2**26-1) >> 25 = 1. Thus input[0] >= -19.
In the second pass, each limb is decreased by at most one. Thus the second
borrow-propagation pass could only have wrapped around to decrease
input[0] again if the first pass left input[0] negative *and* input[1]
through input[9] were all zero. In that case, input[1] is now 2^25 - 1,
and this last borrow-propagation step will leave input[1] non-negative. */
{
const s32 mask = input[0] >> 31;
const s32 carry = -((input[0] & mask) >> 26);
input[0] = input[0] + (carry << 26);
input[1] = input[1] - carry;
}
/* All input[i] are now non-negative. However, there might be values between
* 2^25 and 2^26 in a limb which is, nominally, 25 bits wide. */
for (j = 0; j < 2; j++) {
for (i = 0; i < 9; i++) {
if ((i & 1) == 1) {
const s32 carry = input[i] >> 25;
input[i] &= 0x1ffffff;
input[i+1] += carry;
} else {
const s32 carry = input[i] >> 26;
input[i] &= 0x3ffffff;
input[i+1] += carry;
}
}
{
const s32 carry = input[9] >> 25;
input[9] &= 0x1ffffff;
input[0] += 19*carry;
}
}
/* If the first carry-chain pass, just above, ended up with a carry from
* input[9], and that caused input[0] to be out-of-bounds, then input[0] was
* < 2^26 + 2*19, because the carry was, at most, two.
*
* If the second pass carried from input[9] again then input[0] is < 2*19 and
* the input[9] -> input[0] carry didn't push input[0] out of bounds. */
/* It still remains the case that input might be between 2^255-19 and 2^255.
* In this case, input[1..9] must take their maximum value and input[0] must
* be >= (2^255-19) & 0x3ffffff, which is 0x3ffffed. */
mask = s32_gte(input[0], 0x3ffffed);
for (i = 1; i < 10; i++) {
if ((i & 1) == 1) {
mask &= s32_eq(input[i], 0x1ffffff);
} else {
mask &= s32_eq(input[i], 0x3ffffff);
}
}
/* mask is either 0xffffffff (if input >= 2^255-19) and zero otherwise. Thus
* this conditionally subtracts 2^255-19. */
input[0] -= mask & 0x3ffffed;
for (i = 1; i < 10; i++) {
if ((i & 1) == 1) {
input[i] -= mask & 0x1ffffff;
} else {
input[i] -= mask & 0x3ffffff;
}
}
input[1] <<= 2;
input[2] <<= 3;
input[3] <<= 5;
input[4] <<= 6;
input[6] <<= 1;
input[7] <<= 3;
input[8] <<= 4;
input[9] <<= 6;
#define F(i, s) \
output[s+0] |= input[i] & 0xff; \
output[s+1] = (input[i] >> 8) & 0xff; \
output[s+2] = (input[i] >> 16) & 0xff; \
output[s+3] = (input[i] >> 24) & 0xff;
output[0] = 0;
output[16] = 0;
F(0,0);
F(1,3);
F(2,6);
F(3,9);
F(4,12);
F(5,16);
F(6,19);
F(7,22);
F(8,25);
F(9,28);
#undef F
}
/* Input: Q, Q', Q-Q'
* Output: 2Q, Q+Q'
*
* x2 z3: long form
* x3 z3: long form
* x z: short form, destroyed
* xprime zprime: short form, destroyed
* qmqp: short form, preserved
*
* On entry and exit, the absolute value of the limbs of all inputs and outputs
* are < 2^26. */
static void fmonty(limb *x2, limb *z2, /* output 2Q */
limb *x3, limb *z3, /* output Q + Q' */
limb *x, limb *z, /* input Q */
limb *xprime, limb *zprime, /* input Q' */
const limb *qmqp /* input Q - Q' */) {
limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19],
zzprime[19], zzzprime[19], xxxprime[19];
memcpy(origx, x, 10 * sizeof(limb));
fsum(x, z);
/* |x[i]| < 2^27 */
fdifference(z, origx); /* does x - z */
/* |z[i]| < 2^27 */
memcpy(origxprime, xprime, sizeof(limb) * 10);
fsum(xprime, zprime);
/* |xprime[i]| < 2^27 */
fdifference(zprime, origxprime);
/* |zprime[i]| < 2^27 */
fproduct(xxprime, xprime, z);
/* |xxprime[i]| < 14*2^54: the largest product of two limbs will be <
* 2^(27+27) and fproduct adds together, at most, 14 of those products.
* (Approximating that to 2^58 doesn't work out.) */
fproduct(zzprime, x, zprime);
/* |zzprime[i]| < 14*2^54 */
freduce_degree(xxprime);
freduce_coefficients(xxprime);
/* |xxprime[i]| < 2^26 */
freduce_degree(zzprime);
freduce_coefficients(zzprime);
/* |zzprime[i]| < 2^26 */
memcpy(origxprime, xxprime, sizeof(limb) * 10);
fsum(xxprime, zzprime);
/* |xxprime[i]| < 2^27 */
fdifference(zzprime, origxprime);
/* |zzprime[i]| < 2^27 */
fsquare(xxxprime, xxprime);
/* |xxxprime[i]| < 2^26 */
fsquare(zzzprime, zzprime);
/* |zzzprime[i]| < 2^26 */
fproduct(zzprime, zzzprime, qmqp);
/* |zzprime[i]| < 14*2^52 */
freduce_degree(zzprime);
freduce_coefficients(zzprime);
/* |zzprime[i]| < 2^26 */
memcpy(x3, xxxprime, sizeof(limb) * 10);
memcpy(z3, zzprime, sizeof(limb) * 10);
fsquare(xx, x);
/* |xx[i]| < 2^26 */
fsquare(zz, z);
/* |zz[i]| < 2^26 */
fproduct(x2, xx, zz);
/* |x2[i]| < 14*2^52 */
freduce_degree(x2);
freduce_coefficients(x2);
/* |x2[i]| < 2^26 */
fdifference(zz, xx); // does zz = xx - zz
/* |zz[i]| < 2^27 */
memset(zzz + 10, 0, sizeof(limb) * 9);
fscalar_product(zzz, zz, 121665);
/* |zzz[i]| < 2^(27+17) */
/* No need to call freduce_degree here:
fscalar_product doesn't increase the degree of its input. */
freduce_coefficients(zzz);
/* |zzz[i]| < 2^26 */
fsum(zzz, xx);
/* |zzz[i]| < 2^27 */
fproduct(z2, zz, zzz);
/* |z2[i]| < 14*2^(26+27) */
freduce_degree(z2);
freduce_coefficients(z2);
/* |z2|i| < 2^26 */
}
/* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave
* them unchanged if 'iswap' is 0. Runs in data-invariant time to avoid
* side-channel attacks.
*
* NOTE that this function requires that 'iswap' be 1 or 0; other values give
* wrong results. Also, the two limb arrays must be in reduced-coefficient,
* reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped,
* and all all values in a[0..9],b[0..9] must have magnitude less than
* INT32_MAX. */
static void
swap_conditional(limb a[19], limb b[19], limb iswap) {
unsigned i;
const s32 swap = (s32) -iswap;
for (i = 0; i < 10; ++i) {
const s32 x = swap & ( ((s32)a[i]) ^ ((s32)b[i]) );
a[i] = ((s32)a[i]) ^ x;
b[i] = ((s32)b[i]) ^ x;
}
}
/* Calculates nQ where Q is the x-coordinate of a point on the curve
*
* resultx/resultz: the x coordinate of the resulting curve point (short form)
* n: a little endian, 32-byte number
* q: a point of the curve (short form) */
static void
cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) {
limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0};
limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t;
limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1};
limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h;
unsigned i, j;
memcpy(nqpqx, q, sizeof(limb) * 10);
for (i = 0; i < 32; ++i) {
u8 byte = n[31 - i];
for (j = 0; j < 8; ++j) {
const limb bit = byte >> 7;
swap_conditional(nqx, nqpqx, bit);
swap_conditional(nqz, nqpqz, bit);
fmonty(nqx2, nqz2,
nqpqx2, nqpqz2,
nqx, nqz,
nqpqx, nqpqz,
q);
swap_conditional(nqx2, nqpqx2, bit);
swap_conditional(nqz2, nqpqz2, bit);
t = nqx;
nqx = nqx2;
nqx2 = t;
t = nqz;
nqz = nqz2;
nqz2 = t;
t = nqpqx;
nqpqx = nqpqx2;
nqpqx2 = t;
t = nqpqz;
nqpqz = nqpqz2;
nqpqz2 = t;
byte <<= 1;
}
}
memcpy(resultx, nqx, sizeof(limb) * 10);
memcpy(resultz, nqz, sizeof(limb) * 10);
}
// -----------------------------------------------------------------------------
// Shamelessly copied from djb's code
// -----------------------------------------------------------------------------
static void
crecip(limb *out, const limb *z) {
limb z2[10];
limb z9[10];
limb z11[10];
limb z2_5_0[10];
limb z2_10_0[10];
limb z2_20_0[10];
limb z2_50_0[10];
limb z2_100_0[10];
limb t0[10];
limb t1[10];
int i;
/* 2 */ fsquare(z2,z);
/* 4 */ fsquare(t1,z2);
/* 8 */ fsquare(t0,t1);
/* 9 */ fmul(z9,t0,z);
/* 11 */ fmul(z11,z9,z2);
/* 22 */ fsquare(t0,z11);
/* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9);
/* 2^6 - 2^1 */ fsquare(t0,z2_5_0);
/* 2^7 - 2^2 */ fsquare(t1,t0);
/* 2^8 - 2^3 */ fsquare(t0,t1);
/* 2^9 - 2^4 */ fsquare(t1,t0);
/* 2^10 - 2^5 */ fsquare(t0,t1);
/* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0);
/* 2^11 - 2^1 */ fsquare(t0,z2_10_0);
/* 2^12 - 2^2 */ fsquare(t1,t0);
/* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
/* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0);
/* 2^21 - 2^1 */ fsquare(t0,z2_20_0);
/* 2^22 - 2^2 */ fsquare(t1,t0);
/* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
/* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0);
/* 2^41 - 2^1 */ fsquare(t1,t0);
/* 2^42 - 2^2 */ fsquare(t0,t1);
/* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
/* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0);
/* 2^51 - 2^1 */ fsquare(t0,z2_50_0);
/* 2^52 - 2^2 */ fsquare(t1,t0);
/* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
/* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0);
/* 2^101 - 2^1 */ fsquare(t1,z2_100_0);
/* 2^102 - 2^2 */ fsquare(t0,t1);
/* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
/* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0);
/* 2^201 - 2^1 */ fsquare(t0,t1);
/* 2^202 - 2^2 */ fsquare(t1,t0);
/* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
/* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0);
/* 2^251 - 2^1 */ fsquare(t1,t0);
/* 2^252 - 2^2 */ fsquare(t0,t1);
/* 2^253 - 2^3 */ fsquare(t1,t0);
/* 2^254 - 2^4 */ fsquare(t0,t1);
/* 2^255 - 2^5 */ fsquare(t1,t0);
/* 2^255 - 21 */ fmul(out,t1,z11);
}
int
curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) {
limb bp[10], x[10], z[11], zmone[10];
uint8_t e[32];
int i;
for (i = 0; i < 32; ++i) e[i] = secret[i];
e[0] &= 248;
e[31] &= 127;
e[31] |= 64;
fexpand(bp, basepoint);
cmult(x, z, e, bp);
crecip(zmone, z);
fmul(z, x, zmone);
fcontract(mypublic, z);
return 0;
}

View file

@ -0,0 +1,14 @@
#ifndef __curve25519_donnaDotH__
#define __curve25519_donnaDotH__
#ifdef __cplusplus
extern "C" {
#endif
void curve25519_donna( unsigned char *outKey, const unsigned char *inSecret, const unsigned char *inBasePoint );
#ifdef __cplusplus
}
#endif
#endif // __curve25519_donnaDotH__

View file

@ -0,0 +1,583 @@
/*
This is an implementation of the AES128 algorithm, specifically ECB and CBC mode.
The implementation is verified against the test vectors in:
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
ECB-AES128
----------
plain-text:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710
key:
2b7e151628aed2a6abf7158809cf4f3c
resulting cipher
3ad77bb40d7a3660a89ecaf32466ef97
f5d3d58503b9699de785895a96fdbaaf
43b1cd7f598ece23881b00e3ed030688
7b0c785e27e8ad3f8223207104725dd4
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
*/
/*****************************************************************************/
/* Includes: */
/*****************************************************************************/
#include <stdint.h>
#include <string.h> // CBC mode, for memset
#include "aes.h"
/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
#define Nb 4
// The number of 32 bit words in a key.
#define Nk 4
// Key length in bytes [128 bit]
#define KEYLEN 16
// The number of rounds in AES Cipher.
#define Nr 10
// jcallan@github points out that declaring Multiply as a function
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES128-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif
/*****************************************************************************/
/* Private variables: */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];
static state_t* state;
// The array that stores the round keys.
static uint8_t RoundKey[176];
// The Key input to the AES Program
static const uint8_t* Key;
#if defined(CBC) && CBC
// Initial Vector used only for CBC mode
static uint8_t* Iv;
#endif
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM -
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
static const uint8_t sbox[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
static const uint8_t rsbox[256] =
{ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
// The round constant word array, Rcon[i], contains the values given by
// x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
// Note that i starts at 1, not 0).
static const uint8_t Rcon[255] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb };
/*****************************************************************************/
/* Private functions: */
/*****************************************************************************/
static uint8_t getSBoxValue(uint8_t num)
{
return sbox[num];
}
static uint8_t getSBoxInvert(uint8_t num)
{
return rsbox[num];
}
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
static void KeyExpansion(void)
{
uint32_t i, j, k;
uint8_t tempa[4]; // Used for the column/row operations
// The first round key is the key itself.
for(i = 0; i < Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
// All other round keys are found from the previous round keys.
for(; (i < (Nb * (Nr + 1))); ++i)
{
for(j = 0; j < 4; ++j)
{
tempa[j]=RoundKey[(i-1) * 4 + j];
}
if (i % Nk == 0)
{
// This function rotates the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
k = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = k;
}
// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i/Nk];
}
else if (Nk > 6 && i % Nk == 4)
{
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
RoundKey[i * 4 + 0] = RoundKey[(i - Nk) * 4 + 0] ^ tempa[0];
RoundKey[i * 4 + 1] = RoundKey[(i - Nk) * 4 + 1] ^ tempa[1];
RoundKey[i * 4 + 2] = RoundKey[(i - Nk) * 4 + 2] ^ tempa[2];
RoundKey[i * 4 + 3] = RoundKey[(i - Nk) * 4 + 3] ^ tempa[3];
}
}
// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round)
{
uint8_t i,j;
for(i=0;i<4;++i)
{
for(j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[round * Nb * 4 + i * Nb + j];
}
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(void)
{
uint8_t i, j;
for(i = 0; i < 4; ++i)
{
for(j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(void)
{
uint8_t temp;
// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
}
// MixColumns function mixes the columns of the state matrix
static void MixColumns(void)
{
uint8_t i;
uint8_t Tmp,Tm,t;
for(i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}
// Multiply is used to multiply numbers in the field GF(2^8)
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
((y>>1 & 1) * xtime(x)) ^
((y>>2 & 1) * xtime(xtime(x))) ^
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))));
}
#else
#define Multiply(x, y) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
#endif
// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void InvMixColumns(void)
{
int i;
uint8_t a,b,c,d;
for(i=0;i<4;++i)
{
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(void)
{
uint8_t i,j;
for(i=0;i<4;++i)
{
for(j=0;j<4;++j)
{
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(void)
{
uint8_t temp;
// Rotate first row 1 columns to right
temp=(*state)[3][1];
(*state)[3][1]=(*state)[2][1];
(*state)[2][1]=(*state)[1][1];
(*state)[1][1]=(*state)[0][1];
(*state)[0][1]=temp;
// Rotate second row 2 columns to right
temp=(*state)[0][2];
(*state)[0][2]=(*state)[2][2];
(*state)[2][2]=temp;
temp=(*state)[1][2];
(*state)[1][2]=(*state)[3][2];
(*state)[3][2]=temp;
// Rotate third row 3 columns to right
temp=(*state)[0][3];
(*state)[0][3]=(*state)[1][3];
(*state)[1][3]=(*state)[2][3];
(*state)[2][3]=(*state)[3][3];
(*state)[3][3]=temp;
}
// Cipher is the main function that encrypts the PlainText.
static void Cipher(void)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for(round = 1; round < Nr; ++round)
{
SubBytes();
ShiftRows();
MixColumns();
AddRoundKey(round);
}
// The last round is given below.
// The MixColumns function is not here in the last round.
SubBytes();
ShiftRows();
AddRoundKey(Nr);
}
static void InvCipher(void)
{
uint8_t round=0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(Nr);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for(round=Nr-1;round>0;round--)
{
InvShiftRows();
InvSubBytes();
AddRoundKey(round);
InvMixColumns();
}
// The last round is given below.
// The MixColumns function is not here in the last round.
InvShiftRows();
InvSubBytes();
AddRoundKey(0);
}
static void BlockCopy(uint8_t* output, uint8_t* input)
{
uint8_t i;
for (i=0;i<KEYLEN;++i)
{
output[i] = input[i];
}
}
/*****************************************************************************/
/* Public functions: */
/*****************************************************************************/
#if defined(ECB) && ECB
void AES128_ECB_encrypt(uint8_t* input, const uint8_t* key, uint8_t* output)
{
// Copy input to output, and work in-memory on output
BlockCopy(output, input);
state = (state_t*)output;
Key = key;
KeyExpansion();
// The next function call encrypts the PlainText with the Key using AES algorithm.
Cipher();
}
void AES128_ECB_decrypt(uint8_t* input, const uint8_t* key, uint8_t *output)
{
// Copy input to output, and work in-memory on output
BlockCopy(output, input);
state = (state_t*)output;
// The KeyExpansion routine must be called before encryption.
Key = key;
KeyExpansion();
InvCipher();
}
#endif // #if defined(ECB) && ECB
#if defined(CBC) && CBC
static void XorWithIv(uint8_t* buf)
{
uint8_t i;
for(i = 0; i < KEYLEN; ++i)
{
buf[i] ^= Iv[i];
}
}
void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv)
{
uintptr_t i;
uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */
BlockCopy(output, input);
state = (state_t*)output;
// Skip the key expansion if key is passed as 0
if(0 != key)
{
Key = key;
KeyExpansion();
}
if(iv != 0)
{
Iv = (uint8_t*)iv;
}
for(i = 0; i < length; i += KEYLEN)
{
XorWithIv(input);
BlockCopy(output, input);
state = (state_t*)output;
Cipher();
Iv = output;
input += KEYLEN;
output += KEYLEN;
}
if(remainders)
{
BlockCopy(output, input);
memset(output + remainders, 0, KEYLEN - remainders); /* add 0-padding */
state = (state_t*)output;
Cipher();
}
}
void AES128_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv)
{
uintptr_t i;
uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */
BlockCopy(output, input);
state = (state_t*)output;
// Skip the key expansion if key is passed as 0
if(0 != key)
{
Key = key;
KeyExpansion();
}
// If iv is passed as 0, we continue to encrypt without re-setting the Iv
if(iv != 0)
{
Iv = (uint8_t*)iv;
}
for(i = 0; i < length; i += KEYLEN)
{
BlockCopy(output, input);
state = (state_t*)output;
InvCipher();
XorWithIv(output);
Iv = input;
input += KEYLEN;
output += KEYLEN;
}
if(remainders)
{
BlockCopy(output, input);
memset(output+remainders, 0, KEYLEN - remainders); /* add 0-padding */
state = (state_t*)output;
InvCipher();
}
}
#endif // #if defined(CBC) && CBC

View file

@ -0,0 +1,40 @@
#ifndef _AES_H_
#define _AES_H_
#include <stdint.h>
// #define the macros below to 1/0 to enable/disable the mode of operation.
//
// CBC enables AES128 encryption in CBC-mode of operation and handles 0-padding.
// ECB enables the basic ECB 16-byte block algorithm. Both can be enabled simultaneously.
// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
#ifndef CBC
#define CBC 1
#endif
#ifndef ECB
#define ECB 1
#endif
#if defined(ECB) && ECB
void AES128_ECB_encrypt(uint8_t* input, const uint8_t* key, uint8_t *output);
void AES128_ECB_decrypt(uint8_t* input, const uint8_t* key, uint8_t *output);
#endif // #if defined(ECB) && ECB
#if defined(CBC) && CBC
void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv);
void AES128_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv);
#endif // #if defined(CBC) && CBC
#endif //_AES_H_

View file

@ -0,0 +1,55 @@
//
// FAuthData.h
// Firebase
//
// Created by Katherine Fang on 7/30/14.
//
#import <Accounts/Accounts.h>
#import "FAuthType.h"
/**
* The FAuthData class is a wrapper around the user metadata returned from the Firebase auth server.
* It includes the provider authenticated against, a uid (with the possible exception of authenticating against a custom
* backend), and a token used to authenticate with Firebase.
*
* It may include other metadata about the user, depending on the provider used to do the authentication.
*/
@interface FAuthData : NSObject
/**
* @return Raw authentication token payload returned by the server
*/
@property (nonatomic, strong, readonly) NSDictionary *auth;
/**
* @return Authentication token expiration timestamp (seconds since epoch) returned by the server
*/
@property (nonatomic, strong, readonly) NSNumber *expires;
/**
* @return A uid for this user. It is unique across all auth providers.
*/
@property (nonatomic, strong, readonly) NSString *uid;
/**
* @return The provider that authenticated this user
*/
@property (nonatomic, readonly) NSString *provider;
/**
* @return The token that was used to authenticate this user with Firebase
*/
@property (nonatomic, strong, readonly) NSString *token;
/**
* @return Provider data keyed by provider. Includes cached data from third-party providers
*/
@property (nonatomic, strong, readonly) NSDictionary *providerData;
@end

View file

@ -0,0 +1,41 @@
//
// FAuthType.h
// Firebase
//
// Created by Katherine Fang on 7/30/14.
//
// All public-facing auth enums.
//
#ifndef Firebase_FAuthenticationTypes_h
#define Firebase_FAuthenticationTypes_h
typedef NS_ENUM(NSInteger, FAuthenticationError) {
// Developer / Config Errors
FAuthenticationErrorProviderDisabled = -1,
FAuthenticationErrorInvalidConfiguration = -2,
FAuthenticationErrorInvalidOrigin = -3,
FAuthenticationErrorInvalidProvider = -4,
// User Errors (Email / Password)
FAuthenticationErrorInvalidEmail = -5,
FAuthenticationErrorInvalidPassword = -6,
FAuthenticationErrorInvalidToken = -7,
FAuthenticationErrorUserDoesNotExist = -8,
FAuthenticationErrorEmailTaken = -9,
// User Errors (Facebook / Twitter / Github / Google)
FAuthenticationErrorDeniedByUser = -10,
FAuthenticationErrorInvalidCredentials = -11,
FAuthenticationErrorInvalidArguments = -12,
FAuthenticationErrorProviderError = -13,
FAuthenticationErrorLimitsExceeded = -14,
// Client side errors
FAuthenticationErrorNetworkError = -15,
FAuthenticationErrorPreempted = -16,
FAuthenticationErrorUnknown = -9999
};
#endif

View file

@ -0,0 +1,69 @@
/*
* Firebase iOS Client Library
*
* Copyright © 2015 Firebase - All Rights Reserved
* https://www.firebase.com
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binaryform must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY FIREBASE 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 FIREBASE 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 <Foundation/Foundation.h>
/**
* Configuration object for Firebase. You can get the default FConfig object via
* `[Firebase defaultConfig]` and modify it. You must make all changes to it before
* you create your first Firebase instance.
*/
@interface FConfig : NSObject
/**
* By default the Firebase client will keep data in memory while your application is running, but not
* when it is restarted. By setting this value to YES, the data will be persisted to on-device (disk)
* storage and will thus be available again when the app is restarted (even when there is no network
* connectivity at that time). Note that this property must be set before creating your first Firebase
* reference and only needs to be called once per application.
*
* If your app uses Firebase Authentication, the client will automatically persist the user's authentication
* token across restarts, even without persistence enabled. But if the auth token expired while offline and
* you've enabled persistence, the client will pause write operations until you successfully re-authenticate
* (or explicitly unauthenticate) to prevent your writes from being sent unauthenticated and failing due to
* security rules.
*/
@property (nonatomic) BOOL persistenceEnabled;
/**
* By default Firebase will use up to 10MB of disk space to cache data. If the cache grows beyond this size,
* Firebase will start removing data that hasn't been recently used. If you find that your application caches too
* little or too much data, call this method to change the cache size. This property must be set before creating
* your first Firebase reference and only needs to be called once per application.
*
* Note that the specified cache size is only an approximation and the size on disk may temporarily exceed it
* at times.
*/
@property (nonatomic) NSUInteger persistenceCacheSizeBytes;
/**
* Sets the dispatch queue on which all events are raised. The default queue is the main queue.
*/
@property (nonatomic, strong) dispatch_queue_t callbackQueue;
@end

View file

@ -0,0 +1,154 @@
/*
* Firebase iOS Client Library
*
* Copyright © 2013 Firebase - All Rights Reserved
* https://www.firebase.com
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binaryform must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY FIREBASE 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 FIREBASE 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 <Foundation/Foundation.h>
@class Firebase;
/**
* An FDataSnapshot contains data from a Firebase location. Any time you read
* Firebase data, you receive the data as an FDataSnapshot.
*
* FDataSnapshots are passed to the blocks you attach with observeEventType:withBlock: or observeSingleEvent:withBlock:.
* They are efficiently-generated immutable copies of the data at a Firebase location.
* They can't be modified and will never change. To modify data at a location,
* use a Firebase reference (e.g. with setValue:).
*/
@interface FDataSnapshot : NSObject
/** @name Navigating and inspecting a snapshot */
/**
* Get an FDataSnapshot for the location at the specified relative path.
* The relative path can either be a simple child key (e.g. 'fred')
* or a deeper slash-separated path (e.g. 'fred/name/first'). If the child
* location has no data, an empty FDataSnapshot is returned.
*
* @param childPathString A relative path to the location of child data.
* @return The FDataSnapshot for the child location.
*/
- (FDataSnapshot *) childSnapshotForPath:(NSString *)childPathString;
/**
* Return YES if the specified child exists.
*
* @param childPathString A relative path to the location of a potential child.
* @return YES if data exists at the specified childPathString, else false.
*/
- (BOOL) hasChild:(NSString *)childPathString;
/**
* Return YES if the DataSnapshot has any children.
*
* @return YES if this snapshot has any children, else NO.
*/
- (BOOL) hasChildren;
/**
* Return YES if the DataSnapshot contains a non-null value.
*
* @return YES if this snapshot contains a non-null value, otherwise NO.
*/
- (BOOL) exists;
/** @name Data export */
/**
* Returns the raw value at this location, coupled with any metadata, such as priority.
*
* Priorities, where they exist, are accessible under the ".priority" key in instances of NSDictionary.
* For leaf locations with priorities, the value will be under the ".value" key.
*/
- (id) valueInExportFormat;
/** @name Properties */
/**
* Returns the contents of this data snapshot as native types.
*
* Data types returned:
* * NSDictionary
* * NSArray
* * NSNumber (also includes booleans)
* * NSString
*
* @return The data as a native object.
*/
@property (strong, readonly, nonatomic) id value;
/**
* Get the number of children for this DataSnapshot.
*
* @return An integer indicating the number of children.
*/
@property (readonly, nonatomic) NSUInteger childrenCount;
/**
* Get a Firebase reference for the location that this data came from
*
* @return A Firebase instance for the location of this data
*/
@property (nonatomic, readonly, strong) Firebase* ref;
/**
* The key of the location that generated this FDataSnapshot.
*
* @return An NSString containing the key for the location of this FDataSnapshot.
*/
@property (strong, readonly, nonatomic) NSString* key;
/**
* An iterator for snapshots of the child nodes in this snapshot.
* You can use the native for..in syntax:
*
* for (FDataSnapshot* child in snapshot.children) {
* ...
* }
*
* @return An NSEnumerator of the children
*/
@property (strong, readonly, nonatomic) NSEnumerator* children;
/**
* The priority of the data in this FDataSnapshot.
*
* @return The priority as a string, or nil if no priority was set.
*/
@property (strong, readonly, nonatomic) id priority;
@end

View file

@ -0,0 +1,43 @@
/*
* Firebase iOS Client Library
*
* Copyright © 2013 Firebase - All Rights Reserved
* https://www.firebase.com
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binaryform must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY FIREBASE 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 FIREBASE 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.
*/
#ifndef Firebase_FEventType_h
#define Firebase_FEventType_h
/**
* This enum is the set of events that you can observe at a Firebase location.
*/
typedef NS_ENUM(NSInteger, FEventType) {
FEventTypeChildAdded, // 0, fired when a new child node is added to a location
FEventTypeChildRemoved, // 1, fired when a child node is removed from a location
FEventTypeChildChanged, // 2, fired when a child node at a location changes
FEventTypeChildMoved, // 3, fired when a child node moves relative to the other child nodes at a location
FEventTypeValue // 4, fired when any data changes at a location and, recursively, any children
};
#endif

View file

@ -0,0 +1,143 @@
/*
* Firebase iOS Client Library
*
* Copyright © 2013 Firebase - All Rights Reserved
* https://www.firebase.com
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binaryform must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY FIREBASE 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 FIREBASE 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 <Foundation/Foundation.h>
/**
* An FMutableData instance is populated with data from a Firebase location.
* When you are using runTransactionBlock:, you will be given an instance containing the current
* data at that location. Your block will be responsible for updating that instance to the data
* you wish to save at that location, and then returning using [FTransactionResult successWithValue:].
*
* To modify the data, set its value property to any of the native types support by Firebase:
* * NSNumber (includes BOOL)
* * NSDictionary
* * NSArray
* * NSString
* * nil / NSNull to remove the data
*
* Note that changes made to a child FMutableData instance will be visible to the parent.
*/
@interface FMutableData : NSObject
/** @name Inspecting and navigating the data */
/**
* Returns boolean indicating whether this mutable data has children.
*
* @return YES if this data contains child nodes.
*/
- (BOOL) hasChildren;
/**
* Indicates whether this mutable data has a child at the given path.
*
* @param path A path string, consisting either of a single segment, like 'child', or multiple segments, 'a/deeper/child'
* @return YES if this data contains a child at the specified relative path
*/
- (BOOL) hasChildAtPath:(NSString *)path;
/**
* Used to obtain an FMutableData instance that encapsulates the data at the given relative path.
* Note that changes made to the child will be visible to the parent.
*
* @param path A path string, consisting either of a single segment, like 'child', or multiple segments, 'a/deeper/child'
* @return An FMutableData instance containing the data at the given path
*/
- (FMutableData *) childDataByAppendingPath:(NSString *)path;
/** @name Properties */
/**
* This method is deprecated.
*
* @return An FMutableData instance containing the data at the parent location, or nil if this is the top-most location
*/
@property (strong, readonly, nonatomic) FMutableData* parent __attribute__((deprecated("Deprecated. Do not use.")));;
/**
* To modify the data contained by this instance of FMutableData, set this to any of the native types support by Firebase:
*
* * NSNumber (includes BOOL)
* * NSDictionary
* * NSArray
* * NSString
* * nil / NSNull to remove the data
*
* Note that setting the value will override the priority at this location.
*
* @return The current data at this location as a native object
*/
@property (strong, nonatomic) id value;
/**
* Set this property to update the priority of the data at this location. Can be set to the following types:
*
* * NSNumber
* * NSString
* * nil / NSNull to remove the priority
*
* @return The priority of the data at this location
*/
@property (strong, nonatomic) id priority;
/**
* @return The number of child nodes at this location
*/
@property (readonly, nonatomic) NSUInteger childrenCount;
/**
* Used to iterate over the children at this location. You can use the native for .. in syntax:
*
* for (FMutableData* child in data.children) {
* ...
* }
*
* Note that this enumerator operates on an immutable copy of the child list. So, you can modify the instance
* during iteration, but the new additions will not be visible until you get a new enumerator.
*/
@property (readonly, nonatomic, strong) NSEnumerator* children;
/**
* @return The key name of this node, or nil if it is the top-most location
*/
@property (readonly, nonatomic, strong) NSString* key;
@end

View file

@ -0,0 +1,412 @@
/*
* Firebase iOS Client Library
*
* Copyright © 2013 Firebase - All Rights Reserved
* https://www.firebase.com
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binaryform must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY FIREBASE 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 FIREBASE 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 <Foundation/Foundation.h>
#import "FEventType.h"
#import "FDataSnapshot.h"
typedef NSUInteger FirebaseHandle;
/**
* An FQuery instance represents a query over the data at a particular location.
*
* You create one by calling one of the query methods (queryStartingAtPriority:, queryEndingAtPriority:, etc.)
* on a Firebase reference. The query methods can be chained to further specify the data you are interested in
* observing
*/
@interface FQuery : NSObject
/** @name Attaching observers to read data */
/**
* observeEventType:withBlock: is used to listen for data changes at a particular location.
* This is the primary way to read data from Firebase. Your block will be triggered
* for the initial data and again whenever the data changes.
*
* Use removeObserverWithHandle: to stop receiving updates.
* @param eventType The type of event to listen for.
* @param block The block that should be called with initial data and updates. It is passed the data as an FDataSnapshot.
* @return A handle used to unregister this block later using removeObserverWithHandle:
*/
- (FirebaseHandle) observeEventType:(FEventType)eventType withBlock:(void (^)(FDataSnapshot* snapshot))block;
/**
* observeEventType:andPreviousSiblingKeyWithBlock: is used to listen for data changes at a particular location.
* This is the primary way to read data from Firebase. Your block will be triggered
* for the initial data and again whenever the data changes. In addition, for FEventTypeChildAdded, FEventTypeChildMoved, and
* FEventTypeChildChanged events, your block will be passed the key of the previous node by priority order.
*
* Use removeObserverWithHandle: to stop receiving updates.
*
* @param eventType The type of event to listen for.
* @param block The block that should be called with initial data and updates. It is passed the data as an FDataSnapshot
* and the previous child's key.
* @return A handle used to unregister this block later using removeObserverWithHandle:
*/
- (FirebaseHandle) observeEventType:(FEventType)eventType andPreviousSiblingKeyWithBlock:(void (^)(FDataSnapshot* snapshot, NSString* prevKey))block;
/**
* observeEventType:withBlock: is used to listen for data changes at a particular location.
* This is the primary way to read data from Firebase. Your block will be triggered
* for the initial data and again whenever the data changes.
*
* The cancelBlock will be called if you will no longer receive new events due to no longer having permission.
*
* Use removeObserverWithHandle: to stop receiving updates.
*
* @param eventType The type of event to listen for.
* @param block The block that should be called with initial data and updates. It is passed the data as an FDataSnapshot.
* @param cancelBlock The block that should be called if this client no longer has permission to receive these events
* @return A handle used to unregister this block later using removeObserverWithHandle:
*/
- (FirebaseHandle) observeEventType:(FEventType)eventType withBlock:(void (^)(FDataSnapshot* snapshot))block withCancelBlock:(void (^)(NSError* error))cancelBlock;
/**
* observeEventType:andPreviousSiblingKeyWithBlock: is used to listen for data changes at a particular location.
* This is the primary way to read data from Firebase. Your block will be triggered
* for the initial data and again whenever the data changes. In addition, for FEventTypeChildAdded, FEventTypeChildMoved, and
* FEventTypeChildChanged events, your block will be passed the key of the previous node by priority order.
*
* The cancelBlock will be called if you will no longer receive new events due to no longer having permission.
*
* Use removeObserverWithHandle: to stop receiving updates.
*
* @param eventType The type of event to listen for.
* @param block The block that should be called with initial data and updates. It is passed the data as an FDataSnapshot
* and the previous child's key.
* @param cancelBlock The block that should be called if this client no longer has permission to receive these events
* @return A handle used to unregister this block later using removeObserverWithHandle:
*/
- (FirebaseHandle) observeEventType:(FEventType)eventType andPreviousSiblingKeyWithBlock:(void (^)(FDataSnapshot* snapshot, NSString* prevKey))block withCancelBlock:(void (^)(NSError* error))cancelBlock;
/**
* This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned.
*
* @param eventType The type of event to listen for.
* @param block The block that should be called. It is passed the data as an FDataSnapshot.
*/
- (void) observeSingleEventOfType:(FEventType)eventType withBlock:(void (^)(FDataSnapshot* snapshot))block;
/**
* This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned. In addition, for FEventTypeChildAdded, FEventTypeChildMoved, and
* FEventTypeChildChanged events, your block will be passed the key of the previous node by priority order.
*
* @param eventType The type of event to listen for.
* @param block The block that should be called. It is passed the data as an FDataSnapshot and the previous child's key.
*/
- (void) observeSingleEventOfType:(FEventType)eventType andPreviousSiblingKeyWithBlock:(void (^)(FDataSnapshot* snapshot, NSString* prevKey))block;
/**
* This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned.
*
* The cancelBlock will be called if you do not have permission to read data at this location.
*
* @param eventType The type of event to listen for.
* @param block The block that should be called. It is passed the data as an FDataSnapshot.
* @param cancelBlock The block that will be called if you don't have permission to access this data
*/
- (void) observeSingleEventOfType:(FEventType)eventType withBlock:(void (^)(FDataSnapshot* snapshot))block withCancelBlock:(void (^)(NSError* error))cancelBlock;
/**
* This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned. In addition, for FEventTypeChildAdded, FEventTypeChildMoved, and
* FEventTypeChildChanged events, your block will be passed the key of the previous node by priority order.
*
* The cancelBlock will be called if you do not have permission to read data at this location.
*
* @param eventType The type of event to listen for.
* @param block The block that should be called. It is passed the data as an FDataSnapshot and the previous child's key.
* @param cancelBlock The block that will be called if you don't have permission to access this data
*/
- (void) observeSingleEventOfType:(FEventType)eventType andPreviousSiblingKeyWithBlock:(void (^)(FDataSnapshot* snapshot, NSString* prevKey))block withCancelBlock:(void (^)(NSError* error))cancelBlock;
/** @name Detaching observers */
/**
* Detach a block previously attached with observeEventType:withBlock:.
*
* @param handle The handle returned by the call to observeEventType:withBlock: which we are trying to remove.
*/
- (void) removeObserverWithHandle:(FirebaseHandle)handle;
/**
* Detach all blocks previously attached to this Firebase location with observeEventType:withBlock:
*/
- (void) removeAllObservers;
/**
* By calling `keepSynced:YES` on a location, the data for that location will automatically be downloaded and
* kept in sync, even when no listeners are attached for that location. Additionally, while a location is kept
* synced, it will not be evicted from the persistent disk cache.
*
* @param keepSynced Pass YES to keep this location synchronized, pass NO to stop synchronization.
*/
- (void) keepSynced:(BOOL)keepSynced;
/** @name Querying and limiting */
/**
* This method is deprecated in favor of using queryStartingAtValue:. This can be used with queryOrderedByPriority
* to query by priority.
*
* queryStartingAtPriority: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryStartingAtPriority: will respond to events at nodes with a priority
* greater than or equal to startPriority
*
* @param startPriority The lower bound, inclusive, for the priority of data visible to the returned FQuery
* @return An FQuery instance, limited to data with priority greater than or equal to startPriority
*/
- (FQuery *) queryStartingAtPriority:(id)startPriority __attribute__((deprecated("Use [[FQuery queryOrderedByPriority] queryStartingAtValue:] instead")));
/**
* This method is deprecated in favor of using queryStartingAtValue:childKey:. This can be used with queryOrderedByPriority
* to query by priority.
*
* queryStartingAtPriority:andChildName: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryStartingAtPriority:andChildName will respond to events at nodes with a priority
* greater than startPriority, or equal to startPriority and with a name greater than or equal to childName
*
* @param startPriority The lower bound, inclusive, for the priority of data visible to the returned FQuery
* @param childName The lower bound, inclusive, for the name of nodes with priority equal to startPriority
* @return An FQuery instance, limited to data with priority greater than or equal to startPriority
*/
- (FQuery *) queryStartingAtPriority:(id)startPriority andChildName:(NSString *)childName __attribute__((deprecated("Use [[FQuery queryOrderedByPriority] queryStartingAtValue:childKey:] instead")));
/**
* This method is deprecated in favor of using queryEndingAtValue:. This can be used with queryOrderedByPriority
* to query by priority.
*
* queryEndingAtPriority: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryEndingAtPriority: will respond to events at nodes with a priority
* less than or equal to startPriority and with a name greater than or equal to childName
*
* @param endPriority The upper bound, inclusive, for the priority of data visible to the returned FQuery
* @return An FQuery instance, limited to data with priority less than or equal to endPriority
*/
- (FQuery *) queryEndingAtPriority:(id)endPriority __attribute__((deprecated("Use [[FQuery queryOrderedByPriority] queryEndingAtValue:] instead")));
/**
* This method is deprecated in favor of using queryEndingAtValue:childKey:. This can be used with queryOrderedByPriority
* to query by priority.
*
* queryEndingAtPriority:andChildName: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryEndingAtPriority:andChildName will respond to events at nodes with a priority
* less than endPriority, or equal to endPriority and with a name less than or equal to childName
*
* @param endPriority The upper bound, inclusive, for the priority of data visible to the returned FQuery
* @param childName The upper bound, inclusive, for the name of nodes with priority equal to endPriority
* @return An FQuery instance, limited to data with priority less than endPriority or equal to endPriority and with a name less than or equal to childName
*/
- (FQuery *) queryEndingAtPriority:(id)endPriority andChildName:(NSString *)childName __attribute__((deprecated("Use [[FQuery queryOrderedByPriority] queryEndingAtValue:childKey:] instead")));
/**
* This method is deprecated in favor of using queryEqualToValue:. This can be used with queryOrderedByPriority
* to query by priority.
*
* queryEqualToPriority: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryEqualToPriority: will respond to events at nodes with a priority equal to
* supplied argument.
*
* @param priority The priority that the data returned by this FQuery will have
* @return An Fquery instance, limited to data with the supplied priority.
*/
- (FQuery *) queryEqualToPriority:(id)priority __attribute__((deprecated("Use [[FQuery queryOrderedByPriority] queryEqualToValue:] instead")));
/**
* This method is deprecated in favor of using queryEqualAtValue:childKey:. This can be used with queryOrderedByPriority
* to query by priority.
*
* queryEqualToPriority:andChildName: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryEqualToPriority:andChildName will respond to events at nodes with a priority
* equal to the supplied argument with a name equal to childName. There will be at most one node that matches because
* child names are unique.
*
* @param priority The priority that the data returned by this FQuery will have
* @param childName The name of nodes with the right priority
* @return An FQuery instance, limited to data with the supplied priority and the name.
*/
- (FQuery *) queryEqualToPriority:(id)priority andChildName:(NSString *)childName __attribute__((deprecated("Use [[FQuery queryOrderedByPriority] queryEqualToValue:childKey:] instead")));
/**
* This method is deprecated in favor of using queryLimitedToFirst:limit or queryLimitedToLast:limit instead.
*
* queryLimitedToNumberOfChildren: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryLimitedToNumberOfChildren: will respond to events from at most limit child nodes.
*
* @param limit The upper bound, inclusive, for the number of child nodes to receive events for
* @return An FQuery instance, limited to at most limit child nodes.
*/
- (FQuery *) queryLimitedToNumberOfChildren:(NSUInteger)limit __attribute__((deprecated("Use [FQuery queryLimitedToFirst:limit] or [FQuery queryLimitedToLast:limit] instead")));
/**
* queryLimitedToFirst: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryLimitedToFirst: will respond to at most the first limit child nodes.
*
* @param limit The upper bound, inclusive, for the number of child nodes to receive events for
* @return An FQuery instance, limited to at most limit child nodes.
*/
- (FQuery *) queryLimitedToFirst:(NSUInteger)limit;
/**
* queryLimitedToLast: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryLimitedToLast: will respond to at most the last limit child nodes.
*
* @param limit The upper bound, inclusive, for the number of child nodes to receive events for
* @return An FQuery instance, limited to at most limit child nodes.
*/
- (FQuery *) queryLimitedToLast:(NSUInteger)limit;
/**
* queryOrderBy: is used to generate a reference to a view of the data that's been sorted by the values of
* a particular child key. This method is intended to be used in combination with queryStartingAtValue:,
* queryEndingAtValue:, or queryEqualToValue:.
*
* @param key The child key to use in ordering data visible to the returned FQuery
* @return An FQuery instance, ordered by the values of the specified child key.
*/
- (FQuery *) queryOrderedByChild:(NSString *)key;
/**
* queryOrderedByKey: is used to generate a reference to a view of the data that's been sorted by child key.
* This method is intended to be used in combination with queryStartingAtValue:, queryEndingAtValue:,
* or queryEqualToValue:.
*
* @return An FQuery instance, ordered by child keys.
*/
- (FQuery *) queryOrderedByKey;
/**
* queryOrderedByValue: is used to generate a reference to a view of the data that's been sorted by child value.
* This method is intended to be used in combination with queryStartingAtValue:, queryEndingAtValue:,
* or queryEqualToValue:.
*
* @return An FQuery instance, ordered by child value.
*/
- (FQuery *) queryOrderedByValue;
/**
* queryOrderedByPriority: is used to generate a reference to a view of the data that's been sorted by child
* priority. This method is intended to be used in combination with queryStartingAtValue:, queryEndingAtValue:,
* or queryEqualToValue:.
*
* @return An FQuery instance, ordered by child priorities.
*/
- (FQuery *) queryOrderedByPriority;
/**
* queryStartingAtValue: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryStartingAtValue: will respond to events at nodes with a value
* greater than or equal to startValue.
*
* @param startValue The lower bound, inclusive, for the value of data visible to the returned FQuery
* @return An FQuery instance, limited to data with value greater than or equal to startValue
*/
- (FQuery *) queryStartingAtValue:(id)startValue;
/**
* queryStartingAtValue:childKey: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryStartingAtValue:childKey will respond to events at nodes with a value
* greater than startValue, or equal to startValue and with a key greater than or equal to childKey.
*
* @param startValue The lower bound, inclusive, for the value of data visible to the returned FQuery
* @param childKey The lower bound, inclusive, for the key of nodes with value equal to startValue
* @return An FQuery instance, limited to data with value greater than or equal to startValue
*/
- (FQuery *) queryStartingAtValue:(id)startValue childKey:(NSString *)childKey;
/**
* queryEndingAtValue: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryEndingAtValue: will respond to events at nodes with a value
* less than or equal to endValue.
*
* @param endValue The upper bound, inclusive, for the value of data visible to the returned FQuery
* @return An FQuery instance, limited to data with value less than or equal to endValue
*/
- (FQuery *) queryEndingAtValue:(id)endValue;
/**
* queryEndingAtValue:childKey: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryEndingAtValue:childKey will respond to events at nodes with a value
* less than endValue, or equal to endValue and with a key less than or equal to childKey.
*
* @param endValue The upper bound, inclusive, for the value of data visible to the returned FQuery
* @param childKey The upper bound, inclusive, for the key of nodes with value equal to endValue
* @return An FQuery instance, limited to data with value less than or equal to endValue
*/
- (FQuery *) queryEndingAtValue:(id)endValue childKey:(NSString *)childKey;
/**
* queryEqualToValue: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryEqualToValue: will respond to events at nodes with a value equal
* to the supplied argument.
*
* @param value The value that the data returned by this FQuery will have
* @return An Fquery instance, limited to data with the supplied value.
*/
- (FQuery *) queryEqualToValue:(id)value;
/**
* queryEqualToValue:childKey: is used to generate a reference to a limited view of the data at this location.
* The FQuery instance returned by queryEqualToValue:childKey will respond to events at nodes with a value
* equal to the supplied argument with a name equal to childKey. There will be at most one node that matches because
* child keys are unique.
*
* @param value The value that the data returned by this FQuery will have
* @param childKey The name of nodes with the right value
* @return An FQuery instance, limited to data with the supplied value and the key.
*/
- (FQuery *) queryEqualToValue:(id)value childKey:(NSString *)childKey;
/** @name Properties */
/**
* Get a Firebase reference for the location of this query.
*
* @return A Firebase instance for the location of this query.
*/
@property (nonatomic, readonly, strong) Firebase* ref;
@end

View file

@ -0,0 +1,53 @@
/*
* Firebase iOS Client Library
*
* Copyright © 2013 Firebase - All Rights Reserved
* https://www.firebase.com
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binaryform must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY FIREBASE 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 FIREBASE 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 <Foundation/Foundation.h>
#import "FMutableData.h"
/**
* Used for runTransactionBlock:. An FTransactionResult instance is a container for the results of the transaction.
*/
@interface FTransactionResult : NSObject
/**
* Used for runTransactionBlock:. Indicates that the new value should be saved at this location
*
* @param value An FMutableData instance containing the new value to be set
* @return An FTransactionResult instance that can be used as a return value from the block given to runTransactionBlock:
*/
+ (FTransactionResult *) successWithValue:(FMutableData *)value;
/**
* Used for runTransactionBlock:. Indicates that the current transaction should no longer proceed.
*
* @return An FTransactionResult instance that can be used as a return value from the block given to runTransactionBlock:
*/
+ (FTransactionResult *) abort;
@end

View file

@ -0,0 +1,34 @@
#import <Foundation/Foundation.h>
/**
* All Firebase references to the same database share a connection, persistent cache, etc. FirebaseApp
* represents this shared state and can be accessed from any reference via its `app` property.
* It has methods for managing your Firebase connection, etc.
*
* There is a one-to-one relationship between a FirebaseApp instance and a connection to Firebase.
*/
@interface FirebaseApp : NSObject
/**
* The Firebase client automatically queues writes and sends them to the server at the earliest opportunity,
* depending on network connectivity. In some cases (e.g. offline usage) there may be a large number of writes
* waiting to be sent. Calling this method will purge all outstanding writes so they are abandoned.
*
* All writes will be purged, including transactions and {@link Firebase#onDisconnect} writes. The writes will
* be rolled back locally, perhaps triggering events for affected event listeners, and the client will not
* (re-)send them to the Firebase backend.
*/
- (void)purgeOutstandingWrites;
/**
* Shuts down our connection to the Firebase backend until goOnline is called.
*/
- (void)goOffline;
/**
* Resumes our connection to the Firebase backend after a previous goOffline call.
*/
- (void)goOnline;
@end

View file

@ -0,0 +1,5 @@
@interface FirebaseServerValue : NSObject
+ (NSDictionary *) timestamp;
@end

View file

@ -0,0 +1,6 @@
framework module Firebase {
umbrella header "Firebase.h"
export *
module * { export * }
}

View file

@ -0,0 +1,47 @@
Google LevelDB
Copyright (c) 2011 The LevelDB Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
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.
--
Square Socket Rocket
Copyright 2012 Square Inc.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
--
APLevelDB
Created by Adam Preble on 1/23/12.
Copyright (c) 2012 Adam Preble. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,186 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8191" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
</dependencies>
<scenes>
<!--SensorVC-->
<scene sceneID="Mru-rL-xg1">
<objects>
<viewController id="zBM-vU-OIv" customClass="HTSensorVC" customModule="WiGadget" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="0Xa-aj-Q8a"/>
<viewControllerLayoutGuide type="bottom" id="z4J-gF-dn8"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="rSr-31-j5d">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fjl-zL-0VX" customClass="LineChartView" customModule="WiGadget" customModuleProvider="target">
<rect key="frame" x="20" y="216" width="560" height="356"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Temperature" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HIB-eZ-OGe">
<rect key="frame" x="20" y="72" width="560" height="64"/>
<constraints>
<constraint firstAttribute="height" constant="64" id="4Fj-43-DZN"/>
<constraint firstAttribute="height" constant="64" id="MbI-yn-shb"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="default">
<mask key="constraints">
<exclude reference="MbI-yn-shb"/>
</mask>
</variation>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Humidity" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HYX-EI-AUs">
<rect key="frame" x="20" y="144" width="560" height="64"/>
<constraints>
<constraint firstAttribute="height" constant="64" id="EiN-lZ-c9Z"/>
<constraint firstAttribute="height" constant="64" id="Mzo-J4-9ef"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="default">
<mask key="constraints">
<exclude reference="EiN-lZ-c9Z"/>
</mask>
</variation>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="3K9-am-e0u">
<rect key="frame" x="233" y="580" width="134" height="12"/>
<constraints>
<constraint firstAttribute="height" constant="12" id="4wr-Ee-DaH"/>
<constraint firstAttribute="width" constant="256" id="Gm1-jZ-5x8"/>
<constraint firstAttribute="height" constant="12" id="Xqo-Fv-BLB"/>
<constraint firstAttribute="width" secondItem="3K9-am-e0u" secondAttribute="height" multiplier="256:23" id="dRs-08-J8F"/>
<constraint firstAttribute="width" secondItem="3K9-am-e0u" secondAttribute="height" multiplier="256:23" id="jGQ-Gf-doF"/>
<constraint firstAttribute="width" secondItem="3K9-am-e0u" secondAttribute="height" multiplier="64:3" id="lbE-dc-QiZ"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="4wr-Ee-DaH"/>
<exclude reference="Gm1-jZ-5x8"/>
<exclude reference="dRs-08-J8F"/>
<exclude reference="lbE-dc-QiZ"/>
</mask>
</variation>
</imageView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="HIB-eZ-OGe" firstAttribute="leading" secondItem="rSr-31-j5d" secondAttribute="leadingMargin" id="0H7-qC-6My"/>
<constraint firstItem="3K9-am-e0u" firstAttribute="top" secondItem="0Xa-aj-Q8a" secondAttribute="bottom" constant="516" id="15h-nf-170"/>
<constraint firstItem="3K9-am-e0u" firstAttribute="centerX" secondItem="rSr-31-j5d" secondAttribute="centerX" id="2X8-DK-lxi"/>
<constraint firstItem="HIB-eZ-OGe" firstAttribute="trailing" secondItem="rSr-31-j5d" secondAttribute="trailingMargin" id="2cm-gC-PKd"/>
<constraint firstItem="fjl-zL-0VX" firstAttribute="leading" secondItem="rSr-31-j5d" secondAttribute="leadingMargin" id="2hD-cJ-VDC"/>
<constraint firstItem="HYX-EI-AUs" firstAttribute="leading" secondItem="rSr-31-j5d" secondAttribute="leadingMargin" id="3zL-Gz-Na5"/>
<constraint firstItem="HYX-EI-AUs" firstAttribute="top" secondItem="HIB-eZ-OGe" secondAttribute="bottom" constant="8" symbolic="YES" id="4ZT-G4-LDd"/>
<constraint firstItem="HIB-eZ-OGe" firstAttribute="top" secondItem="0Xa-aj-Q8a" secondAttribute="bottom" constant="8" symbolic="YES" id="5BY-RR-pLl"/>
<constraint firstItem="fjl-zL-0VX" firstAttribute="trailing" secondItem="rSr-31-j5d" secondAttribute="trailingMargin" id="8fk-iz-1mM"/>
<constraint firstItem="HYX-EI-AUs" firstAttribute="top" secondItem="HIB-eZ-OGe" secondAttribute="bottom" constant="8" symbolic="YES" id="9Yc-Ga-4Q3"/>
<constraint firstItem="HYX-EI-AUs" firstAttribute="top" secondItem="HIB-eZ-OGe" secondAttribute="bottom" constant="8" id="9ZH-Zv-SP7"/>
<constraint firstItem="z4J-gF-dn8" firstAttribute="top" secondItem="3K9-am-e0u" secondAttribute="bottom" constant="8" symbolic="YES" id="BvD-Zc-ubP"/>
<constraint firstItem="fjl-zL-0VX" firstAttribute="trailing" secondItem="rSr-31-j5d" secondAttribute="trailingMargin" id="Df8-OE-1vN"/>
<constraint firstItem="3K9-am-e0u" firstAttribute="top" secondItem="fjl-zL-0VX" secondAttribute="bottom" constant="8" symbolic="YES" id="HHL-yV-nta"/>
<constraint firstItem="HYX-EI-AUs" firstAttribute="leading" secondItem="rSr-31-j5d" secondAttribute="leadingMargin" id="J3h-8F-J6w"/>
<constraint firstItem="z4J-gF-dn8" firstAttribute="top" secondItem="3K9-am-e0u" secondAttribute="bottom" constant="8" symbolic="YES" id="KcO-wX-dE3"/>
<constraint firstItem="fjl-zL-0VX" firstAttribute="top" secondItem="0Xa-aj-Q8a" secondAttribute="bottom" constant="8" symbolic="YES" id="LB3-QF-JSM"/>
<constraint firstItem="HIB-eZ-OGe" firstAttribute="height" secondItem="HYX-EI-AUs" secondAttribute="height" id="NOQ-YM-rlu"/>
<constraint firstItem="fjl-zL-0VX" firstAttribute="leading" secondItem="rSr-31-j5d" secondAttribute="leadingMargin" id="Nwo-xb-KM1"/>
<constraint firstItem="3K9-am-e0u" firstAttribute="centerX" secondItem="fjl-zL-0VX" secondAttribute="centerX" id="Q9p-h8-K6F"/>
<constraint firstAttribute="centerX" secondItem="3K9-am-e0u" secondAttribute="centerX" id="QYz-O9-fgn"/>
<constraint firstItem="HIB-eZ-OGe" firstAttribute="top" secondItem="0Xa-aj-Q8a" secondAttribute="bottom" constant="8" symbolic="YES" id="R8n-BT-dnc"/>
<constraint firstItem="fjl-zL-0VX" firstAttribute="top" secondItem="HYX-EI-AUs" secondAttribute="bottom" constant="8" symbolic="YES" id="W8O-PY-5pA"/>
<constraint firstItem="3K9-am-e0u" firstAttribute="top" secondItem="fjl-zL-0VX" secondAttribute="bottom" constant="8" symbolic="YES" id="ZxJ-xM-AhK"/>
<constraint firstAttribute="trailingMargin" secondItem="HYX-EI-AUs" secondAttribute="trailing" id="cLX-fs-54T"/>
<constraint firstItem="3K9-am-e0u" firstAttribute="top" secondItem="0Xa-aj-Q8a" secondAttribute="bottom" constant="516" id="gWG-eV-5Mu"/>
<constraint firstItem="HYX-EI-AUs" firstAttribute="trailing" secondItem="rSr-31-j5d" secondAttribute="trailingMargin" id="h9K-Jq-KUm"/>
<constraint firstItem="fjl-zL-0VX" firstAttribute="top" secondItem="HYX-EI-AUs" secondAttribute="bottom" constant="8" symbolic="YES" id="iUZ-Nx-eIp"/>
<constraint firstItem="HIB-eZ-OGe" firstAttribute="leading" secondItem="rSr-31-j5d" secondAttribute="leadingMargin" id="nk4-Uk-L32"/>
<constraint firstItem="3K9-am-e0u" firstAttribute="trailing" secondItem="rSr-31-j5d" secondAttribute="trailingMargin" constant="-220" id="nwl-0Y-gQV"/>
<constraint firstItem="z4J-gF-dn8" firstAttribute="top" secondItem="3K9-am-e0u" secondAttribute="bottom" constant="8" symbolic="YES" id="qFP-0l-qZR"/>
<constraint firstItem="z4J-gF-dn8" firstAttribute="top" secondItem="3K9-am-e0u" secondAttribute="bottom" constant="8" symbolic="YES" id="tc5-0I-ndJ"/>
<constraint firstItem="3K9-am-e0u" firstAttribute="centerX" secondItem="fjl-zL-0VX" secondAttribute="centerX" id="vFM-Yv-nZb"/>
<constraint firstItem="HIB-eZ-OGe" firstAttribute="trailing" secondItem="rSr-31-j5d" secondAttribute="trailingMargin" id="wGt-Uu-HY3"/>
<constraint firstAttribute="centerX" secondItem="3K9-am-e0u" secondAttribute="centerX" id="wqT-Cm-4lN"/>
<constraint firstItem="HIB-eZ-OGe" firstAttribute="width" secondItem="HYX-EI-AUs" secondAttribute="width" id="x0v-9D-l49"/>
<constraint firstItem="3K9-am-e0u" firstAttribute="leading" secondItem="rSr-31-j5d" secondAttribute="leadingMargin" constant="220" id="yVN-CP-K0w"/>
<constraint firstItem="3K9-am-e0u" firstAttribute="top" secondItem="fjl-zL-0VX" secondAttribute="bottom" constant="8" symbolic="YES" id="zWK-Ys-WgR"/>
<constraint firstItem="HYX-EI-AUs" firstAttribute="leading" secondItem="rSr-31-j5d" secondAttribute="leadingMargin" id="zz3-ra-dE5"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="0H7-qC-6My"/>
<exclude reference="2cm-gC-PKd"/>
<exclude reference="NOQ-YM-rlu"/>
<exclude reference="R8n-BT-dnc"/>
<exclude reference="x0v-9D-l49"/>
<exclude reference="3zL-Gz-Na5"/>
<exclude reference="9Yc-Ga-4Q3"/>
<exclude reference="9ZH-Zv-SP7"/>
<exclude reference="J3h-8F-J6w"/>
<exclude reference="cLX-fs-54T"/>
<exclude reference="Df8-OE-1vN"/>
<exclude reference="LB3-QF-JSM"/>
<exclude reference="Nwo-xb-KM1"/>
<exclude reference="iUZ-Nx-eIp"/>
<exclude reference="BvD-Zc-ubP"/>
<exclude reference="KcO-wX-dE3"/>
<exclude reference="qFP-0l-qZR"/>
<exclude reference="15h-nf-170"/>
<exclude reference="2X8-DK-lxi"/>
<exclude reference="HHL-yV-nta"/>
<exclude reference="Q9p-h8-K6F"/>
<exclude reference="gWG-eV-5Mu"/>
<exclude reference="nwl-0Y-gQV"/>
<exclude reference="vFM-Yv-nZb"/>
<exclude reference="wqT-Cm-4lN"/>
<exclude reference="yVN-CP-K0w"/>
<exclude reference="zWK-Ys-WgR"/>
</mask>
</variation>
</view>
<navigationItem key="navigationItem" id="rWo-jq-rEr">
<barButtonItem key="leftBarButtonItem" systemItem="done" id="2nt-yX-289">
<connections>
<action selector="closeVC:" destination="zBM-vU-OIv" id="Kgy-XF-AuI"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="chartView" destination="fjl-zL-0VX" id="Su6-uW-CtT"/>
<outlet property="humidityLabel" destination="HYX-EI-AUs" id="Qjx-9A-ctV"/>
<outlet property="imageView" destination="3K9-am-e0u" id="0dW-mi-6dQ"/>
<outlet property="temperatureLabel" destination="HIB-eZ-OGe" id="BVZ-yb-Xes"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Ydg-JY-kcG" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1358" y="295"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="OHj-NU-0rL">
<objects>
<navigationController storyboardIdentifier="ht-story-board" automaticallyAdjustsScrollViewInsets="NO" id="vvy-Bd-KII" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="5q0-IY-KMw">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="zBM-vU-OIv" kind="relationship" relationship="rootViewController" id="Ml2-34-MIx"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="xCq-f6-stS" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="546" y="295"/>
</scene>
</scenes>
</document>

View file

@ -0,0 +1,331 @@
//
// HTSensorVC.swift
// WiGadget
//
// Created by WU JINZHOU on 2/9/15.
// Copyright (c) 2015 WU JINZHOU. All rights reserved.
//
import UIKit
import Firebase
import AVFoundation
//ameba in tracking list is like:
/*
g0,131,18;* name: ht_sensor ip: 172.25.23.91 mac: 00e04c870000 port: 6866 control_type: 1 pair_state: 1 link_state: 0 firebase_app_id: description: key: [;
*/
//ameba in saved list is like:
/*
g0,131,18;* name: ht_sensor ip: mac: 00e04c870000 port: control_type: 1 pair_state: 1 link_state: 1 firebase_app_id: xxxx description: [NEW]-ht_sensor key: [223, 97, 174, 104, 227, 235, 251, 119, 103, 99, 38, 112, 4, 174, 16, 204][;
*/
//i.e: ip & port are from tracked ameba and key is from saved ameba
class HTSensorVC: UIViewController {
let targetAmeba = AmebaList.linkTarget! //targetAmeba is retrived from saved list
var ip = R.not_assigned
var port = R.not_found
var key = R.not_assigned
var firebaseURL = R.not_assigned
var timer = NSTimer()
var rootRef = Firebase()
struct Reading {
var humidity:Double
var temperature:Double
}
var readings = [Reading]()
var times = [String]()
@IBOutlet weak var temperatureLabel: UILabel!
@IBOutlet weak var humidityLabel: UILabel!
@IBOutlet weak var chartView: LineChartView!
@IBOutlet weak var imageView: UIImageView!
var preloaderImageData = NSData(contentsOfURL: NSBundle.mainBundle().URLForResource("preloader_256x23", withExtension: "gif")!)
var useAlarm = false
var alarmSound = 0 //mapped to sound 1
var humiThres = 0.0
var tempThres = 0.0
var linkFreq = 2.0
var player = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
self.title = targetAmeba.description
imageView.image = UIImage.animatedImageWithAnimatedGIFData(preloaderImageData)
loadSettings()
initChart()
}
override func viewDidAppear(animated: Bool) {
if let trackedAmeba = AmebaList.trackingListGetAmebaByMac(targetAmeba.mac) {
ip = trackedAmeba.ip
port = Int(trackedAmeba.port)!
key = targetAmeba.key
timer = NSTimer.scheduledTimerWithTimeInterval(linkFreq, target: self, selector: "localLink", userInfo: nil, repeats: true)
}
else if targetAmeba.control_type == R.cloud_control {
firebaseURL = "https://" + targetAmeba.firebase_app_id + ".firebaseio.com"
rootRef = Firebase(url: firebaseURL)
cloudLink()
}
}
func loadSettings() {
let defaults = NSUserDefaults.standardUserDefaults()
let dataStr = defaults.valueForKey(R.settings_key_ht) as? String ?? R.not_assigned
let htSettings = JSON(data: dataStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!)
if !(htSettings.isEmpty) {
useAlarm = htSettings[R.settings_key_ht_enable_alarm].boolValue
alarmSound = htSettings[R.settings_key_ht_alarm_sound].intValue
humiThres = htSettings[R.settings_key_ht_humi_thres].doubleValue
tempThres = htSettings[R.settings_key_ht_temp_thres].doubleValue
linkFreq = htSettings[R.settings_key_ht_link_freq].doubleValue as NSTimeInterval
}
}
func initChart() {
chartView.clearsContextBeforeDrawing = true
chartView.descriptionText = R.ht_chart_description
chartView.noDataText = R.ht_chart_no_data_text
chartView.highlightEnabled = true
chartView.dragEnabled = false
chartView.setScaleEnabled(false)
chartView.drawGridBackgroundEnabled = true
chartView.pinchZoomEnabled = false
chartView.legend.font = R.ht_chart_legend_font
chartView.legend.textColor = R.ht_chart_legend_text_color
chartView.gridBackgroundColor = R.ht_chart_grid_background_color
let xAxis = chartView.xAxis
xAxis.labelFont = R.ht_chart_legend_font
xAxis.labelTextColor = R.ht_chart_legend_text_color
xAxis.drawGridLinesEnabled = false
xAxis.drawAxisLineEnabled = false
xAxis.spaceBetweenLabels = R.ht_chart_space_betweenLabels
xAxis.drawGridLinesEnabled = true
xAxis.labelPosition = .Bottom
}
func handleAlarm(temp temp:Double, humi:Double, soundIdx:Int) {
if useAlarm {
if (temp >= tempThres) || (humi >= humiThres) {
let path = NSBundle.mainBundle().pathForResource(R.settings_ht_alarm_sounds[soundIdx], ofType: "wav")
try! player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: path!))
player.prepareToPlay()
player.play()
}
}
}
func cloudLink() {
rootRef.observeEventType(.Value, withBlock: {
snapshot in
Log.i("cloudLink received:\n\(snapshot.value)")
let tem = (((snapshot.value[R.ht_sensor] as! NSDictionary)[self.targetAmeba.mac] as! NSDictionary)[R.ht_key_temp] as! NSString).doubleValue
let hum = (((snapshot.value[R.ht_sensor] as! NSDictionary)[self.targetAmeba.mac] as! NSDictionary)[R.ht_key_humi] as! NSString).doubleValue
let reading = Reading(humidity: hum, temperature: tem)
self.pushData(reading)
self.handleAlarm(temp: tem, humi: hum, soundIdx: self.alarmSound)
}, withCancelBlock: {
error in
Log.e(error.description)
})
}
func localLink() {
let qos = Int(QOS_CLASS_USER_INITIATED.rawValue)
let mainQ = dispatch_get_main_queue()
let sockQ = dispatch_get_global_queue(qos, 0)
var dataStr = R.not_assigned
var needPushData = false
dispatch_async(sockQ){
//off-UI Q
let client = TCPClient(addr: self.ip, port: self.port)
var (success,errmsg) = client.connect(timeout: R.tcp_connection_time_out)
if !success {
Log.e(errmsg)
}
else
{
let txEnc = Crypto.encrypt(R.tcp_tx_request, key: self.key)
(success,errmsg) = client.send(str: txEnc)
if !success {
Log.e(errmsg)
}
else
{
if let rxDataEnc = client.read(R.tcp_expect_rx_data_length) {
let rxStrEnc = String(bytes: rxDataEnc, encoding: NSUTF8StringEncoding)!
dataStr = Crypto.decrypt(rxStrEnc, key: self.key)
if dataStr == R.not_assigned {
Log.e("tcp_rx_nothing, maybe use wrong key or send unknown cmd")
}
else{
Log.i("localLink received:\n\(dataStr)")
needPushData = true
}
}
else{
Log.e("loca_link_rx_nil, read data from server failed")
}
}
}
client.close()
dispatch_async(mainQ)
{
//main Q
if needPushData {
let json = JSON(data: dataStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!)
let humi = (json[R.ht_key_humi].stringValue as NSString).doubleValue
let temp = (json[R.ht_key_temp].stringValue as NSString).doubleValue
let reading = Reading(humidity: humi, temperature: temp)
self.pushData(reading)
self.handleAlarm(temp: temp, humi: humi, soundIdx: self.alarmSound)
}
}
}
}
@IBAction func closeVC(sender: AnyObject) {
//...close any link connection
rootRef.removeAllObservers()
timer.invalidate()
self.dismissViewControllerAnimated(true, completion: nil);
}
func pushData(reading:Reading) {
let time = systemTime()
Log.v("pushData: hum: \(reading.humidity) tem: \(reading.temperature) time: \(time)")
readings.append(reading)
times.append(time)
while (readings.count > R.ht_readings_to_keep_in_buffer) && (readings.count != 0) {
readings.removeAtIndex(0)
times.removeAtIndex(0)
}
plotChart()
temperatureLabel.text = "Temperature: \(reading.temperature)"
humidityLabel.text = "Humidity: \(reading.humidity)"
}
func systemTime() -> String {
let date = NSDate()
let formatter = NSDateFormatter()
formatter.timeZone = NSTimeZone.localTimeZone()
formatter.dateFormat = R.ht_chart_time_format
return formatter.stringFromDate(date)
}
func plotChart() {
let humi = readings.map({$0.humidity})
let temp = readings.map({$0.temperature})
var ht = (humi + temp)
ht.sortInPlace(<)
let leftAxis = chartView.leftAxis
leftAxis.labelTextColor = R.ht_chart_color_green_H
leftAxis.customAxisMin = floor(ht.first! - (abs(ht.first!) * 0.1))
leftAxis.customAxisMax = ceil(ht.last! + (abs(ht.last!) * 0.1))
leftAxis.drawGridLinesEnabled = true
leftAxis.startAtZeroEnabled = false
let rightAxis = chartView.rightAxis
rightAxis.labelTextColor = R.ht_chart_color_blue_T
rightAxis.customAxisMin = floor(ht.first! - (abs(ht.first!) * 0.1))
rightAxis.customAxisMax = ceil(ht.last! + (abs(ht.last!) * 0.1))
rightAxis.drawGridLinesEnabled = true
rightAxis.startAtZeroEnabled = false
var hEntry = [ChartDataEntry]()
for i in 0 ..< readings.count {
let dataEntry = ChartDataEntry(value: humi[i], xIndex: i)
hEntry.append(dataEntry)
}
let hSet = LineChartDataSet(yVals: hEntry, label: "Humidity: " + String(format: "%.2f", humi.last!))
hSet.lineWidth = R.ht_chart_line_width
hSet.circleRadius = R.ht_chart_circle_radius
hSet.fillAlpha = R.ht_chart_fill_alpha
hSet.setColor(R.ht_chart_color_green_H)
hSet.fillColor = R.ht_chart_color_green_H
hSet.circleColors = [R.ht_chart_color_green_H]
var tEntry = [ChartDataEntry]()
for i in 0 ..< readings.count {
let dataEntry = ChartDataEntry(value: temp[i], xIndex: i)
tEntry.append(dataEntry)
}
let tSet = LineChartDataSet(yVals: tEntry, label: "Temperature: " + String(format: "%.2f", temp.last!))
tSet.lineWidth = R.ht_chart_line_width
tSet.circleRadius = R.ht_chart_circle_radius
tSet.fillAlpha = R.ht_chart_fill_alpha
tSet.setColor(R.ht_chart_color_blue_T)
tSet.fillColor = R.ht_chart_color_blue_T
tSet.circleColors = [R.ht_chart_color_blue_T]
var dataSets = [LineChartDataSet]()
dataSets.append(hSet)
dataSets.append(tSet)
let data = LineChartData(xVals: times, dataSets: dataSets)
chartView.data = data
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,121 @@
{
"images" : [
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "AppIcon29x29.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "AppIcon29x29@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "AppIcon40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "AppIcon40x40@3x.png",
"scale" : "3x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "AppIcon57x57.png",
"scale" : "1x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "AppIcon57x57@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "AppIcon60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "AppIcon60x60@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "AppIcon29x29~ipad.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "AppIcon29x29@2x~ipad.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "AppIcon40x40~ipad.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "AppIcon40x40@2x~ipad.png",
"scale" : "2x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "AppIcon50x50.png",
"scale" : "1x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "AppIcon50x50@2x.png",
"scale" : "2x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "AppIcon72x72~ipad.png",
"scale" : "1x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "AppIcon72x72@2x~ipad.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "AppIcon76x76~ipad.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "AppIcon76x76@2x~ipad.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View file

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "app_banner_441x72.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "color_label_bg.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "ic_about_72x72.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "ic_device_ht_sensor_cloud_offline_128x128.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "ic_device_ht_sensor_cloud_online_128x128.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "ic_device_ht_sensor_local_offline_128x128.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "ic_device_ht_sensor_local_online_128x128.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "ic_failed_512x513.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "ic_find_device_72x72.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "ic_ht_56x56.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_launcher_676x1054.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "ic_my_device_72x72.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "ic_selected_55x55.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "ic_settings_72x72.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "ic_succeed_512x513.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View file

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "menu.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "menu@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "menu@3x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View file

@ -0,0 +1,956 @@
//
// Just.swift
// Just
//
// Created by Daniel Duan on 4/21/15.
// Copyright (c) 2015 JustHTTP. All rights reserved.
//
import Foundation
// stolen from python-requests
let statusCodeDescriptions = [
// Informational.
100: "continue" , 101: "switching protocols" , 102: "processing" ,
103: "checkpoint" , 122: "uri too long" , 200: "ok" ,
201: "created" , 202: "accepted" , 203: "non authoritative info" ,
204: "no content" , 205: "reset content" , 206: "partial content" ,
207: "multi status" , 208: "already reported" , 226: "im used" ,
// Redirection.
300: "multiple choices" , 301: "moved permanently" , 302: "found" ,
303: "see other" , 304: "not modified" , 305: "use proxy" ,
306: "switch proxy" , 307: "temporary redirect" , 308: "permanent redirect" ,
// Client Error.
400: "bad request" , 401: "unauthorized" , 402: "payment required" ,
403: "forbidden" , 404: "not found" , 405: "method not allowed" ,
406: "not acceptable" , 407: "proxy authentication required" , 408: "request timeout" ,
409: "conflict" , 410: "gone" , 411: "length required" ,
412: "precondition failed" , 413: "request entity too large" , 414: "request uri too large" ,
415: "unsupported media type" , 416: "requested range not satisfiable" , 417: "expectation failed" ,
418: "im a teapot" , 422: "unprocessable entity" , 423: "locked" ,
424: "failed dependency" , 425: "unordered collection" , 426: "upgrade required" ,
428: "precondition required" , 429: "too many requests" , 431: "header fields too large" ,
444: "no response" , 449: "retry with" , 450: "blocked by windows parental controls" ,
451: "unavailable for legal reasons" , 499: "client closed request" ,
// Server Error.
500: "internal server error" , 501: "not implemented" , 502: "bad gateway" ,
503: "service unavailable" , 504: "gateway timeout" , 505: "http version not supported" ,
506: "variant also negotiates" , 507: "insufficient storage" , 509: "bandwidth limit exceeded" ,
510: "not extended" ,
]
public enum HTTPFile {
case URL(NSURL,String?) // URL to a file, mimetype
case Data(String,NSData,String?) // filename, data, mimetype
case Text(String,String,String?) // filename, text, mimetype
}
// Supported request types
public enum HTTPMethod: String {
case DELETE = "DELETE"
case GET = "GET"
case HEAD = "HEAD"
case OPTIONS = "OPTIONS"
case PATCH = "PATCH"
case POST = "POST"
case PUT = "PUT"
}
/// The only reason this is not a struct is the requirements for
/// lazy evaluation of `headers` and `cookies`, which is mutating the
/// struct. This would make those properties unusable with `HTTPResult`s
/// declared with `let`
public final class HTTPResult : NSObject {
public final var content:NSData?
public var response:NSURLResponse?
public var error:NSError?
public var request:NSURLRequest?
public var encoding = NSUTF8StringEncoding
public var JSONReadingOptions = NSJSONReadingOptions(rawValue: 0)
public var reason:String {
if let code = self.statusCode,
let text = statusCodeDescriptions[code] {
return text
}
if let error = self.error {
return error.localizedDescription
}
return "Unkown"
}
public var isRedirect:Bool {
if let code = self.statusCode {
return code >= 300 && code < 400
}
return false
}
public var isPermanentRedirect:Bool {
return self.statusCode == 301
}
public override var description:String {
if let status = statusCode,
urlString = request?.URL?.absoluteString,
method = request?.HTTPMethod
{
return "\(method) \(urlString) \(status)"
} else {
return "<Empty>"
}
}
public init(data:NSData?, response:NSURLResponse?, error:NSError?, request:NSURLRequest?) {
self.content = data
self.response = response
self.error = error
self.request = request
}
public var json:AnyObject? {
if let theData = self.content {
return try? NSJSONSerialization.JSONObjectWithData(theData, options: JSONReadingOptions)
}
return nil
}
public var statusCode: Int? {
if let theResponse = self.response as? NSHTTPURLResponse {
return theResponse.statusCode
}
return nil
}
public var text:String? {
if let theData = self.content {
return NSString(data:theData, encoding:encoding) as? String
}
return nil
}
public lazy var headers:CaseInsensitiveDictionary<String,String> = {
return CaseInsensitiveDictionary<String,String>(dictionary: (self.response as? NSHTTPURLResponse)?.allHeaderFields as? [String:String] ?? [:])
}()
public lazy var cookies:[String:NSHTTPCookie] = {
let foundCookies: [NSHTTPCookie]
if let responseHeaders = (self.response as? NSHTTPURLResponse)?.allHeaderFields as? [String: String] {
foundCookies = NSHTTPCookie.cookiesWithResponseHeaderFields(responseHeaders, forURL:NSURL(string:"")!) as [NSHTTPCookie]
} else {
foundCookies = []
}
var result:[String:NSHTTPCookie] = [:]
for cookie in foundCookies {
result[cookie.name] = cookie
}
return result
}()
public var ok:Bool {
return statusCode != nil && !(statusCode! >= 400 && statusCode! < 600)
}
public var url:NSURL? {
return response?.URL
}
public lazy var links: [String:[String:String]] = {
var result = [String:[String:String]]()
if let content = self.headers["link"] {
content.componentsSeparatedByString(",").forEach { s in
let linkComponents = s.componentsSeparatedByString(";").map { ($0 as NSString).stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) }
if linkComponents.count > 1 { // although a link without a rel is valid, there's no way to reference it
let urlComponent = linkComponents.first!
let urlRange = urlComponent.startIndex.advancedBy(1)..<urlComponent.endIndex.advancedBy(-1)
var link: [String: String] = ["url": String(urlComponent.characters[urlRange])]
linkComponents.dropFirst().forEach { s in
if let equalIndex = s.characters.indexOf("=") {
let componentKey = String(s.characters[s.startIndex..<equalIndex])
let componentValueCharacters = s.characters[equalIndex.advancedBy(1)..<s.endIndex]
if componentValueCharacters.first == "\"" && componentValueCharacters.last == "\"" {
let unquotedValueRange = componentValueCharacters.startIndex.advancedBy(1)..<componentValueCharacters.endIndex.advancedBy(-1)
link[componentKey] = String(componentValueCharacters[unquotedValueRange])
} else {
link[componentKey] = String(componentValueCharacters)
}
}
}
if let rel = link["rel"] {
result[rel] = link
}
}
}
}
return result
}()
}
public struct CaseInsensitiveDictionary<Key: Hashable, Value>: CollectionType, DictionaryLiteralConvertible {
private var _data:[Key: Value] = [:]
private var _keyMap: [String: Key] = [:]
public typealias Element = (Key, Value)
public typealias Index = DictionaryIndex<Key, Value>
public var startIndex: Index
public var endIndex: Index
public var count: Int {
assert(_data.count == _keyMap.count, "internal keys out of sync")
return _data.count
}
public var isEmpty: Bool {
return _data.isEmpty
}
public init() {
startIndex = _data.startIndex
endIndex = _data.endIndex
}
public init(dictionaryLiteral elements: (Key, Value)...) {
for (key, value) in elements {
_keyMap["\(key)".lowercaseString] = key
_data[key] = value
}
startIndex = _data.startIndex
endIndex = _data.endIndex
}
public init(dictionary:[Key:Value]) {
for (key, value) in dictionary {
_keyMap["\(key)".lowercaseString] = key
_data[key] = value
}
startIndex = _data.startIndex
endIndex = _data.endIndex
}
public subscript (position: Index) -> Element {
return _data[position]
}
public subscript (key: Key) -> Value? {
get {
if let realKey = _keyMap["\(key)".lowercaseString] {
return _data[realKey]
}
return nil
}
set(newValue) {
let lowerKey = "\(key)".lowercaseString
if _keyMap[lowerKey] == nil {
_keyMap[lowerKey] = key
}
_data[_keyMap[lowerKey]!] = newValue
}
}
public func generate() -> DictionaryGenerator<Key, Value> {
return _data.generate()
}
public var keys: LazyMapCollection<[Key : Value], Key> {
return _data.keys
}
public var values: LazyMapCollection<[Key : Value], Value> {
return _data.values
}
}
typealias TaskID = Int
public typealias Credentials = (username:String, password:String)
public typealias TaskProgressHandler = (HTTPProgress!) -> Void
typealias TaskCompletionHandler = (HTTPResult) -> Void
struct TaskConfiguration {
let credential:Credentials?
let redirects:Bool
let originalRequest: NSURLRequest?
var data: NSMutableData
let progressHandler: TaskProgressHandler?
let completionHandler: TaskCompletionHandler?
}
public struct JustSessionDefaults {
public var JSONReadingOptions = NSJSONReadingOptions(rawValue: 0)
public var JSONWritingOptions = NSJSONWritingOptions(rawValue: 0)
public var headers:[String:String] = [:]
public var multipartBoundary = "Ju5tH77P15Aw350m3"
public var encoding = NSUTF8StringEncoding
}
public struct HTTPProgress {
public enum Type {
case Upload
case Download
}
public let type:Type
public let bytesProcessed:Int64
public let bytesExpectedToProcess:Int64
public var percent: Float {
return Float(bytesProcessed) / Float(bytesExpectedToProcess)
}
}
let errorDomain = "net.justhttp.Just"
public protocol JustAdaptor {
func request(
method:HTTPMethod,
URLString:String,
params:[String:AnyObject],
data:[String:AnyObject],
json:[String:AnyObject]?,
headers:[String:String],
files:[String:HTTPFile],
auth:Credentials?,
cookies: [String:String],
redirects:Bool,
timeout:Double?,
URLQuery:String?,
requestBody:NSData?,
asyncProgressHandler:TaskProgressHandler?,
asyncCompletionHandler:((HTTPResult!) -> Void)?) -> HTTPResult
init(session:NSURLSession?, defaults:JustSessionDefaults?)
}
public struct JustOf<Adaptor: JustAdaptor> {
private let adaptor: Adaptor
public init(session:NSURLSession? = nil, defaults:JustSessionDefaults? = nil) {
adaptor = Adaptor(session: session, defaults: defaults)
}
}
extension JustOf {
public func delete(
URLString:String,
params:[String:AnyObject] = [:],
data:[String:AnyObject] = [:],
json:[String:AnyObject]? = nil,
headers:[String:String] = [:],
files:[String:HTTPFile] = [:],
auth:(String,String)? = nil,
cookies:[String:String] = [:],
allowRedirects:Bool = true,
timeout:Double? = nil,
URLQuery:String? = nil,
requestBody:NSData? = nil,
asyncProgressHandler:((HTTPProgress!) -> Void)? = nil,
asyncCompletionHandler:((HTTPResult!) -> Void)? = nil
) -> HTTPResult {
return adaptor.request(
.DELETE,
URLString: URLString,
params: params,
data: data,
json: json,
headers: headers,
files:files,
auth: auth,
cookies: cookies,
redirects: allowRedirects,
timeout:timeout,
URLQuery: URLQuery,
requestBody: requestBody,
asyncProgressHandler: asyncProgressHandler,
asyncCompletionHandler: asyncCompletionHandler
)
}
public func get(
URLString:String,
params:[String:AnyObject] = [:],
data:[String:AnyObject] = [:],
json:[String:AnyObject]? = nil,
headers:[String:String] = [:],
files:[String:HTTPFile] = [:],
auth:(String,String)? = nil,
allowRedirects:Bool = true,
cookies:[String:String] = [:],
timeout:Double? = nil,
requestBody:NSData? = nil,
URLQuery:String? = nil,
asyncProgressHandler:((HTTPProgress!) -> Void)? = nil,
asyncCompletionHandler:((HTTPResult!) -> Void)? = nil
) -> HTTPResult {
return adaptor.request(
.GET,
URLString: URLString,
params: params,
data: data,
json: json,
headers: headers,
files:files,
auth: auth,
cookies: cookies,
redirects: allowRedirects,
timeout:timeout,
URLQuery: URLQuery,
requestBody: requestBody,
asyncProgressHandler: asyncProgressHandler,
asyncCompletionHandler: asyncCompletionHandler
)
}
public func head(
URLString:String,
params:[String:AnyObject] = [:],
data:[String:AnyObject] = [:],
json:[String:AnyObject]? = nil,
headers:[String:String] = [:],
files:[String:HTTPFile] = [:],
auth:(String,String)? = nil,
cookies:[String:String] = [:],
allowRedirects:Bool = true,
timeout:Double? = nil,
requestBody:NSData? = nil,
URLQuery:String? = nil,
asyncProgressHandler:((HTTPProgress!) -> Void)? = nil,
asyncCompletionHandler:((HTTPResult!) -> Void)? = nil
) -> HTTPResult {
return adaptor.request(
.HEAD,
URLString: URLString,
params: params,
data: data,
json: json,
headers: headers,
files:files,
auth: auth,
cookies: cookies,
redirects: allowRedirects,
timeout: timeout,
URLQuery: URLQuery,
requestBody: requestBody,
asyncProgressHandler: asyncProgressHandler,
asyncCompletionHandler: asyncCompletionHandler
)
}
public func options(
URLString:String,
params:[String:AnyObject] = [:],
data:[String:AnyObject] = [:],
json:[String:AnyObject]? = nil,
headers:[String:String] = [:],
files:[String:HTTPFile] = [:],
auth:(String,String)? = nil,
cookies:[String:String] = [:],
allowRedirects:Bool = true,
timeout:Double? = nil,
requestBody:NSData? = nil,
URLQuery:String? = nil,
asyncProgressHandler:((HTTPProgress!) -> Void)? = nil,
asyncCompletionHandler:((HTTPResult!) -> Void)? = nil
) -> HTTPResult {
return adaptor.request(
.OPTIONS,
URLString: URLString,
params: params,
data: data,
json: json,
headers: headers,
files:files,
auth: auth,
cookies: cookies,
redirects: allowRedirects,
timeout: timeout,
URLQuery: URLQuery,
requestBody: requestBody,
asyncProgressHandler: asyncProgressHandler,
asyncCompletionHandler: asyncCompletionHandler
)
}
public func patch(
URLString:String,
params:[String:AnyObject] = [:],
data:[String:AnyObject] = [:],
json:[String:AnyObject]? = nil,
headers:[String:String] = [:],
files:[String:HTTPFile] = [:],
auth:(String,String)? = nil,
cookies:[String:String] = [:],
allowRedirects:Bool = true,
timeout:Double? = nil,
requestBody:NSData? = nil,
URLQuery:String? = nil,
asyncProgressHandler:((HTTPProgress!) -> Void)? = nil,
asyncCompletionHandler:((HTTPResult!) -> Void)? = nil
) -> HTTPResult {
return adaptor.request(
.OPTIONS,
URLString: URLString,
params: params,
data: data,
json: json,
headers: headers,
files:files,
auth: auth,
cookies: cookies,
redirects: allowRedirects,
timeout: timeout,
URLQuery: URLQuery,
requestBody: requestBody,
asyncProgressHandler: asyncProgressHandler,
asyncCompletionHandler: asyncCompletionHandler
)
}
public func post(
URLString:String,
params:[String:AnyObject] = [:],
data:[String:AnyObject] = [:],
json:[String:AnyObject]? = nil,
headers:[String:String] = [:],
files:[String:HTTPFile] = [:],
auth:(String,String)? = nil,
cookies:[String:String] = [:],
allowRedirects:Bool = true,
timeout:Double? = nil,
requestBody:NSData? = nil,
URLQuery:String? = nil,
asyncProgressHandler:((HTTPProgress!) -> Void)? = nil,
asyncCompletionHandler:((HTTPResult!) -> Void)? = nil
) -> HTTPResult {
return adaptor.request(
.POST,
URLString: URLString,
params: params,
data: data,
json: json,
headers: headers,
files:files,
auth: auth,
cookies: cookies,
redirects: allowRedirects,
timeout: timeout,
URLQuery: URLQuery,
requestBody: requestBody,
asyncProgressHandler: asyncProgressHandler,
asyncCompletionHandler: asyncCompletionHandler
)
}
public func put(
URLString:String,
params:[String:AnyObject] = [:],
data:[String:AnyObject] = [:],
json:[String:AnyObject]? = nil,
headers:[String:String] = [:],
files:[String:HTTPFile] = [:],
auth:(String,String)? = nil,
cookies:[String:String] = [:],
allowRedirects:Bool = true,
timeout:Double? = nil,
requestBody:NSData? = nil,
URLQuery:String? = nil,
asyncProgressHandler:((HTTPProgress!) -> Void)? = nil,
asyncCompletionHandler:((HTTPResult!) -> Void)? = nil
) -> HTTPResult {
return adaptor.request(
.PUT,
URLString: URLString,
params: params,
data: data,
json: json,
headers: headers,
files:files,
auth: auth,
cookies: cookies,
redirects: allowRedirects,
timeout: timeout,
URLQuery: URLQuery,
requestBody: requestBody,
asyncProgressHandler: asyncProgressHandler,
asyncCompletionHandler: asyncCompletionHandler
)
}
}
public final class HTTP: NSObject, NSURLSessionDelegate, JustAdaptor {
public init(session:NSURLSession? = nil, defaults:JustSessionDefaults? = nil) {
super.init()
if let initialSession = session {
self.session = initialSession
} else {
self.session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate:self, delegateQueue:nil)
}
if let initialDefaults = defaults {
self.defaults = initialDefaults
} else {
self.defaults = JustSessionDefaults()
}
}
var taskConfigs:[TaskID:TaskConfiguration]=[:]
var defaults:JustSessionDefaults!
var session: NSURLSession!
var invalidURLError = NSError(
domain: errorDomain,
code: 0,
userInfo: [NSLocalizedDescriptionKey:"[Just] URL is invalid"]
)
var syncResultAccessError = NSError(
domain: errorDomain,
code: 1,
userInfo: [NSLocalizedDescriptionKey:"[Just] You are accessing asynchronous result synchronously."]
)
func queryComponents(key: String, _ value: AnyObject) -> [(String, String)] {
var components: [(String, String)] = []
if let dictionary = value as? [String: AnyObject] {
for (nestedKey, value) in dictionary {
components += queryComponents("\(key)[\(nestedKey)]", value)
}
} else if let array = value as? [AnyObject] {
for value in array {
components += queryComponents("\(key)", value)
}
} else {
components.appendContentsOf([(percentEncodeString(key), percentEncodeString("\(value)"))])
}
return components
}
func query(parameters: [String: AnyObject]) -> String {
var components: [(String, String)] = []
for key in Array(parameters.keys).sort(<) {
let value: AnyObject! = parameters[key]
components += self.queryComponents(key, value)
}
return (components.map{"\($0)=\($1)"} as [String]).joinWithSeparator("&")
}
func percentEncodeString(originalObject: AnyObject) -> String {
if originalObject is NSNull {
return "null"
} else {
return "\(originalObject)".stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()) ?? ""
}
}
func makeTask(request:NSURLRequest, configuration: TaskConfiguration) -> NSURLSessionDataTask? {
let task = session.dataTaskWithRequest(request)
taskConfigs[task.taskIdentifier] = configuration
return task
}
func synthesizeMultipartBody(data:[String:AnyObject], files:[String:HTTPFile]) -> NSData? {
let body = NSMutableData()
let boundary = "--\(self.defaults.multipartBoundary)\r\n".dataUsingEncoding(defaults.encoding)!
for (k,v) in data {
let valueToSend:AnyObject = v is NSNull ? "null" : v
body.appendData(boundary)
body.appendData("Content-Disposition: form-data; name=\"\(k)\"\r\n\r\n".dataUsingEncoding(defaults.encoding)!)
body.appendData("\(valueToSend)\r\n".dataUsingEncoding(defaults.encoding)!)
}
for (k,v) in files {
body.appendData(boundary)
var partContent: NSData? = nil
var partFilename:String? = nil
var partMimetype:String? = nil
switch v {
case let .URL(URL, mimetype):
if let component = URL.lastPathComponent {
partFilename = component
}
if let URLContent = NSData(contentsOfURL: URL) {
partContent = URLContent
}
partMimetype = mimetype
case let .Text(filename, text, mimetype):
partFilename = filename
if let textData = text.dataUsingEncoding(defaults.encoding) {
partContent = textData
}
partMimetype = mimetype
case let .Data(filename, data, mimetype):
partFilename = filename
partContent = data
partMimetype = mimetype
}
if let content = partContent, let filename = partFilename {
body.appendData(NSData(data: "Content-Disposition: form-data; name=\"\(k)\"; filename=\"\(filename)\"\r\n".dataUsingEncoding(defaults.encoding)!))
if let type = partMimetype {
body.appendData("Content-Type: \(type)\r\n\r\n".dataUsingEncoding(defaults.encoding)!)
} else {
body.appendData("\r\n".dataUsingEncoding(defaults.encoding)!)
}
body.appendData(content)
body.appendData("\r\n".dataUsingEncoding(defaults.encoding)!)
}
}
if body.length > 0 {
body.appendData("--\(self.defaults.multipartBoundary)--\r\n".dataUsingEncoding(defaults.encoding)!)
}
return body
}
public func synthesizeRequest(
method:HTTPMethod,
URLString:String,
params:[String:AnyObject],
data:[String:AnyObject],
json:[String:AnyObject]?,
headers:CaseInsensitiveDictionary<String,String>,
files:[String:HTTPFile],
timeout:Double?,
requestBody:NSData?,
URLQuery:String?
) -> NSURLRequest? {
if let urlComponent = NSURLComponents(string: URLString) {
let queryString = query(params)
if queryString.characters.count > 0 {
urlComponent.percentEncodedQuery = queryString
}
var finalHeaders = headers
var contentType:String? = nil
var body:NSData?
if let requestData = requestBody {
body = requestData
} else if files.count > 0 {
body = synthesizeMultipartBody(data, files:files)
contentType = "multipart/form-data; boundary=\(self.defaults.multipartBoundary)"
} else {
if let requestJSON = json {
contentType = "application/json"
body = try? NSJSONSerialization.dataWithJSONObject(requestJSON, options: defaults.JSONWritingOptions)
} else {
if data.count > 0 {
if headers["content-type"]?.lowercaseString == "application/json" { // assume user wants JSON if she is using this header
body = try? NSJSONSerialization.dataWithJSONObject(data, options: defaults.JSONWritingOptions)
} else {
contentType = "application/x-www-form-urlencoded"
body = query(data).dataUsingEncoding(defaults.encoding)
}
}
}
}
if let contentTypeValue = contentType {
finalHeaders["Content-Type"] = contentTypeValue
}
if let URL = urlComponent.URL {
let request = NSMutableURLRequest(URL: URL)
request.cachePolicy = .ReloadIgnoringLocalCacheData
request.HTTPBody = body
request.HTTPMethod = method.rawValue
if let requestTimeout = timeout {
request.timeoutInterval = requestTimeout
}
for (k,v) in defaults.headers {
request.addValue(v, forHTTPHeaderField: k)
}
for (k,v) in finalHeaders {
request.addValue(v, forHTTPHeaderField: k)
}
return request
}
}
return nil
}
public func request(
method:HTTPMethod,
URLString:String,
params:[String:AnyObject],
data:[String:AnyObject],
json:[String:AnyObject]?,
headers:[String:String],
files:[String:HTTPFile],
auth:Credentials?,
cookies: [String:String],
redirects:Bool,
timeout:Double?,
URLQuery:String?,
requestBody:NSData?,
asyncProgressHandler:TaskProgressHandler?,
asyncCompletionHandler:((HTTPResult!) -> Void)?) -> HTTPResult {
let isSync = asyncCompletionHandler == nil
let semaphore = dispatch_semaphore_create(0)
var requestResult:HTTPResult = HTTPResult(data: nil, response: nil, error: syncResultAccessError, request: nil)
let caseInsensitiveHeaders = CaseInsensitiveDictionary<String,String>(dictionary:headers)
if let request = synthesizeRequest(
method,
URLString: URLString,
params: params,
data: data,
json: json,
headers: caseInsensitiveHeaders,
files: files,
timeout:timeout,
requestBody:requestBody,
URLQuery: URLQuery
) {
addCookies(request.URL!, newCookies: cookies)
let config = TaskConfiguration(
credential:auth,
redirects:redirects,
originalRequest:request,
data:NSMutableData(),
progressHandler: asyncProgressHandler
) { (result) in
if let handler = asyncCompletionHandler {
handler(result)
}
if isSync {
requestResult = result
dispatch_semaphore_signal(semaphore)
}
}
if let task = makeTask(request, configuration:config) {
task.resume()
}
if isSync {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
return requestResult
}
} else {
let erronousResult = HTTPResult(data: nil, response: nil, error: invalidURLError, request: nil)
if let handler = asyncCompletionHandler {
handler(erronousResult)
} else {
return erronousResult
}
}
return requestResult
}
func addCookies(URL:NSURL, newCookies:[String:String]) {
for (k,v) in newCookies {
if let cookie = NSHTTPCookie(properties: [
NSHTTPCookieName: k,
NSHTTPCookieValue: v,
NSHTTPCookieOriginURL: URL,
NSHTTPCookiePath: "/"
]) {
session.configuration.HTTPCookieStorage?.setCookie(cookie)
}
}
}
}
extension HTTP: NSURLSessionTaskDelegate, NSURLSessionDataDelegate {
public func URLSession(
session: NSURLSession,
task: NSURLSessionTask,
didReceiveChallenge challenge: NSURLAuthenticationChallenge,
completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void
) {
var endCredential:NSURLCredential? = nil
if let credential = taskConfigs[task.taskIdentifier]?.credential {
if !(challenge.previousFailureCount > 0) {
endCredential = NSURLCredential(user: credential.0, password: credential.1, persistence: .ForSession)
}
}
completionHandler(.UseCredential, endCredential)
}
public func URLSession(
session: NSURLSession,
task: NSURLSessionTask,
willPerformHTTPRedirection response: NSHTTPURLResponse,
newRequest request: NSURLRequest, completionHandler: (NSURLRequest?) -> Void
) {
if let allowRedirects = taskConfigs[task.taskIdentifier]?.redirects {
if !allowRedirects {
completionHandler(nil)
return
}
completionHandler(request)
} else {
completionHandler(request)
}
}
public func URLSession(
session: NSURLSession,
task: NSURLSessionTask,
didSendBodyData bytesSent: Int64,
totalBytesSent: Int64,
totalBytesExpectedToSend: Int64
) {
if let handler = taskConfigs[task.taskIdentifier]?.progressHandler {
handler(
HTTPProgress(
type: .Upload,
bytesProcessed: totalBytesSent,
bytesExpectedToProcess: totalBytesExpectedToSend
)
)
}
}
public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
if let handler = taskConfigs[dataTask.taskIdentifier]?.progressHandler {
handler(
HTTPProgress(
type: .Download,
bytesProcessed: dataTask.countOfBytesReceived,
bytesExpectedToProcess: dataTask.countOfBytesExpectedToReceive
)
)
}
if taskConfigs[dataTask.taskIdentifier]?.data != nil {
taskConfigs[dataTask.taskIdentifier]?.data.appendData(data)
}
}
public func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if let config = taskConfigs[task.taskIdentifier], let handler = config.completionHandler {
let result = HTTPResult(
data: config.data,
response: task.response,
error: error,
request: config.originalRequest ?? task.originalRequest
)
result.JSONReadingOptions = self.defaults.JSONReadingOptions
result.encoding = self.defaults.encoding
handler(result)
}
taskConfigs.removeValueForKey(task.taskIdentifier)
}
}
public let Just = JustOf<HTTP>()

View file

@ -0,0 +1,75 @@
//
// Log.swift
// bonjour
//
// Created by WU JINZHOU on 12/8/15.
// Copyright (c) 2015 WU JINZHOU. All rights reserved.
//
// To print color on console, install XcodeColors plugin for xCode
// https://github.com/robbiehanson/XcodeColors
import Foundation
class Log {
static var ESCAPE = ""
static var RESET_FG = ""
static var RESET_BG = ""
static var RESET = ""
static var RED = ""
static var BLUE = ""
static var GREEN = ""
class func e<T>(object:T, functionName: String = __FUNCTION__, fileNameWithPath: String = __FILE__, lineNumber: Int = __LINE__ ) {
if R.debug_message_enable {
setColor()
let fileNameWithoutPath = NSURL(fileURLWithPath: fileNameWithPath).lastPathComponent
let caller = "\(NSDate()): [\(functionName) in \(fileNameWithoutPath), line \(lineNumber)]: "
print("\(ESCAPE)\(RED)\(caller)\(RESET)")
print("\(ESCAPE)\(RED)\(object)\(RESET)")
print("")
}
}
class func i<T>(object:T) {
if R.debug_message_enable {
setColor()
print("\(ESCAPE)\(GREEN)\(object)\(RESET)")
print("")
}
}
class func v<T>(object:T, functionName: String = __FUNCTION__, fileNameWithPath: String = __FILE__, lineNumber: Int = __LINE__ ) {
if R.debug_message_enable {
setColor()
let fileNameWithoutPath = NSURL(fileURLWithPath: fileNameWithPath).lastPathComponent
let caller = "\(NSDate()): [\(functionName) in \(fileNameWithoutPath), line \(lineNumber)]: "
print("\(ESCAPE)\(BLUE)\(caller)\(RESET)")
print("\(ESCAPE)\(BLUE)\(object)\(RESET)")
print("")
}
}
class func setColor(){
if R.debug_message_in_color {
ESCAPE = "\u{001b}["
RESET_FG = ESCAPE + "fg;" // Clear any foreground color
RESET_BG = ESCAPE + "bg;" // Clear any background color
RESET = ESCAPE + ";" // Clear any foreground or background color
RED = "fg255,0,0;"
GREEN = "fg0,131,18;"
BLUE = "fg41,52,212;"
}
}
}

View file

@ -0,0 +1,44 @@
//
// AboutVC.swift
// WiGadget
//
// Created by WU JINZHOU on 24/7/15.
// Copyright (c) 2015 WU JINZHOU. All rights reserved.
//
import UIKit
class AboutVC: UIViewController {
@IBOutlet weak var menuBtn: UIBarButtonItem!
@IBOutlet weak var dateLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
menuBtn.target = self.revealViewController()
menuBtn.action = Selector("revealToggle:")
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
self.title = R.menu_titles[3]
dateLabel.text = R.app_build_date
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}

View file

@ -0,0 +1,67 @@
//
// BackTableVC.swift
// WiGadget
//
// Created by WU JINZHOU on 23/7/15.
// Copyright (c) 2015 WU JINZHOU. All rights reserved.
//
import UIKit
class BackTableVC: UITableViewController {
@IBOutlet weak var deviceCntLabel: UILabel!
@IBOutlet weak var deviceCntLabelMask: UIImageView!
@IBOutlet weak var deviceFoundLabel: UILabel!
@IBOutlet weak var deviceFoundLabelMask: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
title = "Menu Page"
}
override func viewWillAppear(animated: Bool) {
if AmebaList.savedList.count != 0 {
deviceCntLabelMask.layer.cornerRadius = R.menu_label_corner_radius
deviceCntLabelMask.layer.masksToBounds = true
deviceCntLabelMask.hidden = false
deviceCntLabel.textAlignment = .Center
deviceCntLabel.text = "\(AmebaList.savedList.count)" + R.menu_label_trailing
deviceCntLabel.hidden = false
}
else{
deviceCntLabel.hidden = true
deviceCntLabelMask.hidden = true
}
if AmebaList.newfoundList.count != 0 {
deviceFoundLabelMask.layer.cornerRadius = R.menu_label_corner_radius
deviceFoundLabelMask.layer.masksToBounds = true
deviceFoundLabelMask.hidden = false
deviceFoundLabel.textAlignment = .Center
deviceFoundLabel.text = "New \(AmebaList.newfoundList.count)" + R.menu_label_trailing
deviceFoundLabel.hidden = false
}
else{
deviceFoundLabel.hidden = true
deviceFoundLabelMask.hidden = true
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

View file

@ -0,0 +1,21 @@
//
// DeviceCollectionViewCell.swift
// WiGadget
//
// Created by WU JINZHOU on 13/8/15.
// Copyright (c) 2015 WU JINZHOU. All rights reserved.
//
import UIKit
class DeviceCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var findDeviceImageView: UIImageView!
@IBOutlet weak var findDeviceTextLabel: UILabel!
@IBOutlet weak var myDeviceImageView: UIImageView!
@IBOutlet weak var myDeviceTextLabel: UILabel!
var mac = R.not_assigned
var image = R.not_assigned
}

View file

@ -0,0 +1,441 @@
//
// FindDeviceVC.swift
// WiGadget
//
// Created by WU JINZHOU on 24/7/15.
// Copyright (c) 2015 WU JINZHOU. All rights reserved.
//
import UIKit
class FindDeviceVC: UIViewController,UICollectionViewDataSource, UICollectionViewDelegate,NewfoundListDelegate {
let bonjourBrowser = BonjourBrowser()
@IBOutlet weak var menuBtn: UIBarButtonItem!
@IBOutlet weak var collectionView: UICollectionView!
@IBOutlet weak var imageView: UIImageView!
var imageSource = [String]()
var textSource = [String]()
var macSource = [String]()
var preloaderImageData = NSData(contentsOfURL: NSBundle.mainBundle().URLForResource("preloader_256x23", withExtension: "gif")!)
override func viewDidLoad() {
super.viewDidLoad()
menuBtn.target = self.revealViewController()
menuBtn.action = Selector("revealToggle:")
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
self.title = R.menu_titles[1]
AmebaList.load()
AmebaList.newfoundListDataSource = self
let gesture: UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "collectionViewCellLongPressed:")
gesture.minimumPressDuration = R.device_cell_long_press_duration
self.collectionView.addGestureRecognizer(gesture)
imageView.image = UIImage.animatedImageWithAnimatedGIFData(preloaderImageData)
Log.v("Start Bonjour Service Discovery")
bonjourBrowser.browse(R.bonjour_service_name, domain: R.bonjour_service_domain)
updateDataSource(AmebaList.newfoundList)
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageSource.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(R.device_cell_ids[0], forIndexPath: indexPath) as! DeviceCollectionViewCell
cell.findDeviceTextLabel.text = textSource[indexPath.row]
cell.image = imageSource[indexPath.row]
cell.findDeviceImageView.image = UIImage(named: cell.image)
cell.mac = macSource[indexPath.row]
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
collectionView.deselectItemAtIndexPath(indexPath, animated: true)
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! DeviceCollectionViewCell
Log.v("cell \(indexPath.row) , mac \(cell.mac) selected")
//get ameba
if let ameba = AmebaList.newfoundListGetAmebaByMac(cell.mac) {
let alert = SCLAlertView()
alert.showAnimationType = .SlideInToCenter
alert.hideAnimationType = .SlideOutToCenter
alert.backgroundType = .Blur
var firebaseAppId = UITextField()
var alertTitle = R.not_assigned
if ameba.pair_state == R.not_paired {
alertTitle = "Pair Device"
if ameba.control_type == R.cloud_control {
firebaseAppId = alert.addTextField("Firebase App ID")
alert.addButton("Register Firebase", actionBlock: { () -> Void in
Log.v("Register Firebase is clicked")
let regiFirebaseVC = UIStoryboard(name: R.storyboard_name_regi_fb,bundle:nil).instantiateViewControllerWithIdentifier(R.storyboard_init_regi_fb_vc[R.storyboard_name_regi_fb]!)
self.presentViewController(regiFirebaseVC, animated: true, completion: nil)
})
}
alert.addButton("Pair Device",
validationBlock: {
Log.v("Pair Device is clicked")
if ameba.control_type == R.cloud_control {
var id = firebaseAppId.text! ?? R.not_assigned
Log.v("check firebase app id: \(id)")
id = id.removeWhitespace()
if id == R.not_assigned {
self.view.makeToast("Firebase app id is empty\nPlease enter your app id", duration: R.toast_duration, position: CSToastPositionBottom, title: "EMPTY APP ID", image: R.toast_ic_failed)
return false
}
return true
}
return true
},
actionBlock: {
() -> Void in
Log.v("Pair process start")
//add an alert pop to handle if the app id is empty for cloud control
let qos = Int(QOS_CLASS_USER_INITIATED.rawValue)
let mainQ = dispatch_get_main_queue()
let sockQ = dispatch_get_global_queue(qos, 0)
var pairResult = R.pair_failed_on_unknown_err
self.view.makeToastActivity()
dispatch_async(sockQ){
// do something off-UI
pairResult = self.pair(ameba,firebaseAppId: firebaseAppId.text!)
dispatch_async(mainQ){
// switch back to main Q
self.view.hideToastActivity()
if pairResult == R.pair_success {
self.view.makeToast("Pair succeed\nNew device saved to \"My Device\" List", duration: R.toast_duration, position: CSToastPositionBottom, title: "PAIR SUCCESS", image: R.toast_ic_succeed)
}
else{
self.view.makeToast("Pair failed\nError code: \(pairResult)", duration: R.toast_duration, position: CSToastPositionBottom, title: "PAIR FAILED", image: R.toast_ic_failed)
}
}
}
})
}
if ameba.pair_state == R.paired {
alertTitle = "Share Device"
alert.addButton("Share Device", actionBlock: { () -> Void in
Log.v("Share Device is clicked")
self.view.makeToast("Device sharing will be supported in the future release", duration: R.toast_duration, position: CSToastPositionBottom, title: "NOT SUPPORTED", image: R.toast_ic_failed)
})
}
let amebaInfo = ameba.name + "\nIP: " + ameba.ip + "\nPort: " + ameba.port
alert.showCustom(self, image: UIImage(named: cell.image), color: R.device_cell_on_selected_color, title: alertTitle, subTitle: amebaInfo, closeButtonTitle: "Cancel", duration: 0 as NSTimeInterval)
}
}
func collectionViewCellLongPressed(longPress: UIGestureRecognizer) {
if longPress.state == UIGestureRecognizerState.Began {
let point = longPress.locationInView(self.collectionView)
let indexPath = self.collectionView.indexPathForItemAtPoint(point)
if let index = indexPath {
let cell = collectionView.cellForItemAtIndexPath(index) as! DeviceCollectionViewCell
Log.v("cell \(index.row) , mac \(cell.mac) long-pressed")
//get ameba info
if let ameba = AmebaList.newfoundListGetAmebaByMac(cell.mac){
//show device info
let amebaInfo = ameba.name + "\nIP: " + ameba.ip + "\nPort: " + ameba.port + "\nControl Type: " + ameba.control_type + "\nPair State: " + ameba.pair_state
let alert = SCLAlertView()
alert.showAnimationType = .SlideInToCenter
alert.hideAnimationType = .SlideOutToCenter
alert.backgroundType = .Blur
alert.showCustom(self, image: UIImage(named: cell.image), color: R.device_cell_on_selected_color, title: "Device Info", subTitle: amebaInfo, closeButtonTitle: "Done", duration: R.scl_alert_auto_dismiss_time)
}
}
else {
Log.e("Could not find cell")
}
}
}
func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) {
let selectedView = UIView() as UIView
selectedView.layer.borderWidth = R.device_cell_on_selected_border_width
selectedView.layer.cornerRadius = R.device_cell_on_selected_corner_radius;
selectedView.layer.borderColor = R.device_cell_on_selected_color.CGColor
selectedView.backgroundColor = R.device_cell_on_selected_color
cell.selectedBackgroundView = selectedView
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func onNewfoundListUpdated(newfoundList:[Ameba]) {
//newfoundList updated do something...
Log.v("updating cell data source")
updateDataSource(newfoundList)
dispatch_async(dispatch_get_main_queue(), {
self.collectionView.reloadData()
})
}
func updateDataSource(amebaList:[Ameba]) {
imageSource.removeAll(keepCapacity: false)
textSource.removeAll(keepCapacity: false)
macSource.removeAll(keepCapacity: false)
for ameba in amebaList {
// TODO: add code for new device here
if ameba.name == R.ht_sensor {
// cell image
imageSource.append(R.ic_ht_sensor[ameba.control_type]![ameba.link_state]!)
// cell txt label
let safeMac = ameba.mac + "5755204A494E5A484F55"
textSource.append(ameba.name + "-" + safeMac[8...11] + "-" + ameba.pair_state)
// cell mac
macSource.append(ameba.mac)
}
}
}
func pair(ameba:Ameba,firebaseAppId:String) -> Int {
let connIp = ameba.ip
let connPort = Int(ameba.port)!
var myKey = R.not_assigned
var sharedKey = R.not_assigned
var rxData = [UInt8]?()
let firebaseAppUrl = fmtURL(firebaseAppId)
//verify app id before create socket
if ameba.control_type == R.cloud_control {
let code = Just.get(firebaseAppUrl).statusCode ?? R.not_found
if code != 200 {
return R.pair_failed_on_verify_firebase_url
}
}
//set up for 1st hs
var client = TCPClient(addr: connIp, port: connPort)
var (success,errmsg) = client.connect(timeout: R.tcp_connection_time_out)
if !success {
client.close()
Log.e(errmsg)
return R.pair_failed_connection_time_out
}
//1st hs : send pair cmd
(success,errmsg) = client.send(str: R.tcp_tx_pair)
if !success {
client.close()
Log.e(errmsg)
return R.pair_failed_on_first_hs_tx
}
rxData = client.read(R.tcp_expect_rx_data_length)
if let data = rxData {
let rx = String(bytes: data, encoding: NSUTF8StringEncoding)!
if rx == R.tcp_rx_error {
client.close()
Log.e("pair_failed_cmd_rejected_by_server")
return R.pair_failed_cmd_rejected_by_server
}
let hisKey = rx
myKey = Crypto.makeCurve25519PublicKey()
sharedKey = Crypto.makePSK(hisKey)
}
else{
client.close()
Log.e("pair_failed_on_first_hs_rx")
return R.pair_failed_on_first_hs_rx
}
client.close()
NSThread.sleepForTimeInterval(R.tcp_thread_sleep_time)
//set up for 2nd hs
client = TCPClient(addr: connIp, port: connPort)
(success,errmsg) = client.connect(timeout: R.tcp_connection_time_out)
if !success {
client.close()
Log.e(errmsg)
return R.pair_failed_connection_time_out
}
//2nd hs : send my pk
(success,errmsg) = client.send(str: myKey)
if !success {
client.close()
Log.e(errmsg)
return R.pair_failed_on_second_hs_tx
}
rxData = client.read(R.tcp_expect_rx_data_length)
if let data = rxData {
let rx = String(bytes: data, encoding: NSUTF8StringEncoding)!
if rx == R.tcp_rx_error {
client.close()
Log.e("pair_failed_pk_rejected_by_server")
return R.pair_failed_pk_rejected_by_server
}
if rx == R.tcp_rx_pair_success {
//local-controlled device pair success
ameba.key = sharedKey
ameba.firebase_app_id = R.not_assigned
ameba.pair_state = R.paired
ameba.link_state = R.online
ameba.description = "[NEW]-\(ameba.name)"
AmebaList.pair(ameba)
client.close()
Log.i("tcp_rx_pair_success")
return R.pair_success
}
if rx != R.tcp_rx_firebase_app_id {
client.close()
Log.v("pair_failed_on_unknown_err")
return R.pair_failed_on_unknown_err
}
}
else{
client.close()
Log.e("pair_failed_on_second_hs_rx")
return R.pair_failed_on_second_hs_rx
}
client.close()
NSThread.sleepForTimeInterval(R.tcp_thread_sleep_time)
//set up for 3rd hs
client = TCPClient(addr: connIp, port: connPort)
(success,errmsg) = client.connect(timeout: R.tcp_connection_time_out)
if !success {
client.close()
Log.e(errmsg)
return R.pair_failed_connection_time_out
}
//3rd hs : send firebase app id
let cipherText = Crypto.encrypt(firebaseAppUrl, key: sharedKey)
(success,errmsg) = client.send(str: cipherText)
if !success {
client.close()
Log.e(errmsg)
return R.pair_failed_on_third_hs_tx
}
rxData = client.read(R.tcp_expect_rx_data_length)
if let data = rxData {
let rx = String(bytes: data, encoding: NSUTF8StringEncoding)!
if rx == R.tcp_rx_error {
client.close()
Log.e("pair_failed_firebase_id_rejected_by_server")
return R.pair_failed_firebase_id_rejected_by_server
}
if rx == R.tcp_rx_pair_success {
//cloud - controlled device pair success
ameba.key = sharedKey
ameba.firebase_app_id = firebaseAppId
ameba.pair_state = R.paired
ameba.link_state = R.online
ameba.description = "[NEW]-\(ameba.name)"
AmebaList.pair(ameba)
client.close()
Log.i("tcp_rx_pair_success")
return R.pair_success
}
}
else{
client.close()
Log.e("pair_failed_on_third_hs_rx")
return R.pair_failed_on_third_hs_rx
}
client.close()
Log.e("pair_failed_on_unknown_err")
return R.pair_failed_on_unknown_err
}
func fmtURL(firebaseAppId:String) -> String {
var firebaseUrl = ""
if firebaseAppId.hasSuffix(".firebaseio.com") {
firebaseUrl = "https://" + firebaseAppId + "/"
}
else {
firebaseUrl = "https://" + firebaseAppId + ".firebaseio.com/"
}
firebaseUrl = firebaseUrl.removeWhitespace().lowercaseString
return firebaseUrl
}
}
extension String {
subscript (r: Range<Int>) -> String {
get {
let startIndex = self.startIndex.advancedBy(r.startIndex)
let endIndex = startIndex.advancedBy(r.endIndex - r.startIndex)
return self[Range(start: startIndex, end: endIndex)]
}
}
}

View file

@ -0,0 +1,329 @@
//
// HTSettingsVC.swift
// WiGadget
//
// Created by WU JINZHOU on 14/9/15.
// Copyright (c) 2015 WU JINZHOU. All rights reserved.
//
import UIKit
import AVFoundation
class HTSettingsVC: UITableViewController, UITextFieldDelegate {
//section 0 cell 0
@IBOutlet weak var s0c0SB: UISwitch!
//section 0 cell 1
@IBOutlet weak var s0c1IV: UIImageView!
@IBOutlet weak var s0c1TL: UILabel!
//section 0 cell 2
@IBOutlet weak var s0c2IV: UIImageView!
@IBOutlet weak var s0c2TL: UILabel!
//section 0 cell 3
@IBOutlet weak var s0c3IV: UIImageView!
@IBOutlet weak var s0c3TL: UILabel!
//section 0 cell 4 -> humi
@IBOutlet weak var s0c4TF: UITextField!
@IBOutlet weak var s0c4TL: UILabel!
//section 0 cell 5 -> temp
@IBOutlet weak var s0c5TF: UITextField!
@IBOutlet weak var s0c5TL: UILabel!
//section 1 cell 0
@IBOutlet weak var s1c0IV: UIImageView!
//section 1 cell 1
@IBOutlet weak var s1c1IV: UIImageView!
//section 1 cell 2
@IBOutlet weak var s1c2IV: UIImageView!
var cellHeight = R.settings_tv_cell_height
var s0IV = [UIImageView]()
var s0TL = [UILabel]()
var s0TF = [UITextField]()
var s1IV = [UIImageView]()
var useAlarm = false
var alarmSound = 0 //mapped to sound 1
var humiThres = 0.0
var tempThres = 0.0
var linkFreq = 2
var player = AVAudioPlayer()
var canPlaySound = false
/*
Tells the delegate that the table view is about to draw a cell for a particular row.
*/
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell,
forRowAtIndexPath indexPath: NSIndexPath)
{
// Remove separator inset
if cell.respondsToSelector("setSeparatorInset:") {
cell.separatorInset = UIEdgeInsetsZero
}
// Prevent the cell from inheriting the Table View's margin settings
if cell.respondsToSelector("setPreservesSuperviewLayoutMargins:") {
cell.preservesSuperviewLayoutMargins = false
}
// Explictly set your cell's layout margins
if cell.respondsToSelector("setLayoutMargins:") {
cell.layoutMargins = UIEdgeInsetsZero
}
}
override func viewDidLoad() {
super.viewDidLoad()
title = "H&T Sensor"
s0TL = [s0c1TL,s0c2TL,s0c3TL,s0c4TL,s0c5TL]
s0IV = [s0c1IV,s0c2IV,s0c3IV]
s0TF = [s0c4TF,s0c5TF]
s1IV = [s1c0IV,s1c1IV,s1c2IV]
s0c0SB.addTarget(self, action: Selector("s0c0SBToggled:"), forControlEvents: UIControlEvents.ValueChanged)
s0c4TF.delegate = self
s0c5TF.delegate = self
loadHTSettings()
initUI()
}
//update & save on TF changed
func textFieldShouldReturn(textField: UITextField) -> Bool {
humiThres = (s0c4TF.text! as NSString).doubleValue
Log.i("humiThres: \(humiThres)")
tempThres = (s0c5TF.text! as NSString).doubleValue
Log.i("tempThres: \(tempThres)")
view.endEditing(true)
saveHTSettings()
return true
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.section == 0 {
if indexPath.row == 0 {
return R.settings_tv_cell_height
}
return cellHeight
}
else {
return R.settings_tv_cell_height
}
}
//update & save on cell choosed
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let section = indexPath.section
let row = indexPath.row
Log.i("selected cell @ section: \(section) row: \(row)")
if rowToData(section: section, row: row) {
cellSelected(section: section, row: row)
saveHTSettings()
}
}
func cellSelected(section section:Int, row:Int) {
if section == 0 {
if 1 ... 3 ~= row {
for i in 0 ..< s0IV.count {
s0IV[i].image = nil
}
s0IV[row - 1].image = UIImage(named: "ic_selected_55x55")
if canPlaySound {
playSound(row - 1)
}
}
}
if section == 1 {
for i in 0 ..< s1IV.count {
s1IV[i].image = nil
}
s1IV[row].image = UIImage(named: "ic_selected_55x55")
}
tableView.reloadData()
}
//update & save on SB toggled
func s0c0SBToggled(switchState: UISwitch) {
if switchState.on {
Log.i("s0c0SBT on")
useAlarm = true
s0Hide(false)
} else {
Log.i("s0c0SBT off")
useAlarm = false
s0Hide(true)
}
saveHTSettings()
}
func s0Hide(hidden:Bool) {
s0c0SB.on = !hidden
for i in 0 ..< s0IV.count {
s0IV[i].hidden = hidden
}
for i in 0 ..< s0TF.count {
s0TF[i].hidden = hidden
}
for i in 0 ..< s0TL.count {
s0TL[i].hidden = hidden
}
if hidden {
cellHeight = 0
}
else {
cellHeight = R.settings_tv_cell_height
}
tableView.reloadData()
}
func saveHTSettings() {
let htSettings = [R.settings_key_ht_enable_alarm:useAlarm,
R.settings_key_ht_alarm_sound:alarmSound,
R.settings_key_ht_humi_thres:humiThres,
R.settings_key_ht_temp_thres:tempThres,
R.settings_key_ht_link_freq:linkFreq]
let data_to_save = JSON(htSettings).rawString(NSUTF8StringEncoding, options: [])
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setValue(data_to_save, forKey: R.settings_key_ht)
defaults.synchronize()
}
func loadHTSettings() {
let defaults = NSUserDefaults.standardUserDefaults()
let dataStr = defaults.valueForKey(R.settings_key_ht) as? String ?? R.not_assigned
let htSettings = JSON(data: dataStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!)
if !(htSettings.isEmpty) {
useAlarm = htSettings[R.settings_key_ht_enable_alarm].boolValue
alarmSound = htSettings[R.settings_key_ht_alarm_sound].intValue
humiThres = htSettings[R.settings_key_ht_humi_thres].doubleValue
tempThres = htSettings[R.settings_key_ht_temp_thres].doubleValue
linkFreq = htSettings[R.settings_key_ht_link_freq].intValue
}
}
func initUI() {
if !useAlarm {
s0Hide(true)
}
else {
s0Hide(false)
}
cellSelected(section: 0, row: dadaToRow(section: 0, dataVal: alarmSound)!)
s0c4TF.text = "\(humiThres)"
s0c5TF.text = "\(tempThres)"
cellSelected(section: 1, row: dadaToRow(section: 1, dataVal: Int(linkFreq))!)
canPlaySound = true
}
func dadaToRow (section section:Int, dataVal:Int) -> Int? {
if section == 0 {
let row = dataVal + 1
if row < 1 {
return 1
}
if row > 3 {
return 3
}
return row
}
if section == 1 {
let row = (dataVal / 2) - 1
if row < 0 {
return 0
}
if row > 2 {
return 2
}
return row
}
return nil
}
func rowToData (section section:Int , row:Int) -> Bool {
if section == 0 {
if row < 1 {
return false
}
if row > 3 {
return false
}
alarmSound = row - 1
return true
}
if section == 1 {
if row < 0 {
return false
}
if row > 2 {
return false
}
linkFreq = (row + 1) * 2
return true
}
return false
}
func playSound (idx:Int) {
if 0 ..< R.settings_ht_alarm_sounds.count ~= idx {
let path = NSBundle.mainBundle().pathForResource(R.settings_ht_alarm_sounds[idx], ofType: "wav")
try! player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: path!))
player.prepareToPlay()
player.play()
}
}
@IBAction func doneEditing(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Some files were not shown because too many files have changed in this diff Show more