WireGuard
This commit is contained in:
committed by
Liz Malinouskaya
parent
660bc9ea03
commit
6ab7fa45a1
@@ -16,3 +16,4 @@
|
||||
xcuserdata/
|
||||
/BuildTools/Creator/Vendors/packages
|
||||
/BuildTools/Creator/Creator
|
||||
/macOS/SystemExtensions/OpenVPN/Info.plist
|
||||
|
||||
@@ -56,6 +56,7 @@ Lint:
|
||||
allow_failure: false
|
||||
only:
|
||||
- develop
|
||||
- /^feature/
|
||||
script:
|
||||
- chmod +x ./BuildTools/create_macOS.sh
|
||||
- chmod +x ./BuildTools/build_macOS.sh
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
<string>PrivadoLauncher DeveloperID</string>
|
||||
<key>io.privado.main.openvpn</key>
|
||||
<string>PrivadoOVPNExtension DeveloperID</string>
|
||||
<key>io.privado.main.wireguard</key>
|
||||
<string>PrivadoWireGuardExtension DeveloperID</string>
|
||||
</dict>
|
||||
<key>signingCertificate</key>
|
||||
<string>Developer ID Application: PRIVADO NETWORKS LLC (4D7YV49724)</string>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+11
-14
@@ -93,9 +93,9 @@ xcodebuild \
|
||||
-scheme Launcher \
|
||||
-configuration "$CONFIG" \
|
||||
-derivedDataPath $DERIVED_PATH \
|
||||
build
|
||||
build | xcpretty
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
if [ "${PIPESTATUS[0]}" != "0" ]; then
|
||||
echo >&2 "⛔️ 🛠 Error: xcodebuild Launcher failed";
|
||||
exit 1
|
||||
fi
|
||||
@@ -121,9 +121,9 @@ xcodebuild \
|
||||
-scheme io.privado.main.hive \
|
||||
-configuration "$CONFIG" \
|
||||
-derivedDataPath $DERIVED_PATH \
|
||||
build
|
||||
build | xcpretty
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
if [ "${PIPESTATUS[0]}" != "0" ]; then
|
||||
echo >&2 "⛔️ 🛠 Error: xcodebuild Hive failed";
|
||||
exit 1
|
||||
fi
|
||||
@@ -142,16 +142,16 @@ unlock_login_keychain "$K8S_SECRET_JARVIS"
|
||||
|
||||
mkdir -p $DERIVED_PATH
|
||||
|
||||
#Build Hive
|
||||
#Build Uninstaller
|
||||
xcodebuild \
|
||||
-sdk macosx \
|
||||
-project ./macOS/Uninstaller/Uninstaller.xcodeproj \
|
||||
-scheme io.privado.main.uninstaller \
|
||||
-configuration "$CONFIG" \
|
||||
-derivedDataPath $DERIVED_PATH \
|
||||
build
|
||||
build | xcpretty
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
if [ "${PIPESTATUS[0]}" != "0" ]; then
|
||||
echo >&2 "⛔️ 🛠 Error: xcodebuild Uninstaller failed";
|
||||
exit 1
|
||||
fi
|
||||
@@ -168,9 +168,6 @@ echo "Current directory is: $(pwd)"
|
||||
|
||||
unlock_login_keychain "$K8S_SECRET_JARVIS"
|
||||
|
||||
#Change build number
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" ./macOS/Privado/Info.plist
|
||||
|
||||
#Change build date
|
||||
/usr/libexec/PlistBuddy -c "Set :PRIVADO_BUILD_TIMESTAMP $(date +%F)" ./macOS/Privado/Info.plist
|
||||
|
||||
@@ -199,9 +196,9 @@ xcodebuild \
|
||||
-derivedDataPath $DERIVED_PATH \
|
||||
archive \
|
||||
GCC_PREPROCESSOR_DEFINITIONS="$gcc_preprocessor" \
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS="$swift_compilation"
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS="$swift_compilation" | xcpretty
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
if [ "${PIPESTATUS[0]}" != "0" ]; then
|
||||
echo >&2 "⛔️ 🛠 Error: xcodebuild Privado failed";
|
||||
exit 1
|
||||
fi
|
||||
@@ -219,9 +216,9 @@ xcodebuild \
|
||||
-exportArchive \
|
||||
-archivePath $RESULT_PATH/$APPLICATION_NAME.xcarchive \
|
||||
-exportPath $RESULT_PATH/Result \
|
||||
-exportOptionsPlist ./BuildTools/Privado-macOS-ExportOptions.plist
|
||||
-exportOptionsPlist ./BuildTools/Privado-macOS-ExportOptions.plist | xcpretty
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
if [ "${PIPESTATUS[0]}" != "0" ]; then
|
||||
echo >&2 "⛔️ 🗜 Error: archieve failed";
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
# build_wireguard_go_bridge.sh - Builds WireGuardKitGo
|
||||
#
|
||||
# Figures out the directory where the wireguard-apple SPM package
|
||||
# is checked out by Xcode (so that it works when building as well as
|
||||
# archiving), then cd-s to the WireGuardKitGo directory
|
||||
# and runs make there.
|
||||
|
||||
wireguard_go_dir="../../Vendors/spm/wireguard-apple-1.0.15-26/Sources/WireGuardKitGo"
|
||||
|
||||
# To ensure we have Go in our path, we add where
|
||||
# Homebrew generally installs executables
|
||||
export PATH=${PATH}:/usr/local/bin:/opt/homebrew/bin
|
||||
|
||||
cd "$wireguard_go_dir" && /usr/bin/make
|
||||
@@ -71,5 +71,7 @@ change_plist_version "$VERSION_NUMBER" "$BUILD_NUMBER" ./macOS/Hive/Info.plist
|
||||
change_plist_version "$VERSION_NUMBER" "$BUILD_NUMBER" ./macOS/Uninstaller/Info.plist
|
||||
change_plist_version "$VERSION_NUMBER" "$BUILD_NUMBER" ./macOS/Launcher/Info.plist
|
||||
change_plist_version "$VERSION_NUMBER" "$BUILD_NUMBER" ./macOS/Privado/Info.plist
|
||||
change_plist_version "$VERSION_NUMBER" "$BUILD_NUMBER" ./macOS/SystemExtensions/OpenVPN/Info.plist
|
||||
change_plist_version "$VERSION_NUMBER" "$BUILD_NUMBER" ./macOS/SystemExtensions/WireGuard/Info.plist
|
||||
|
||||
echo "✅ Generate completed"
|
||||
@@ -14,7 +14,7 @@ fi
|
||||
|
||||
cd $PLATFORM_PATH/Hive
|
||||
echo "🔬 Linting Hive"
|
||||
swiftlint lint --config "../../../BuildTools/Configs/.swiftlint.yml" --path ./Sources
|
||||
swiftlint lint --config "../../BuildTools/Configs/.swiftlint.yml" --path ./Sources
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
echo >&2 "⛔️ Error: Linting Hive failed";
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# for Xcode
|
||||
###########
|
||||
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
Icon?
|
||||
|
||||
Secrets.*
|
||||
_*
|
||||
|
||||
## Build generated
|
||||
build/
|
||||
DerivedData
|
||||
swift-3.0-RELEASE-ubuntu14.04
|
||||
|
||||
## Various settings
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
|
||||
## Sublime
|
||||
*.sublime*
|
||||
|
||||
## Other
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
*.xcuserstate
|
||||
*.xcscmblueprint
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
*.ipa
|
||||
|
||||
## Playgrounds
|
||||
timeline.xctimeline
|
||||
playground.xcworkspace
|
||||
|
||||
# Swift Package Manager
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
||||
# Packages/
|
||||
.build/
|
||||
|
||||
# CocoaPods
|
||||
#
|
||||
# We recommend against adding the Pods directory to your .gitignore. However
|
||||
# you should judge for yourself, the pros and cons are mentioned at:
|
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||
#
|
||||
# Pods/
|
||||
.swift-version
|
||||
|
||||
# Carthage
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||
# Carthage/Checkouts
|
||||
|
||||
Carthage/
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||
# screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
|
||||
|
||||
fastlane/report.xml
|
||||
fastlane/screenshots
|
||||
/.swiftpm
|
||||
@@ -0,0 +1,38 @@
|
||||
# https://github.com/realm/SwiftLint
|
||||
#
|
||||
# run on CLI (or as build phase):
|
||||
#
|
||||
# swiftlint rules / autocorrect / lint (default)
|
||||
#
|
||||
#
|
||||
|
||||
included: # paths to include during linting. `--path` is ignored if present.
|
||||
- Sources
|
||||
- Tests
|
||||
|
||||
excluded: # paths to ignore during linting. Takes precedence over `included`.
|
||||
- Carthage
|
||||
- Packages
|
||||
- .build
|
||||
|
||||
opt_in_rules: # some rules are only opt-in
|
||||
- empty_count
|
||||
|
||||
disabled_rules: # rule identifiers to exclude from running
|
||||
- valid_docs # /// is valid and used by Xcode
|
||||
- cyclomatic_complexity
|
||||
- identifier_name
|
||||
- syntactic_sugar
|
||||
- type_body_length
|
||||
- function_body_length
|
||||
- file_length
|
||||
- vertical_parameter_alignment
|
||||
- function_parameter_count
|
||||
- implicit_getter
|
||||
|
||||
line_length: 200
|
||||
|
||||
type_name:
|
||||
min_length: 2
|
||||
|
||||
reporter: "xcode" # reporter type (xcode, json, csv, checkstyle)
|
||||
@@ -0,0 +1,735 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file following the style described at [Keep a Changelog](http://keepachangelog.com) by [@olivierlacan](https://github.com/olivierlacan).
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
----
|
||||
<br/>
|
||||
|
||||
## 1.9.5 (2021-05-18)
|
||||
|
||||
##### Fixed
|
||||
- Issue with file rotation logic by [@ekurutepe](https://github.com/ekurutepe)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.9.4 (2021-05-05)
|
||||
|
||||
##### Added
|
||||
- Log file rotation by [@mikeumus](https://github.com/mikeumus)
|
||||
- Subclassing of of FileDestination by [@simonseyer](https://github.com/simonseyer)
|
||||
- Safer JSON conversion by [@vishal-iosdeveloper](https://github.com/vishal-iosdeveloper)
|
||||
|
||||
##### Fixed
|
||||
- Xcode 12.5 build error and warning by [@gabors](https://github.com/gabors)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.9.3 (2020-11-10)
|
||||
|
||||
##### Added
|
||||
- Ability to extend custom formatting options by [@adamwulf](https://github.com/adamwulf)
|
||||
|
||||
##### Fixed
|
||||
- Warning iOS version warning on building via CocoaPods by [@uypanha](https://github.com/uypanha)
|
||||
- CI build pipeline by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.9.2 (2020-09-17)
|
||||
|
||||
##### Fixed
|
||||
- fixed Xcode warning under iOS 14 by [@gabors](https://github.com/gabors)
|
||||
- fixed issue with iOS 14 beta by [@preobrazhenskiy](https://github.com/preobrazhenskiy)
|
||||
|
||||
##### Removed
|
||||
- process hostname logging to avoid iOS 14 Permission dialog by [@alexanderlucas](https://github.com/alexanderlucas)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.9.1 (2020-04-17)
|
||||
|
||||
##### Fixed
|
||||
- dylib linking issue by [@CodySchrank](https://github.com/CodySchrank)
|
||||
|
||||
##### Removed
|
||||
- Unreachable code in crypto logic by [@ladeiko](https://github.com/ladeiko)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.9.0 (2020-03-30)
|
||||
|
||||
##### Fixed
|
||||
- More cryptographically secure random text generator by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
##### Removed
|
||||
- Support for Swift 3 by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.8.4 (2019-12-06)
|
||||
|
||||
##### Added
|
||||
- Enabled "Allow app extension API only" flag by [@FelixII](https://github.com/FelixII)
|
||||
- Support for older Swift version with Cocoapods by [@RomanPodymov](https://github.com/RomanPodymov)
|
||||
- Support for colors in FileDestination by [@FabioTacke](https://github.com/FabioTacke)
|
||||
- More human-readable thread names by [@robowen5mac](https://github.com/robowen5mac)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.8.3 (2019-10-23)
|
||||
|
||||
##### Added
|
||||
- Support for older OS versions via SPM by [@OlexandrStepanov](https://github.com/OlexandrStepanov)
|
||||
|
||||
##### Fixed
|
||||
- Support for latest server-side Swift by [@JRHeaton](https://github.com/JRHeaton)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.8.2 (2019-10-02)
|
||||
|
||||
##### Fixed
|
||||
- Key value in `info.plist` by [@Sidetalker](https://github.com/Sidetalker)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.8.1 (2019-09-30)
|
||||
|
||||
##### Fixed
|
||||
- Added missing key to `info.plist` by [@lgaches](https://github.com/lgaches)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.8.0 (2019-09-27)
|
||||
|
||||
##### Added
|
||||
- Support for Xcode 11 and iOS 13 by [@lgaches](https://github.com/lgaches)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.7.1 (2019-08-22)
|
||||
|
||||
##### Added
|
||||
- Improved file destination by [@CognitiveDisson](https://github.com/CognitiveDisson)
|
||||
- Improved README by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- All build targets can use Swift 5 by [@DivineDominion](https://github.com/DivineDominion)
|
||||
|
||||
|
||||
##### Fixed
|
||||
- Issue in BaseDestination with non-required filters by [@FelixII ](https://github.com/FelixII)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.7.0 (2019-03-26)
|
||||
|
||||
##### Added
|
||||
|
||||
- Support for Swift 5 and Xcode 10.2 by [@lgaches](https://github.com/lgaches)
|
||||
- Support for CircleCI 2.0 by [@lgaches](https://github.com/lgaches)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.6.2 (2019-02-11)
|
||||
|
||||
##### Added
|
||||
|
||||
- Improved SPM support for Swift 4.2 by [@heyzooi ](https://github.com/heyzooi)
|
||||
- Improved Carthage support for Swift 4.2 by [@iachievedit ](https://github.com/iachievedit)
|
||||
- Swift type inference by [@rafalmq ](https://github.com/rafalmq)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.6.1 (2018-09-18)
|
||||
|
||||
##### Added
|
||||
|
||||
- Optional sync after each file write by [@crspybits ](https://github.com/crspybits)
|
||||
- Execute methods to run in dest queue by [@keeshux ](https://github.com/keeshux)
|
||||
- Padded format option (see [PR for details](https://github.com/SwiftyBeaver/SwiftyBeaver/pull/298)) by [@htb ](https://github.com/htb)
|
||||
|
||||
|
||||
##### Fixed
|
||||
- Warning caused by iOS 12 by [@lgaches](https://github.com/lgaches)
|
||||
- Issues with formating by [@htb ](https://github.com/htb)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.6.0 (2018-05-23)
|
||||
|
||||
##### Added
|
||||
|
||||
- Custom filters by [@Mordil ](https://github.com/Mordil )
|
||||
- App uptime format variable `$U` by [@LordNali ](https://github.com/LordNali )
|
||||
|
||||
##### Changed
|
||||
- Filter behavior which requires now at least one passing non-required filter by [@cconway](https://github.com/cconway)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.5.2 (2018-04-05)
|
||||
|
||||
##### Added
|
||||
|
||||
- Support for Xcode 9.3 and Swift 4.1 by [@jimmya](https://github.com/jimmya)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.5.1 (2018-01-05)
|
||||
|
||||
##### Added
|
||||
|
||||
- Integration test for context format variable `$X` by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Logging output string is trimmed by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
##### Fixed
|
||||
|
||||
- Fixed issue with Xcode and folder name on case-sensitive file systems by [@konstantinbe](https://github.com/konstantinbe)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.5.0 (2017-12-13)
|
||||
|
||||
##### Added
|
||||
|
||||
- Cross-compatibility for Swift 3.1, 3.2 & 4 by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.4.4 (2017-12-08)
|
||||
|
||||
##### Added
|
||||
|
||||
- Set a custom server URL already on platform destination init by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.4.3 (2017-11-09)
|
||||
|
||||
##### Added
|
||||
|
||||
- Support for latest Xcode 9.1 by removing deprecation warning by [@tomekh7](https://github.com/tomekh7)
|
||||
- Reduced the overall size of the framework by [@NachoSoto](https://github.com/NachoSoto)
|
||||
- Improved support for Swift 4 via SPM by [@lgaches](https://github.com/lgaches)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.4.2 (2017-09-26)
|
||||
|
||||
##### Fixed
|
||||
|
||||
- Fixed memory leak in SBPlatformDestination by [@drougojrom](https://github.com/drougojrom)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.4.1 (2017-09-18)
|
||||
|
||||
##### Fixed
|
||||
|
||||
- Disabled code coverage to fix app submission with Xcode 9 by [@NachoSoto](https://github.com/NachoSoto)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.4.0 (2017-08-12)
|
||||
|
||||
##### Added
|
||||
|
||||
- Support for latest Xcode 9 beta, Swift 3.2 & Swift 4 by [@lgaches](https://github.com/lgaches)
|
||||
- Less aggressive file protection type when logfile is created by [@igorefremov](https://github.com/igorefremov)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.3.2 (2017-07-19)
|
||||
|
||||
##### Fixed
|
||||
|
||||
- Issue under macOS server-side Swift with file protection type by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.3.1 (2017-07-19)
|
||||
|
||||
##### Added
|
||||
- Better solution to instable b64 encoding of Swift 3.1.x under Linux by [@lgaches](https://github.com/lgaches)
|
||||
- Set file protection type when logfile is created by [@igorefremov](https://github.com/igorefremov)
|
||||
|
||||
<br/>
|
||||
|
||||
##### Fixed
|
||||
|
||||
- Issue with validation of required filters by [@alessandroorru](https://github.com/alessandroorru)
|
||||
- Issue issue with multiple destinations with message filters by [@alessandroorru](https://github.com/alessandroorru)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.3.0 (2017-06-22)
|
||||
|
||||
##### Added
|
||||
|
||||
- New context parameter for more detailed logging by [@lgaches](https://github.com/lgaches)
|
||||
- Support for more watchOS versions by [@basememara](https://github.com/basememara)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.2.2 (2017-05-04)
|
||||
|
||||
##### Fixed
|
||||
|
||||
- Issue while building for macOS, tvOS & watchOS by [@alex-can](https://github.com/alex-chan)
|
||||
- Issue while building on a case-sensitive file system by [@alex-can](https://github.com/alex-chan)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.2.1 (2017-04-24)
|
||||
|
||||
##### Fixed
|
||||
|
||||
- Logic issue in filter by [@rajatk](https://github.com/rajatk)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.2.0 (2017-04-11)
|
||||
|
||||
##### Added
|
||||
|
||||
- Google Cloud / Stackdriver destination by [@lgaches](https://github.com/lgaches)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.1.4 (2017-03-28)
|
||||
|
||||
##### Added
|
||||
|
||||
- console destination property `.useTerminalColors` by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.1.3 (2017-02-22)
|
||||
|
||||
##### Added
|
||||
|
||||
- Output logging object as JSON with `.format = "$J"` by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Adjust internal filenames in SBPlatform destination by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
##### Changed
|
||||
|
||||
- a filter’s `required` parameter is now also working for levels by [@picciano](https://github.com/picciano)
|
||||
|
||||
|
||||
##### Removed
|
||||
|
||||
- The option to turn just the message into JSON with `.format = "$m"` by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.1.2 (2017-02-16)
|
||||
|
||||
##### Added
|
||||
|
||||
- Support for Swift 3.1 by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Use of official Swift Docker images by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Method `deleteLogFile()` to manually delete log file by [@felipowsky](https://github.com/felipowsky)
|
||||
- Explicit deployment target for tvOS by [@Dschee](https://github.com/Dschee)
|
||||
|
||||
##### Changed
|
||||
|
||||
- `Public` is now `Open` in `SwiftyBeaver.swift` by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.1.1 (2016-10-28)
|
||||
|
||||
##### Added
|
||||
|
||||
- Support for Xcode 8.1 by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.1.0 (2016-10-12)
|
||||
|
||||
##### Added
|
||||
|
||||
- Support for server-side Swift (macOS & Linux) by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.0.3 (2016-09-21)
|
||||
|
||||
##### Changed
|
||||
|
||||
- New format key `$Z` outputs datetime as UTC by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.0.2 (2016-09-19)
|
||||
|
||||
##### Changed
|
||||
|
||||
- Lowercase enum cases (`.Debug` -> `.debug`) to match Swift 3 convention by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.0.1 (2016-09-17)
|
||||
|
||||
##### Added
|
||||
|
||||
- Colored log level indicators for Xcode 8 Console by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 1.0.0 (2016-09-15)
|
||||
|
||||
##### Added
|
||||
|
||||
- Support for Xcode 8 & Swift 3 by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
##### Changed
|
||||
|
||||
- Master branch is written in Swift 3 instead of Swift 2 by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Names of platform destination support files are public by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Default format has colored log level after time by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- README explains installation under Swift 2 and Swift 3 by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
##### Removed
|
||||
|
||||
- swift3 branch & tag 0.0.0 by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.7.0 (2016-09-09)
|
||||
|
||||
##### Added
|
||||
|
||||
- Exclusion filter by [@renaun](https://github.com/renaun)
|
||||
- Custom log formatting by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
##### Removed
|
||||
|
||||
- .detailOutput, .colored & .coloredLines properties by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.6.5 (2016-07-29)
|
||||
|
||||
##### Changed
|
||||
|
||||
- On Xcode 8 colored console output is disabled by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
##### Fixed
|
||||
|
||||
- Broken support for tvOS in platform destination by [@markj](https://github.com/markj)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.6.4 (2016-07-28)
|
||||
|
||||
##### Added
|
||||
|
||||
- Support for use in app extensions by [@madhavajay](https://github.com/madhavajay)
|
||||
|
||||
##### Changed
|
||||
|
||||
- Minimum target for OS X is 10.10 by [@DivineDominion](https://github.com/DivineDominion)
|
||||
|
||||
##### Fixed
|
||||
|
||||
- Potential issue when setting a platform sending threshold of lower than 1 by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.6.3 (2016-06-29)
|
||||
|
||||
##### Added
|
||||
|
||||
- Filters can have their own minimum log level by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Prepared for new macOS alias for OS detection by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
##### Removed
|
||||
|
||||
- Dedicated log level filter by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.6.2 (2016-06-21)
|
||||
|
||||
##### Added
|
||||
|
||||
- Support for Swift 2.3 under Xcode 8 beta by [@brentleyjones](https://github.com/brentleyjones)
|
||||
|
||||
##### Fixed
|
||||
|
||||
- Potential crash when using ConsoleDestination with NSLog by [@nickoto](https://github.com/nickoto)
|
||||
|
||||
##### Removed
|
||||
|
||||
- Deprecated MinLevelFilter functionality by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.6.1 (2016-06-08)
|
||||
|
||||
##### Changed
|
||||
|
||||
- Filter `required` argument defines AND (`required: true`) or OR (`required: false`) relation between filters by [@JeffBNimble](https://github.com/JeffBNimble)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.6.0 (2016-06-07)
|
||||
|
||||
##### Added
|
||||
|
||||
- New filter system for level, path, function, message per destination by [@JeffBNimble](https://github.com/JeffBNimble)
|
||||
|
||||
##### Changed
|
||||
|
||||
- `.minLevel` & `minLevelFilter()` are deprecated. Use the new filter system instead by [@JeffBNimble](https://github.com/JeffBNimble)
|
||||
|
||||
<br/>
|
||||
## 0.5.4 (2016-05-20)
|
||||
|
||||
##### Changed
|
||||
|
||||
- Function names are now logged without parameters (inspired by Gábor Sajó) by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Default location of log file and other internally used files by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
##### Fixed
|
||||
|
||||
- Memory leak in string manipulation by [@dkalachov](https://github.com/dkalachov)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.5.3 (2016-05-11)
|
||||
|
||||
##### Added
|
||||
|
||||
- Ability to adjust destination properties during runtime by [@MarkQSchultz](https://github.com/MarkQSchultz)
|
||||
|
||||
##### Changed
|
||||
|
||||
- Message resolution is done in background for better performance by [@JeffBNimble](https://github.com/JeffBNimble)
|
||||
- Lowered minimum OSX version to 10.10 for CocoaPods by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.5.2 (2016-05-02)
|
||||
|
||||
##### Added
|
||||
|
||||
- Get more colored content with `coloredLines = true` by [@DasHutch](https://github.com/DasHutch)
|
||||
|
||||
##### Changed
|
||||
|
||||
- Adjusted Xcode Console colors to match SwiftyBeaver Mac App UI by [@DasHutch](https://github.com/DasHutch)
|
||||
- Adjusted file destination colors to match SwiftyBeaver Mac App UI by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.5.1 (2016-04-28)
|
||||
|
||||
##### Added
|
||||
|
||||
- Type-safe adding/removal of destination by [@muukii](https://github.com/muukii)
|
||||
- Allow empty log messages by [@ewanmellor](https://github.com/ewanmellor)
|
||||
- Console can use NSLog instead of print by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Exposing of framework version & build for easier support by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
##### Fixed
|
||||
- Issue with overwritten analytics data by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.5.0 (2016-04-19)
|
||||
|
||||
##### Added
|
||||
|
||||
- SwiftyBeaver Platform destination by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- SwiftyBeaver AES256CBC class for string encryption by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Lots of small improvements by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
## 0.4.2 (2016-03-22)
|
||||
|
||||
##### Changed
|
||||
|
||||
- Optimized codebase for Swift 2.2, Swift 3 & Xcode 7.3 by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.4.1 (2016-03-11)
|
||||
|
||||
##### Added
|
||||
|
||||
- Option to log synchronously during development by [@muukii](https://github.com/muukii)
|
||||
- Code completion docs for most public variables & functions by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Internal linting of code base by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.4.0 (2016-03-04)
|
||||
|
||||
##### Added
|
||||
|
||||
- Default log file directory is OS-dependent by [@xeo-it](https://github.com/xeo-it)
|
||||
- Flush function is accessible to all destinations by [@prenagha](https://github.com/prenagha)
|
||||
- Customizable log colors by [@fvvliet](https://github.com/fvvliet)
|
||||
|
||||
##### Changed
|
||||
|
||||
- Default log file directory for iOS, tvOS & watchOS is an app’s cache directory by [@xeo-it](https://github.com/xeo-it)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
## 0.3.5 (2016-02-24)
|
||||
|
||||
##### Changed
|
||||
|
||||
- Optimized performance by letting log functions take @autoclosure by [@reesemclean](https://github.com/reesemclean)
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.3.4 (2016-02-23)
|
||||
|
||||
##### Changed
|
||||
|
||||
- Optimized writing to log file by [@skreutzberger](https://github.com/skreutzberger). Thanks go to [Andy Chou](https://twitter.com/_achou) for pointing on it.
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.3.3 (2016-02-09)
|
||||
|
||||
##### Added
|
||||
|
||||
- `Flush` function to make sure all logging messages have been written out by [@prenagha](https://github.com/prenagha)
|
||||
|
||||
##### Changed
|
||||
|
||||
- Versions & tags do not start with a "v" anymore by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.3.2 (2016-02-04)
|
||||
|
||||
##### Added
|
||||
|
||||
- Easier creation of custom destinations by making certain base class functions public by [@irace](https://github.com/irace)
|
||||
- Secrets.* files are ignored by Git to act as credential-holding file in the future by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.3.1 (2016-01-11)
|
||||
|
||||
##### Added
|
||||
|
||||
- Logging of thread by [@VDKA](https://github.com/VDKA)
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.3.0 (2015-12-11)
|
||||
|
||||
#### Added
|
||||
|
||||
- File-based minimum level filters by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
|
||||
|
||||
<br/><br/>
|
||||
|
||||
## 0.2.5 (2015-12-10)
|
||||
|
||||
#### Added
|
||||
|
||||
- Support for KZLinkedConsole plugin by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Installation via Carthage for tvOS, watchOS & OSX by [@davidrothera](https://github.com/davidrothera)
|
||||
- Introduction of API limitation to allowed SwiftyBeaver to be used in Extensions by [@impossibleventures](https://github.com/impossibleventures)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.2.4 (2015-12-09)
|
||||
|
||||
#### Added
|
||||
|
||||
- Installation via Cocoapods for tvOS, watchOS2 & OSX by [@davidrothera](https://github.com/davidrothera)
|
||||
|
||||
#### Changed
|
||||
|
||||
- No date output if date format is empty by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.2.3 (2015-12-09)
|
||||
|
||||
#### Added
|
||||
|
||||
- Installation via Swift Package Manager by [@davidrothera](https://github.com/davidrothera)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.2.2 (2015-12-09)
|
||||
|
||||
#### Added
|
||||
|
||||
- Installation via Cocoapods by [@davidrothera](https://github.com/davidrothera)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Wrong level word displayed for Debug level by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.2.1 (2015-12-06)
|
||||
|
||||
#### Added
|
||||
|
||||
- Flexible level names by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Logging of all types and not just strings by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.2.0 (2015-12-05)
|
||||
|
||||
#### Added
|
||||
|
||||
- Dedicated serial queues for each destination by [@skreutzberger](https://github.com/skreutzberger)
|
||||
- Destinations are now each in a single file by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Wrong scope of `init`function by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
|
||||
<br/>
|
||||
## 0.1.1 (2015-12-01)
|
||||
|
||||
#### Added
|
||||
|
||||
- Downloadable assets by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Deployment version to make Carthage work by [@manuelvanrijn](https://github.com/manuelvanrijn)
|
||||
- License text by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
## 0.1.0 (2015-11-28)
|
||||
|
||||
#### Added
|
||||
|
||||
- Initial release by [@skreutzberger](https://github.com/skreutzberger)
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
ARG swift_version=5.0
|
||||
FROM swift:$swift_version
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
COPY Package.swift /code/Package.swift
|
||||
COPY ./Sources /code/Sources
|
||||
COPY ./Tests /code/Tests
|
||||
|
||||
RUN swift --version
|
||||
RUN swift build
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
<?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>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Sebastian Kreutzberger
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,16 @@
|
||||
// swift-tools-version:5.0
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "SwiftyBeaver",
|
||||
products: [
|
||||
.library(name: "SwiftyBeaver", targets: ["SwiftyBeaver"])
|
||||
],
|
||||
targets: [
|
||||
.target(name: "SwiftyBeaver", path: "Sources"),
|
||||
.testTarget(name: "SwiftyBeaverTests", dependencies: ["SwiftyBeaver"]),
|
||||
],
|
||||
swiftLanguageVersions: [.v5]
|
||||
)
|
||||
@@ -0,0 +1,270 @@
|
||||
<p align="center"><a href="https://swiftybeaver.com"><img src="https://cloud.githubusercontent.com/assets/564725/19889302/73b1ee84-a034-11e6-8753-2d060502397c.jpg" style="width: 888px;" alt="SwiftyBeaver"></a><br/><b>Colorful</b>, flexible, <b>lightweight</b> logging for Swift 3, Swift 4 & <b>Swift 5</b>.<br/>Great for <b>development & release</b> with support for Console, File & cloud platforms.<br/>Log <b>during release</b> to the conveniently built-in SwiftyBeaver Platform, the <b>dedicated Mac App</b> & <b>Elasticsearch</b>!<br/><br/><a href="http://docs.swiftybeaver.com">Docs</a> | <a href="https://swiftybeaver.com">Website</a> | <a href="https://twitter.com/SwiftyBeaver">Twitter</a> | <a href="#privacy">Privacy</a> | <a href="https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/LICENSE">License</a><br/></p>
|
||||
|
||||
<p align="center"><a href="https://swift.org" target="_blank"><img src="https://img.shields.io/badge/Language-Swift%203,%204%20&%205-orange.svg" alt="Language Swift 2, 3, 4 & 5"></a> <a href="https://circleci.com/gh/SwiftyBeaver/SwiftyBeaver" target="_blank"><img src="https://circleci.com/gh/SwiftyBeaver/SwiftyBeaver/tree/master.svg?style=shield" alt="CircleCI"/></a><br/><p>
|
||||
|
||||
----
|
||||
|
||||
<br/>
|
||||
|
||||
### During Development: Colored Logging to Xcode Console
|
||||
|
||||
<img src="https://cloud.githubusercontent.com/assets/564725/18608323/ac065a98-7ce6-11e6-8e1b-2a062d54a1d5.png" width="608">
|
||||
|
||||
[Learn more](http://docs.swiftybeaver.com/article/9-log-to-xcode-console) about colored logging to Xcode 8 Console with Swift 3, 4 & 5. For Swift 2.3 [use this Gist](https://gist.github.com/skreutzberger/7c396573796473ed1be2c6d15cafed34). **No need to hack Xcode 8 anymore** to get color. You can even customize the log level word (ATTENTION instead of ERROR maybe?), the general amount of displayed data and if you want to use the 💜s or replace them with something else 😉
|
||||
|
||||
<br/>
|
||||
|
||||
### During Development: Colored Logging to File
|
||||
|
||||
<img src="https://cloud.githubusercontent.com/assets/564725/18608325/b7ecd4c2-7ce6-11e6-829b-7f8f9fe6ef2f.png" width="738">
|
||||
|
||||
[Learn more](http://docs.swiftybeaver.com/article/10-log-to-file) about logging to file which is great for Terminal.app fans or to store logs on disk.
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
### On Release: Encrypted Logging to SwiftyBeaver Platform
|
||||
|
||||
<img src="https://cloud.githubusercontent.com/assets/564725/14281408/38d6a6ba-fb39-11e5-9584-34e3679bb1c5.jpg" width="700">
|
||||
|
||||
[Learn more](http://docs.swiftybeaver.com/article/11-log-to-swiftybeaver-platform) about logging to the SwiftyBeaver Platform **during release!**
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
### Browse, Search & Filter via Mac App
|
||||
|
||||

|
||||
|
||||
Conveniently access your logs during development & release with our [free Mac App](https://swiftybeaver.com).
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
### On Release: Enterprise-ready Logging to Your Private and Public Cloud
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/564725/34486363-dc501aec-efcf-11e7-92b2-1163cca9e7aa.jpg" width="700">
|
||||
|
||||
[Learn more](https://swiftybeaver.com/enterprise.html) about **legally compliant**, end-to-end encrypted logging your own cloud with **SwiftyBeaver Enterprise**. Install via Docker or manual, fully-featured free trial included!
|
||||
|
||||
<br/>
|
||||
|
||||
### Google Cloud & More
|
||||
|
||||
You can fully customize your log format, turn it into JSON, or create your own destinations. For example our [Google Cloud Destination](https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/Sources/GoogleCloudDestination.swift) is just another customized logging format which adds the powerful functionality of automatic server-side Swift logging when hosted on Google Cloud Platform.
|
||||
|
||||
<br/>
|
||||
|
||||
----
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Installation
|
||||
|
||||
- For **Swift 4 & 5** install the latest SwiftyBeaver version
|
||||
- For **Swift 3** install SwiftyBeaver 1.8.4
|
||||
- For **Swift 2** install SwiftyBeaver 0.7.0
|
||||
|
||||
<br/>
|
||||
|
||||
### Carthage
|
||||
|
||||
You can use [Carthage](https://github.com/Carthage/Carthage) to install SwiftyBeaver by adding that to your Cartfile:
|
||||
|
||||
Swift 4 & 5:
|
||||
``` Swift
|
||||
github "SwiftyBeaver/SwiftyBeaver"
|
||||
```
|
||||
|
||||
|
||||
Swift 3:
|
||||
``` Swift
|
||||
github "SwiftyBeaver/SwiftyBeaver" ~> 1.8.4
|
||||
```
|
||||
|
||||
Swift 2:
|
||||
``` Swift
|
||||
github "SwiftyBeaver/SwiftyBeaver" ~> 0.7
|
||||
```
|
||||
|
||||
<br/>
|
||||
|
||||
### Swift Package Manager
|
||||
|
||||
For [Swift Package Manager](https://swift.org/package-manager/) add the following package to your Package.swift file. Just Swift 4 & 5 are supported:
|
||||
|
||||
``` Swift
|
||||
.package(url: "https://github.com/SwiftyBeaver/SwiftyBeaver.git", .upToNextMajor(from: "1.9.0")),
|
||||
```
|
||||
|
||||
<br/>
|
||||
|
||||
### CocoaPods
|
||||
|
||||
To use [CocoaPods](https://cocoapods.org) just add this to your Podfile:
|
||||
|
||||
Swift 4 & 5:
|
||||
``` Swift
|
||||
pod 'SwiftyBeaver'
|
||||
```
|
||||
|
||||
Swift 3:
|
||||
``` Ruby
|
||||
target 'MyProject' do
|
||||
use_frameworks!
|
||||
|
||||
# Pods for MyProject
|
||||
pod 'SwiftyBeaver', '~> 1.8.4'
|
||||
end
|
||||
```
|
||||
|
||||
Swift 2:
|
||||
``` Ruby
|
||||
target 'MyProject' do
|
||||
use_frameworks!
|
||||
|
||||
# Pods for MyProject
|
||||
pod 'SwiftyBeaver', '~> 0.7'
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.build_configurations.each do |config|
|
||||
# Configure Pod targets for Xcode 8 with Swift 2.3
|
||||
config.build_settings['SWIFT_VERSION'] = '2.3'
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Usage
|
||||
|
||||
Add that near the top of your `AppDelegate.swift` to be able to use SwiftyBeaver in your whole project.
|
||||
|
||||
``` Swift
|
||||
import SwiftyBeaver
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
```
|
||||
|
||||
At the the beginning of your `AppDelegate:didFinishLaunchingWithOptions()` add the SwiftyBeaver log destinations (console, file, etc.), optionally adjust the [log format](http://docs.swiftybeaver.com/article/20-custom-format) and then you can already do the following log level calls globally:
|
||||
|
||||
``` Swift
|
||||
// add log destinations. at least one is needed!
|
||||
let console = ConsoleDestination() // log to Xcode Console
|
||||
let file = FileDestination() // log to default swiftybeaver.log file
|
||||
let cloud = SBPlatformDestination(appID: "foo", appSecret: "bar", encryptionKey: "123") // to cloud
|
||||
|
||||
// use custom format and set console output to short time, log level & message
|
||||
console.format = "$DHH:mm:ss$d $L $M"
|
||||
// or use this for JSON output: console.format = "$J"
|
||||
|
||||
// add the destinations to SwiftyBeaver
|
||||
log.addDestination(console)
|
||||
log.addDestination(file)
|
||||
log.addDestination(cloud)
|
||||
|
||||
// Now let’s log!
|
||||
log.verbose("not so important") // prio 1, VERBOSE in silver
|
||||
log.debug("something to debug") // prio 2, DEBUG in green
|
||||
log.info("a nice information") // prio 3, INFO in blue
|
||||
log.warning("oh no, that won’t be good") // prio 4, WARNING in yellow
|
||||
log.error("ouch, an error did occur!") // prio 5, ERROR in red
|
||||
|
||||
// log anything!
|
||||
log.verbose(123)
|
||||
log.info(-123.45678)
|
||||
log.warning(Date())
|
||||
log.error(["I", "like", "logs!"])
|
||||
log.error(["name": "Mr Beaver", "address": "7 Beaver Lodge"])
|
||||
|
||||
// optionally add context to a log message
|
||||
console.format = "$L: $M $X"
|
||||
log.debug("age", context: 123) // "DEBUG: age 123"
|
||||
log.info("my data", context: [1, "a", 2]) // "INFO: my data [1, \"a\", 2]"
|
||||
|
||||
```
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Server-side Swift
|
||||
|
||||
We ❤️ server-side Swift 4 & 5 and SwiftyBeaver supports it **out-of-the-box**! Try for yourself and run SwiftyBeaver inside a Ubuntu Docker container. Just install Docker and then go to your the project folder on macOS or Ubuntu and type:
|
||||
|
||||
```shell
|
||||
# create docker image, build SwiftyBeaver and run unit tests
|
||||
docker run --rm -it -v $PWD:/app swiftybeaver /bin/bash -c "cd /app ; swift build ; swift test"
|
||||
|
||||
# optionally log into container to run Swift CLI and do more stuff
|
||||
docker run --rm -it --privileged=true -v $PWD:/app swiftybeaver
|
||||
```
|
||||
|
||||
Best: for the popular server-side Swift web framework [Vapor](https://github.com/vapor/vapor) you can use **[our Vapor logging provider](https://github.com/SwiftyBeaver/SwiftyBeaver-Vapor)** which makes server logging awesome again 🙌
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Documentation
|
||||
|
||||
**Getting Started:**
|
||||
|
||||
- [Features](http://docs.swiftybeaver.com/article/7-introduction)
|
||||
- [Installation](http://docs.swiftybeaver.com/article/5-installation)
|
||||
- [Basic Setup](http://docs.swiftybeaver.com/article/6-basic-setup)
|
||||
|
||||
**Logging Destinations:**
|
||||
|
||||
- [Colored Logging to Xcode Console](http://docs.swiftybeaver.com/article/9-log-to-xcode-console)
|
||||
- [Colored Logging to File](http://docs.swiftybeaver.com/article/10-log-to-file)
|
||||
- [Encrypted Logging & Analytics to SwiftyBeaver Platform](http://docs.swiftybeaver.com/article/11-log-to-swiftybeaver-platform)
|
||||
- [Encrypted Logging & Analytics to Elasticsearch & Kibana](http://docs.swiftybeaver.com/article/34-enterprise-quick-start-via-docker)
|
||||
|
||||
|
||||
**Advanced Topics:**
|
||||
|
||||
- [Custom Format & Context](http://docs.swiftybeaver.com/article/20-custom-format)
|
||||
- [Filters](http://docs.swiftybeaver.com/article/21-filters)
|
||||
|
||||
**Stay Informed:**
|
||||
|
||||
- [Official Website](https://swiftybeaver.com)
|
||||
- [On Twitter](https://twitter.com/SwiftyBeaver)
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Privacy
|
||||
|
||||
**SwiftyBeaver is not collecting any data without you as a developer knowing about it**. That's why it is **open-source** and developed in a simple way to be easy to inspect and check what it is actually doing under the hood.
|
||||
|
||||
The only sending to servers is done if you use the `SBPlatformDestination`. That destination is meant for production logging and on default it sends your logs plus additional device information **end-to-end encrypted** to our cloud service. Our cloud service **can not decrypt the data**.
|
||||
|
||||
Instead, you install our Mac App and that Mac App downloads the encrypted logs from the cloud and decrypts and shows them to you. Additionally, the Mac App stores all data that it downloads in a local SQLite database file on your computer so that you actually "physically" own your data.
|
||||
|
||||
The business model of the SwiftyBeaver cloud service is to provide the most secure logging solution in the market. On purpose we do not provide a web UI for you because it would require us to store your encryption key on our servers.
|
||||
|
||||
**Only you can see the logging and device data** which is sent from your users' devices. Our servers just see encrypted data and do not know your decryption key.
|
||||
|
||||
SwiftyBeaver is **fully GDPR compliant** due to its focus on encryption and transparency in what data is collected and also meets **Apple’s latest requirements** on the privacy of 3rd party frameworks.
|
||||
|
||||
Our Enterprise offering is an even more secure solution where you are not using anymore our cloud service and Mac App but you send your end-to-end encrypted logs directly to your own servers and you store them in your Elasticsearch cluster. The **Enterprise offering is used by health tech** and governmental institutions which require the highest level of privacy and security.
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## End-to-End Encryption
|
||||
|
||||
SwiftyBeaver is using symmetric AES256CBC encryption in the `SBPlatformDestination` destination. No other officially supported destination uses encryption.
|
||||
|
||||
The encryption used in the `SBPlatformDestination` destination is end-to-end. The open-source SwiftyBeaver logging framework symmetrically encrypts all logging data on your client's device inside your app (iPhone, iPad, ...) before it is sent to the SwiftyBeaver Crypto Cloud. The decryption is done on your Mac which has the SwiftyBeaver Mac App installed. All logging data stays encrypted in the SwiftyBeaver Crypto Cloud due to the lack of the password.
|
||||
|
||||
You are using the encryption at your own risk. SwiftyBeaver’s authors and contributors do not take over any guarantee about the absence of potential security or cryptopgraphy issues, weaknesses, etc.; please also read the LICENSE file for details. Also if you are interested in cryptography in general, please have a look at the file AES256CBC.swift to learn more about the cryptographical implementation.
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## License
|
||||
|
||||
SwiftyBeaver Framework is released under the [MIT License](https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/LICENSE).
|
||||
@@ -0,0 +1,781 @@
|
||||
//
|
||||
// AES256CBC.swift
|
||||
// AES256CBC https://github.com/SwiftyBeaver/AES256CBC
|
||||
//
|
||||
// Created by Sebastian Kreutzberger on 2/9/16.
|
||||
// Copyright © 2016 SwiftyBeaver. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
final class AES256CBC {
|
||||
|
||||
/// returns optional encrypted string via AES-256CBC
|
||||
/// automatically generates and puts a random IV at first 16 chars
|
||||
/// the password must be exactly 32 chars long for AES-256
|
||||
class func encryptString(_ str: String, password: String) -> String? {
|
||||
if !str.isEmpty && Data(password.utf8).count == 32 {
|
||||
let iv = randomText(16)
|
||||
let key = password
|
||||
|
||||
guard let encryptedString = try? aesEncrypt(str, key: key, iv: iv) else {
|
||||
print("an error occured while encrypting")
|
||||
return nil
|
||||
}
|
||||
return iv + encryptedString
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// returns optional decrypted string via AES-256CBC
|
||||
/// IV need to be at first 16 chars, password must be 32 chars long
|
||||
class func decryptString(_ str: String, password: String) -> String? {
|
||||
if Data(str.utf8).count > 16 && Data(password.utf8).count == 32 {
|
||||
// get AES initialization vector from first 16 chars
|
||||
let iv = String(str.prefix(16))
|
||||
let encryptedString = str.replacingOccurrences(of: iv, with: "",
|
||||
options: String.CompareOptions.literal,
|
||||
range: nil) // remove IV
|
||||
|
||||
guard let decryptedString = try? aesDecrypt(encryptedString, key: password, iv: iv) else {
|
||||
print("an error occured while decrypting")
|
||||
return nil
|
||||
}
|
||||
return decryptedString
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// returns random string (uppercase & lowercase, no spaces) of 32 characters length
|
||||
/// which can be used as SHA-256 compatbile password
|
||||
class func generatePassword() -> String {
|
||||
return randomText(32)
|
||||
}
|
||||
|
||||
/// returns random text of a defined length
|
||||
public class func randomText(_ length: Int) -> String {
|
||||
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
return String((0..<length).map { _ in letters.randomElement()!})
|
||||
}
|
||||
|
||||
/// returns encrypted string, IV must be 16 chars long
|
||||
fileprivate class func aesEncrypt(_ str: String, key: String, iv: String) throws -> String {
|
||||
let keyData = key.data(using: String.Encoding.utf8)!
|
||||
let ivData = iv.data(using: String.Encoding.utf8)!
|
||||
let data = str.data(using: String.Encoding.utf8)!
|
||||
#if swift(>=5)
|
||||
let enc = try Data(AESCipher(key: keyData.bytes,
|
||||
iv: ivData.bytes).encrypt(bytes: data.bytes))
|
||||
#else
|
||||
let enc = try Data(bytes: AESCipher(key: keyData.bytes,
|
||||
iv: ivData.bytes).encrypt(bytes: data.bytes))
|
||||
#endif
|
||||
return enc.base64EncodedString(options: [])
|
||||
}
|
||||
|
||||
/// returns decrypted string, IV must be 16 chars long
|
||||
fileprivate class func aesDecrypt(_ str: String, key: String, iv: String) throws -> String {
|
||||
let keyData = key.data(using: String.Encoding.utf8)!
|
||||
let ivData = iv.data(using: String.Encoding.utf8)!
|
||||
if let data = Data(base64Encoded: str) {
|
||||
#if swift(>=5)
|
||||
let dec = try Data(AESCipher(key: keyData.bytes,
|
||||
iv: ivData.bytes).decrypt(bytes: data.bytes))
|
||||
#else
|
||||
let dec = try Data(bytes: AESCipher(key: keyData.bytes,
|
||||
iv: ivData.bytes).decrypt(bytes: data.bytes))
|
||||
#endif
|
||||
guard let decryptStr = String(data: dec, encoding: String.Encoding.utf8) else {
|
||||
throw NSError(domain: "Invalid utf8 data", code: 0, userInfo: nil)
|
||||
}
|
||||
return decryptStr
|
||||
} else {
|
||||
return "error"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// swiftlint:disable line_length
|
||||
// The following is an altered source version that only includes AES-CBC. The original software can be found at:
|
||||
// https://github.com/krzyzanowskim/CryptoSwift
|
||||
//
|
||||
// This is the original copyright notice:
|
||||
//
|
||||
// Copyright (C) 2014 Marcin Krzyżanowski <marcin.krzyzanowski@gmail.com>
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
//
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
// - This notice may not be removed or altered from any source or binary distribution.
|
||||
//
|
||||
// swiftlint:enable line_length
|
||||
|
||||
// MARK: - AESCipher
|
||||
|
||||
private typealias Key = Array<UInt8>
|
||||
|
||||
final private class AESCipher {
|
||||
|
||||
enum Error: Swift.Error {
|
||||
case blockSizeExceeded
|
||||
case dataPaddingRequired
|
||||
case invalidKeyOrInitializationVector
|
||||
case invalidInitializationVector
|
||||
}
|
||||
|
||||
enum Variant: Int {
|
||||
case aes128 = 1, aes192, aes256
|
||||
|
||||
var Nk: Int { // Nk words
|
||||
return [4, 6, 8][self.rawValue - 1]
|
||||
}
|
||||
|
||||
var Nb: Int { // Nb words
|
||||
return 4
|
||||
}
|
||||
|
||||
var Nr: Int { // Nr
|
||||
return Nk + 6
|
||||
}
|
||||
}
|
||||
|
||||
static let blockSize: Int = 16 // 128 /8
|
||||
|
||||
var variant: Variant {
|
||||
switch self.key.count * 8 {
|
||||
case 128:
|
||||
return .aes128
|
||||
case 192:
|
||||
return .aes192
|
||||
case 256:
|
||||
return .aes256
|
||||
default:
|
||||
preconditionFailure("Unknown AES variant for given key.")
|
||||
}
|
||||
}
|
||||
private let key: [UInt8]
|
||||
private let iv: [UInt8]?
|
||||
private let blockMode = CBCBlockMode()
|
||||
private lazy var expandedKey: Array<Array<UInt32>> = self.expandKey(self.key, variant: self.variant)
|
||||
private lazy var expandedKeyInv: Array<Array<UInt32>> = self.expandKeyInv(self.key, variant: self.variant)
|
||||
|
||||
private lazy var sBoxes:(sBox: [UInt32], invSBox: [UInt32]) = self.calculateSBox()
|
||||
private lazy var sBox: [UInt32] = self.sBoxes.sBox
|
||||
private lazy var sBoxInv: [UInt32] = self.sBoxes.invSBox
|
||||
|
||||
// Parameters for Linear Congruence Generators
|
||||
private let Rcon: [UInt8] = [
|
||||
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, 0x8d]
|
||||
|
||||
// swiftlint:disable line_length
|
||||
fileprivate let T0: Array<UInt32> = [0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0xdf2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x3010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0xbf0f0fb, 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x2f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x8f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0xc040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0xf05050a, 0xb59a9a2f, 0x907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x0, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x6020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x4f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0xef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0xa06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, 0x7f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x5030306, 0x1f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c]
|
||||
fileprivate let T0_INV: Array<UInt32> = [0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x2752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x728ebb2, 0x3c2b52f, 0x9a7bc586, 0xa50837d3, 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x69f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x55dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x0, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0xfe75793, 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0xaba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0xb0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0xd927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x97826cd, 0xf418596e, 0x1b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x8cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, 0xf7daec41, 0xe50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x4ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0xc25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0]
|
||||
fileprivate let T1: Array<UInt32> = [0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x1010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x5050a0f, 0x9a9a2fb5, 0x7070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x0, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x2020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0xc0c1814, 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0xb0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0xa0a141e, 0x494992db, 0x6060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x8081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x3030605, 0xf6f6f701, 0xe0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, 0x8c8c038f, 0xa1a159f8, 0x89890980, 0xd0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0xf0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a]
|
||||
fileprivate let T1_INV: Array<UInt32> = [0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1, 0x58faacab, 0x3e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0xeea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x24b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x837d3a5, 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x6dd963d, 0x53eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db, 0xa7ca147, 0xf427ce9, 0x1e84f8c9, 0x0, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0xd090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x775af4c, 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0xbd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x99fead4, 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c, 0xca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x1a83971, 0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042]
|
||||
fileprivate let T2: Array<UInt32> = [0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x1020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x4080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x50a0f05, 0x9a2fb59a, 0x70e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x9121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x0, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x2040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0xc18140c, 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0xb161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0xa141e0a, 0x4992db49, 0x60c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x8101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x3060503, 0xf6f701f6, 0xe1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, 0x8c038f8c, 0xa159f8a1, 0x89098089, 0xd1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0xf1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16]
|
||||
fileprivate let T2_INV: Array<UInt32> = [0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x2f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x8f9942b, 0x48705868, 0x458f19fd, 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, 0x2830f287, 0xbf23b2a5, 0x302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x7f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x6046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x0, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0xefdfbff, 0x850f5638, 0xae3d1ed5, 0x2d362739, 0xf0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0xa0cb167, 0x57930fe7, 0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x90e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60, 0x1f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x4f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0xbfb2e41, 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0xdff4195, 0xa8397101, 0xc08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257]
|
||||
fileprivate let T3: Array<UInt32> = [0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x2030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x80c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0xa0f0505, 0x2fb59a9a, 0xe090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x0, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x4060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x58a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, 0x3bab9090, 0xb838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0xc0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x18c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, 0x61dcbdbd, 0xd868b8b, 0xf858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x6050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x7898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, 0x38f8c8c, 0x59f8a1a1, 0x9808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616]
|
||||
fileprivate let T3_INV: Array<UInt32> = [0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x3e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, 0x30f28728, 0x23b2a5bf, 0x2ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x6d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x532e18a, 0xa475ebf6, 0xb39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x46fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x7888b89, 0xe7385b19, 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x0, 0x9838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, 0xf563885, 0x3d1ed5ae, 0x3627392d, 0xa64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0xcb1670a, 0x930fe757, 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0xe0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, 0xdec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x15d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x8deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8]
|
||||
fileprivate var U1: Array<UInt32> = [0x0, 0xb0d090e, 0x161a121c, 0x1d171b12, 0x2c342438, 0x27392d36, 0x3a2e3624, 0x31233f2a, 0x58684870, 0x5365417e, 0x4e725a6c, 0x457f5362, 0x745c6c48, 0x7f516546, 0x62467e54, 0x694b775a, 0xb0d090e0, 0xbbdd99ee, 0xa6ca82fc, 0xadc78bf2, 0x9ce4b4d8, 0x97e9bdd6, 0x8afea6c4, 0x81f3afca, 0xe8b8d890, 0xe3b5d19e, 0xfea2ca8c, 0xf5afc382, 0xc48cfca8, 0xcf81f5a6, 0xd296eeb4, 0xd99be7ba, 0x7bbb3bdb, 0x70b632d5, 0x6da129c7, 0x66ac20c9, 0x578f1fe3, 0x5c8216ed, 0x41950dff, 0x4a9804f1, 0x23d373ab, 0x28de7aa5, 0x35c961b7, 0x3ec468b9, 0xfe75793, 0x4ea5e9d, 0x19fd458f, 0x12f04c81, 0xcb6bab3b, 0xc066a235, 0xdd71b927, 0xd67cb029, 0xe75f8f03, 0xec52860d, 0xf1459d1f, 0xfa489411, 0x9303e34b, 0x980eea45, 0x8519f157, 0x8e14f859, 0xbf37c773, 0xb43ace7d, 0xa92dd56f, 0xa220dc61, 0xf66d76ad, 0xfd607fa3, 0xe07764b1, 0xeb7a6dbf, 0xda595295, 0xd1545b9b, 0xcc434089, 0xc74e4987, 0xae053edd, 0xa50837d3, 0xb81f2cc1, 0xb31225cf, 0x82311ae5, 0x893c13eb, 0x942b08f9, 0x9f2601f7, 0x46bde64d, 0x4db0ef43, 0x50a7f451, 0x5baafd5f, 0x6a89c275, 0x6184cb7b, 0x7c93d069, 0x779ed967, 0x1ed5ae3d, 0x15d8a733, 0x8cfbc21, 0x3c2b52f, 0x32e18a05, 0x39ec830b, 0x24fb9819, 0x2ff69117, 0x8dd64d76, 0x86db4478, 0x9bcc5f6a, 0x90c15664, 0xa1e2694e, 0xaaef6040, 0xb7f87b52, 0xbcf5725c, 0xd5be0506, 0xdeb30c08, 0xc3a4171a, 0xc8a91e14, 0xf98a213e, 0xf2872830, 0xef903322, 0xe49d3a2c, 0x3d06dd96, 0x360bd498, 0x2b1ccf8a, 0x2011c684, 0x1132f9ae, 0x1a3ff0a0, 0x728ebb2, 0xc25e2bc, 0x656e95e6, 0x6e639ce8, 0x737487fa, 0x78798ef4, 0x495ab1de, 0x4257b8d0, 0x5f40a3c2, 0x544daacc, 0xf7daec41, 0xfcd7e54f, 0xe1c0fe5d, 0xeacdf753, 0xdbeec879, 0xd0e3c177, 0xcdf4da65, 0xc6f9d36b, 0xafb2a431, 0xa4bfad3f, 0xb9a8b62d, 0xb2a5bf23, 0x83868009, 0x888b8907, 0x959c9215, 0x9e919b1b, 0x470a7ca1, 0x4c0775af, 0x51106ebd, 0x5a1d67b3, 0x6b3e5899, 0x60335197, 0x7d244a85, 0x7629438b, 0x1f6234d1, 0x146f3ddf, 0x97826cd, 0x2752fc3, 0x335610e9, 0x385b19e7, 0x254c02f5, 0x2e410bfb, 0x8c61d79a, 0x876cde94, 0x9a7bc586, 0x9176cc88, 0xa055f3a2, 0xab58faac, 0xb64fe1be, 0xbd42e8b0, 0xd4099fea, 0xdf0496e4, 0xc2138df6, 0xc91e84f8, 0xf83dbbd2, 0xf330b2dc, 0xee27a9ce, 0xe52aa0c0, 0x3cb1477a, 0x37bc4e74, 0x2aab5566, 0x21a65c68, 0x10856342, 0x1b886a4c, 0x69f715e, 0xd927850, 0x64d90f0a, 0x6fd40604, 0x72c31d16, 0x79ce1418, 0x48ed2b32, 0x43e0223c, 0x5ef7392e, 0x55fa3020, 0x1b79aec, 0xaba93e2, 0x17ad88f0, 0x1ca081fe, 0x2d83bed4, 0x268eb7da, 0x3b99acc8, 0x3094a5c6, 0x59dfd29c, 0x52d2db92, 0x4fc5c080, 0x44c8c98e, 0x75ebf6a4, 0x7ee6ffaa, 0x63f1e4b8, 0x68fcedb6, 0xb1670a0c, 0xba6a0302, 0xa77d1810, 0xac70111e, 0x9d532e34, 0x965e273a, 0x8b493c28, 0x80443526, 0xe90f427c, 0xe2024b72, 0xff155060, 0xf418596e, 0xc53b6644, 0xce366f4a, 0xd3217458, 0xd82c7d56, 0x7a0ca137, 0x7101a839, 0x6c16b32b, 0x671bba25, 0x5638850f, 0x5d358c01, 0x40229713, 0x4b2f9e1d, 0x2264e947, 0x2969e049, 0x347efb5b, 0x3f73f255, 0xe50cd7f, 0x55dc471, 0x184adf63, 0x1347d66d, 0xcadc31d7, 0xc1d138d9, 0xdcc623cb, 0xd7cb2ac5, 0xe6e815ef, 0xede51ce1, 0xf0f207f3, 0xfbff0efd, 0x92b479a7, 0x99b970a9, 0x84ae6bbb, 0x8fa362b5, 0xbe805d9f, 0xb58d5491, 0xa89a4f83, 0xa397468d]
|
||||
fileprivate var U2: Array<UInt32> = [0x0, 0xd090e0b, 0x1a121c16, 0x171b121d, 0x3424382c, 0x392d3627, 0x2e36243a, 0x233f2a31, 0x68487058, 0x65417e53, 0x725a6c4e, 0x7f536245, 0x5c6c4874, 0x5165467f, 0x467e5462, 0x4b775a69, 0xd090e0b0, 0xdd99eebb, 0xca82fca6, 0xc78bf2ad, 0xe4b4d89c, 0xe9bdd697, 0xfea6c48a, 0xf3afca81, 0xb8d890e8, 0xb5d19ee3, 0xa2ca8cfe, 0xafc382f5, 0x8cfca8c4, 0x81f5a6cf, 0x96eeb4d2, 0x9be7bad9, 0xbb3bdb7b, 0xb632d570, 0xa129c76d, 0xac20c966, 0x8f1fe357, 0x8216ed5c, 0x950dff41, 0x9804f14a, 0xd373ab23, 0xde7aa528, 0xc961b735, 0xc468b93e, 0xe757930f, 0xea5e9d04, 0xfd458f19, 0xf04c8112, 0x6bab3bcb, 0x66a235c0, 0x71b927dd, 0x7cb029d6, 0x5f8f03e7, 0x52860dec, 0x459d1ff1, 0x489411fa, 0x3e34b93, 0xeea4598, 0x19f15785, 0x14f8598e, 0x37c773bf, 0x3ace7db4, 0x2dd56fa9, 0x20dc61a2, 0x6d76adf6, 0x607fa3fd, 0x7764b1e0, 0x7a6dbfeb, 0x595295da, 0x545b9bd1, 0x434089cc, 0x4e4987c7, 0x53eddae, 0x837d3a5, 0x1f2cc1b8, 0x1225cfb3, 0x311ae582, 0x3c13eb89, 0x2b08f994, 0x2601f79f, 0xbde64d46, 0xb0ef434d, 0xa7f45150, 0xaafd5f5b, 0x89c2756a, 0x84cb7b61, 0x93d0697c, 0x9ed96777, 0xd5ae3d1e, 0xd8a73315, 0xcfbc2108, 0xc2b52f03, 0xe18a0532, 0xec830b39, 0xfb981924, 0xf691172f, 0xd64d768d, 0xdb447886, 0xcc5f6a9b, 0xc1566490, 0xe2694ea1, 0xef6040aa, 0xf87b52b7, 0xf5725cbc, 0xbe0506d5, 0xb30c08de, 0xa4171ac3, 0xa91e14c8, 0x8a213ef9, 0x872830f2, 0x903322ef, 0x9d3a2ce4, 0x6dd963d, 0xbd49836, 0x1ccf8a2b, 0x11c68420, 0x32f9ae11, 0x3ff0a01a, 0x28ebb207, 0x25e2bc0c, 0x6e95e665, 0x639ce86e, 0x7487fa73, 0x798ef478, 0x5ab1de49, 0x57b8d042, 0x40a3c25f, 0x4daacc54, 0xdaec41f7, 0xd7e54ffc, 0xc0fe5de1, 0xcdf753ea, 0xeec879db, 0xe3c177d0, 0xf4da65cd, 0xf9d36bc6, 0xb2a431af, 0xbfad3fa4, 0xa8b62db9, 0xa5bf23b2, 0x86800983, 0x8b890788, 0x9c921595, 0x919b1b9e, 0xa7ca147, 0x775af4c, 0x106ebd51, 0x1d67b35a, 0x3e58996b, 0x33519760, 0x244a857d, 0x29438b76, 0x6234d11f, 0x6f3ddf14, 0x7826cd09, 0x752fc302, 0x5610e933, 0x5b19e738, 0x4c02f525, 0x410bfb2e, 0x61d79a8c, 0x6cde9487, 0x7bc5869a, 0x76cc8891, 0x55f3a2a0, 0x58faacab, 0x4fe1beb6, 0x42e8b0bd, 0x99fead4, 0x496e4df, 0x138df6c2, 0x1e84f8c9, 0x3dbbd2f8, 0x30b2dcf3, 0x27a9ceee, 0x2aa0c0e5, 0xb1477a3c, 0xbc4e7437, 0xab55662a, 0xa65c6821, 0x85634210, 0x886a4c1b, 0x9f715e06, 0x9278500d, 0xd90f0a64, 0xd406046f, 0xc31d1672, 0xce141879, 0xed2b3248, 0xe0223c43, 0xf7392e5e, 0xfa302055, 0xb79aec01, 0xba93e20a, 0xad88f017, 0xa081fe1c, 0x83bed42d, 0x8eb7da26, 0x99acc83b, 0x94a5c630, 0xdfd29c59, 0xd2db9252, 0xc5c0804f, 0xc8c98e44, 0xebf6a475, 0xe6ffaa7e, 0xf1e4b863, 0xfcedb668, 0x670a0cb1, 0x6a0302ba, 0x7d1810a7, 0x70111eac, 0x532e349d, 0x5e273a96, 0x493c288b, 0x44352680, 0xf427ce9, 0x24b72e2, 0x155060ff, 0x18596ef4, 0x3b6644c5, 0x366f4ace, 0x217458d3, 0x2c7d56d8, 0xca1377a, 0x1a83971, 0x16b32b6c, 0x1bba2567, 0x38850f56, 0x358c015d, 0x22971340, 0x2f9e1d4b, 0x64e94722, 0x69e04929, 0x7efb5b34, 0x73f2553f, 0x50cd7f0e, 0x5dc47105, 0x4adf6318, 0x47d66d13, 0xdc31d7ca, 0xd138d9c1, 0xc623cbdc, 0xcb2ac5d7, 0xe815efe6, 0xe51ce1ed, 0xf207f3f0, 0xff0efdfb, 0xb479a792, 0xb970a999, 0xae6bbb84, 0xa362b58f, 0x805d9fbe, 0x8d5491b5, 0x9a4f83a8, 0x97468da3]
|
||||
fileprivate var U3: Array<UInt32> = [0x0, 0x90e0b0d, 0x121c161a, 0x1b121d17, 0x24382c34, 0x2d362739, 0x36243a2e, 0x3f2a3123, 0x48705868, 0x417e5365, 0x5a6c4e72, 0x5362457f, 0x6c48745c, 0x65467f51, 0x7e546246, 0x775a694b, 0x90e0b0d0, 0x99eebbdd, 0x82fca6ca, 0x8bf2adc7, 0xb4d89ce4, 0xbdd697e9, 0xa6c48afe, 0xafca81f3, 0xd890e8b8, 0xd19ee3b5, 0xca8cfea2, 0xc382f5af, 0xfca8c48c, 0xf5a6cf81, 0xeeb4d296, 0xe7bad99b, 0x3bdb7bbb, 0x32d570b6, 0x29c76da1, 0x20c966ac, 0x1fe3578f, 0x16ed5c82, 0xdff4195, 0x4f14a98, 0x73ab23d3, 0x7aa528de, 0x61b735c9, 0x68b93ec4, 0x57930fe7, 0x5e9d04ea, 0x458f19fd, 0x4c8112f0, 0xab3bcb6b, 0xa235c066, 0xb927dd71, 0xb029d67c, 0x8f03e75f, 0x860dec52, 0x9d1ff145, 0x9411fa48, 0xe34b9303, 0xea45980e, 0xf1578519, 0xf8598e14, 0xc773bf37, 0xce7db43a, 0xd56fa92d, 0xdc61a220, 0x76adf66d, 0x7fa3fd60, 0x64b1e077, 0x6dbfeb7a, 0x5295da59, 0x5b9bd154, 0x4089cc43, 0x4987c74e, 0x3eddae05, 0x37d3a508, 0x2cc1b81f, 0x25cfb312, 0x1ae58231, 0x13eb893c, 0x8f9942b, 0x1f79f26, 0xe64d46bd, 0xef434db0, 0xf45150a7, 0xfd5f5baa, 0xc2756a89, 0xcb7b6184, 0xd0697c93, 0xd967779e, 0xae3d1ed5, 0xa73315d8, 0xbc2108cf, 0xb52f03c2, 0x8a0532e1, 0x830b39ec, 0x981924fb, 0x91172ff6, 0x4d768dd6, 0x447886db, 0x5f6a9bcc, 0x566490c1, 0x694ea1e2, 0x6040aaef, 0x7b52b7f8, 0x725cbcf5, 0x506d5be, 0xc08deb3, 0x171ac3a4, 0x1e14c8a9, 0x213ef98a, 0x2830f287, 0x3322ef90, 0x3a2ce49d, 0xdd963d06, 0xd498360b, 0xcf8a2b1c, 0xc6842011, 0xf9ae1132, 0xf0a01a3f, 0xebb20728, 0xe2bc0c25, 0x95e6656e, 0x9ce86e63, 0x87fa7374, 0x8ef47879, 0xb1de495a, 0xb8d04257, 0xa3c25f40, 0xaacc544d, 0xec41f7da, 0xe54ffcd7, 0xfe5de1c0, 0xf753eacd, 0xc879dbee, 0xc177d0e3, 0xda65cdf4, 0xd36bc6f9, 0xa431afb2, 0xad3fa4bf, 0xb62db9a8, 0xbf23b2a5, 0x80098386, 0x8907888b, 0x9215959c, 0x9b1b9e91, 0x7ca1470a, 0x75af4c07, 0x6ebd5110, 0x67b35a1d, 0x58996b3e, 0x51976033, 0x4a857d24, 0x438b7629, 0x34d11f62, 0x3ddf146f, 0x26cd0978, 0x2fc30275, 0x10e93356, 0x19e7385b, 0x2f5254c, 0xbfb2e41, 0xd79a8c61, 0xde94876c, 0xc5869a7b, 0xcc889176, 0xf3a2a055, 0xfaacab58, 0xe1beb64f, 0xe8b0bd42, 0x9fead409, 0x96e4df04, 0x8df6c213, 0x84f8c91e, 0xbbd2f83d, 0xb2dcf330, 0xa9ceee27, 0xa0c0e52a, 0x477a3cb1, 0x4e7437bc, 0x55662aab, 0x5c6821a6, 0x63421085, 0x6a4c1b88, 0x715e069f, 0x78500d92, 0xf0a64d9, 0x6046fd4, 0x1d1672c3, 0x141879ce, 0x2b3248ed, 0x223c43e0, 0x392e5ef7, 0x302055fa, 0x9aec01b7, 0x93e20aba, 0x88f017ad, 0x81fe1ca0, 0xbed42d83, 0xb7da268e, 0xacc83b99, 0xa5c63094, 0xd29c59df, 0xdb9252d2, 0xc0804fc5, 0xc98e44c8, 0xf6a475eb, 0xffaa7ee6, 0xe4b863f1, 0xedb668fc, 0xa0cb167, 0x302ba6a, 0x1810a77d, 0x111eac70, 0x2e349d53, 0x273a965e, 0x3c288b49, 0x35268044, 0x427ce90f, 0x4b72e202, 0x5060ff15, 0x596ef418, 0x6644c53b, 0x6f4ace36, 0x7458d321, 0x7d56d82c, 0xa1377a0c, 0xa8397101, 0xb32b6c16, 0xba25671b, 0x850f5638, 0x8c015d35, 0x97134022, 0x9e1d4b2f, 0xe9472264, 0xe0492969, 0xfb5b347e, 0xf2553f73, 0xcd7f0e50, 0xc471055d, 0xdf63184a, 0xd66d1347, 0x31d7cadc, 0x38d9c1d1, 0x23cbdcc6, 0x2ac5d7cb, 0x15efe6e8, 0x1ce1ede5, 0x7f3f0f2, 0xefdfbff, 0x79a792b4, 0x70a999b9, 0x6bbb84ae, 0x62b58fa3, 0x5d9fbe80, 0x5491b58d, 0x4f83a89a, 0x468da397]
|
||||
fileprivate var U4: Array<UInt32> = [0x0, 0xe0b0d09, 0x1c161a12, 0x121d171b, 0x382c3424, 0x3627392d, 0x243a2e36, 0x2a31233f, 0x70586848, 0x7e536541, 0x6c4e725a, 0x62457f53, 0x48745c6c, 0x467f5165, 0x5462467e, 0x5a694b77, 0xe0b0d090, 0xeebbdd99, 0xfca6ca82, 0xf2adc78b, 0xd89ce4b4, 0xd697e9bd, 0xc48afea6, 0xca81f3af, 0x90e8b8d8, 0x9ee3b5d1, 0x8cfea2ca, 0x82f5afc3, 0xa8c48cfc, 0xa6cf81f5, 0xb4d296ee, 0xbad99be7, 0xdb7bbb3b, 0xd570b632, 0xc76da129, 0xc966ac20, 0xe3578f1f, 0xed5c8216, 0xff41950d, 0xf14a9804, 0xab23d373, 0xa528de7a, 0xb735c961, 0xb93ec468, 0x930fe757, 0x9d04ea5e, 0x8f19fd45, 0x8112f04c, 0x3bcb6bab, 0x35c066a2, 0x27dd71b9, 0x29d67cb0, 0x3e75f8f, 0xdec5286, 0x1ff1459d, 0x11fa4894, 0x4b9303e3, 0x45980eea, 0x578519f1, 0x598e14f8, 0x73bf37c7, 0x7db43ace, 0x6fa92dd5, 0x61a220dc, 0xadf66d76, 0xa3fd607f, 0xb1e07764, 0xbfeb7a6d, 0x95da5952, 0x9bd1545b, 0x89cc4340, 0x87c74e49, 0xddae053e, 0xd3a50837, 0xc1b81f2c, 0xcfb31225, 0xe582311a, 0xeb893c13, 0xf9942b08, 0xf79f2601, 0x4d46bde6, 0x434db0ef, 0x5150a7f4, 0x5f5baafd, 0x756a89c2, 0x7b6184cb, 0x697c93d0, 0x67779ed9, 0x3d1ed5ae, 0x3315d8a7, 0x2108cfbc, 0x2f03c2b5, 0x532e18a, 0xb39ec83, 0x1924fb98, 0x172ff691, 0x768dd64d, 0x7886db44, 0x6a9bcc5f, 0x6490c156, 0x4ea1e269, 0x40aaef60, 0x52b7f87b, 0x5cbcf572, 0x6d5be05, 0x8deb30c, 0x1ac3a417, 0x14c8a91e, 0x3ef98a21, 0x30f28728, 0x22ef9033, 0x2ce49d3a, 0x963d06dd, 0x98360bd4, 0x8a2b1ccf, 0x842011c6, 0xae1132f9, 0xa01a3ff0, 0xb20728eb, 0xbc0c25e2, 0xe6656e95, 0xe86e639c, 0xfa737487, 0xf478798e, 0xde495ab1, 0xd04257b8, 0xc25f40a3, 0xcc544daa, 0x41f7daec, 0x4ffcd7e5, 0x5de1c0fe, 0x53eacdf7, 0x79dbeec8, 0x77d0e3c1, 0x65cdf4da, 0x6bc6f9d3, 0x31afb2a4, 0x3fa4bfad, 0x2db9a8b6, 0x23b2a5bf, 0x9838680, 0x7888b89, 0x15959c92, 0x1b9e919b, 0xa1470a7c, 0xaf4c0775, 0xbd51106e, 0xb35a1d67, 0x996b3e58, 0x97603351, 0x857d244a, 0x8b762943, 0xd11f6234, 0xdf146f3d, 0xcd097826, 0xc302752f, 0xe9335610, 0xe7385b19, 0xf5254c02, 0xfb2e410b, 0x9a8c61d7, 0x94876cde, 0x869a7bc5, 0x889176cc, 0xa2a055f3, 0xacab58fa, 0xbeb64fe1, 0xb0bd42e8, 0xead4099f, 0xe4df0496, 0xf6c2138d, 0xf8c91e84, 0xd2f83dbb, 0xdcf330b2, 0xceee27a9, 0xc0e52aa0, 0x7a3cb147, 0x7437bc4e, 0x662aab55, 0x6821a65c, 0x42108563, 0x4c1b886a, 0x5e069f71, 0x500d9278, 0xa64d90f, 0x46fd406, 0x1672c31d, 0x1879ce14, 0x3248ed2b, 0x3c43e022, 0x2e5ef739, 0x2055fa30, 0xec01b79a, 0xe20aba93, 0xf017ad88, 0xfe1ca081, 0xd42d83be, 0xda268eb7, 0xc83b99ac, 0xc63094a5, 0x9c59dfd2, 0x9252d2db, 0x804fc5c0, 0x8e44c8c9, 0xa475ebf6, 0xaa7ee6ff, 0xb863f1e4, 0xb668fced, 0xcb1670a, 0x2ba6a03, 0x10a77d18, 0x1eac7011, 0x349d532e, 0x3a965e27, 0x288b493c, 0x26804435, 0x7ce90f42, 0x72e2024b, 0x60ff1550, 0x6ef41859, 0x44c53b66, 0x4ace366f, 0x58d32174, 0x56d82c7d, 0x377a0ca1, 0x397101a8, 0x2b6c16b3, 0x25671bba, 0xf563885, 0x15d358c, 0x13402297, 0x1d4b2f9e, 0x472264e9, 0x492969e0, 0x5b347efb, 0x553f73f2, 0x7f0e50cd, 0x71055dc4, 0x63184adf, 0x6d1347d6, 0xd7cadc31, 0xd9c1d138, 0xcbdcc623, 0xc5d7cb2a, 0xefe6e815, 0xe1ede51c, 0xf3f0f207, 0xfdfbff0e, 0xa792b479, 0xa999b970, 0xbb84ae6b, 0xb58fa362, 0x9fbe805d, 0x91b58d54, 0x83a89a4f, 0x8da39746]
|
||||
// swiftlint:enable line_length
|
||||
|
||||
init(key: [UInt8], iv: [UInt8]) throws {
|
||||
self.key = key
|
||||
self.iv = iv
|
||||
}
|
||||
|
||||
/**
|
||||
Encrypt message. If padding is necessary, then PKCS7 padding is added and needs to be removed after decryption.
|
||||
|
||||
- parameter message: Plaintext data
|
||||
- parameter padding: Optional padding
|
||||
|
||||
- returns: Encrypted data
|
||||
*/
|
||||
|
||||
func encrypt(bytes: [UInt8]) throws -> [UInt8] {
|
||||
let finalBytes = PKCS7().add(bytes: bytes, blockSize: AESCipher.blockSize)
|
||||
let blocks = finalBytes.chunks(size: AESCipher.blockSize)
|
||||
return try blockMode.encrypt(blocks: blocks, iv: self.iv, cipherOperation: encrypt)
|
||||
}
|
||||
|
||||
private func encrypt(block: [UInt8]) -> [UInt8]? {
|
||||
let rounds = self.variant.Nr
|
||||
let rk = self.expandedKey
|
||||
var b = toUInt32Array(slice: block[block.startIndex..<block.endIndex])
|
||||
|
||||
var t = [UInt32](repeating: 0, count: 4)
|
||||
|
||||
for r in 0..<rounds - 1 {
|
||||
t[0] = b[0] ^ rk[r][0]
|
||||
t[1] = b[1] ^ rk[r][1]
|
||||
t[2] = b[2] ^ rk[r][2]
|
||||
t[3] = b[3] ^ rk[r][3]
|
||||
|
||||
let lb00 = T0[Int(t[0] & 0xFF)]
|
||||
let lb01 = T1[Int((t[1] >> 8) & 0xFF)]
|
||||
let lb02 = T2[Int((t[2] >> 16) & 0xFF)]
|
||||
let lb03 = T3[Int(t[3] >> 24)]
|
||||
b[0] = lb00 ^ lb01 ^ lb02 ^ lb03
|
||||
|
||||
let lb10 = T0[Int(t[1] & 0xFF)]
|
||||
let lb11 = T1[Int((t[2] >> 8) & 0xFF)]
|
||||
let lb12 = T2[Int((t[3] >> 16) & 0xFF)]
|
||||
let lb13 = T3[Int(t[0] >> 24)]
|
||||
b[1] = lb10 ^ lb11 ^ lb12 ^ lb13
|
||||
|
||||
let lb20 = T0[Int(t[2] & 0xFF)]
|
||||
let lb21 = T1[Int((t[3] >> 8) & 0xFF)]
|
||||
let lb22 = T2[Int((t[0] >> 16) & 0xFF)]
|
||||
let lb23 = T3[Int(t[1] >> 24)]
|
||||
b[2] = lb20 ^ lb21 ^ lb22 ^ lb23
|
||||
|
||||
let lb30 = T0[Int(t[3] & 0xFF)]
|
||||
let lb31 = T1[Int((t[0] >> 8) & 0xFF)]
|
||||
let lb32 = T2[Int((t[1] >> 16) & 0xFF)]
|
||||
let lb33 = T3[Int(t[2] >> 24)]
|
||||
b[3] = lb30 ^ lb31 ^ lb32 ^ lb33
|
||||
}
|
||||
|
||||
// last round
|
||||
let r = rounds - 1
|
||||
|
||||
t[0] = b[0] ^ rk[r][0]
|
||||
t[1] = b[1] ^ rk[r][1]
|
||||
t[2] = b[2] ^ rk[r][2]
|
||||
t[3] = b[3] ^ rk[r][3]
|
||||
|
||||
// rounds
|
||||
b[0] = F1(t[0], t[1], t[2], t[3]) ^ rk[rounds][0]
|
||||
b[1] = F1(t[1], t[2], t[3], t[0]) ^ rk[rounds][1]
|
||||
b[2] = F1(t[2], t[3], t[0], t[1]) ^ rk[rounds][2]
|
||||
b[3] = F1(t[3], t[0], t[1], t[2]) ^ rk[rounds][3]
|
||||
|
||||
var out = [UInt8]()
|
||||
out.reserveCapacity(b.count * 4)
|
||||
for num in b {
|
||||
out.append(UInt8(num & 0xFF))
|
||||
out.append(UInt8((num >> 8) & 0xFF))
|
||||
out.append(UInt8((num >> 16) & 0xFF))
|
||||
out.append(UInt8((num >> 24) & 0xFF))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func decrypt(bytes: [UInt8]) throws -> [UInt8] {
|
||||
if (bytes.count % AESCipher.blockSize) != 0 {
|
||||
throw Error.blockSizeExceeded
|
||||
}
|
||||
|
||||
let blocks = bytes.chunks(size: AESCipher.blockSize)
|
||||
return try PKCS7().remove(bytes: blockMode.decrypt(blocks: blocks, iv: self.iv, cipherOperation: decrypt), blockSize: AESCipher.blockSize)
|
||||
}
|
||||
|
||||
private func decrypt(block: [UInt8]) -> [UInt8]? {
|
||||
let rounds = self.variant.Nr
|
||||
let rk = expandedKeyInv
|
||||
var b = toUInt32Array(slice: block[block.startIndex..<block.endIndex])
|
||||
|
||||
var t = [UInt32](repeating: 0, count: 4)
|
||||
|
||||
for r in (2...rounds).reversed() {
|
||||
t[0] = b[0] ^ rk[r][0]
|
||||
t[1] = b[1] ^ rk[r][1]
|
||||
t[2] = b[2] ^ rk[r][2]
|
||||
t[3] = b[3] ^ rk[r][3]
|
||||
|
||||
let b00 = T0_INV[Int(t[0] & 0xFF)]
|
||||
let b01 = T1_INV[Int((t[3] >> 8) & 0xFF)]
|
||||
let b02 = T2_INV[Int((t[2] >> 16) & 0xFF)]
|
||||
let b03 = T3_INV[Int(t[1] >> 24)]
|
||||
b[0] = b00 ^ b01 ^ b02 ^ b03
|
||||
|
||||
let b10 = T0_INV[Int(t[1] & 0xFF)]
|
||||
let b11 = T1_INV[Int((t[0] >> 8) & 0xFF)]
|
||||
let b12 = T2_INV[Int((t[3] >> 16) & 0xFF)]
|
||||
let b13 = T3_INV[Int(t[2] >> 24)]
|
||||
b[1] = b10 ^ b11 ^ b12 ^ b13
|
||||
|
||||
let b20 = T0_INV[Int(t[2] & 0xFF)]
|
||||
let b21 = T1_INV[Int((t[1] >> 8) & 0xFF)]
|
||||
let b22 = T2_INV[Int((t[0] >> 16) & 0xFF)]
|
||||
let b23 = T3_INV[Int(t[3] >> 24)]
|
||||
b[2] = b20 ^ b21 ^ b22 ^ b23
|
||||
|
||||
let b30 = T0_INV[Int(t[3] & 0xFF)]
|
||||
let b31 = T1_INV[Int((t[2] >> 8) & 0xFF)]
|
||||
let b32 = T2_INV[Int((t[1] >> 16) & 0xFF)]
|
||||
let b33 = T3_INV[Int(t[0] >> 24)]
|
||||
b[3] = b30 ^ b31 ^ b32 ^ b33
|
||||
}
|
||||
|
||||
// last round
|
||||
t[0] = b[0] ^ rk[1][0]
|
||||
t[1] = b[1] ^ rk[1][1]
|
||||
t[2] = b[2] ^ rk[1][2]
|
||||
t[3] = b[3] ^ rk[1][3]
|
||||
|
||||
// rounds
|
||||
|
||||
let lb00 = sBoxInv[Int(B0(t[0]))]
|
||||
let lb01 = (sBoxInv[Int(B1(t[3]))] << 8)
|
||||
let lb02 = (sBoxInv[Int(B2(t[2]))] << 16)
|
||||
let lb03 = (sBoxInv[Int(B3(t[1]))] << 24)
|
||||
b[0] = lb00 | lb01 | lb02 | lb03 ^ rk[0][0]
|
||||
|
||||
let lb10 = sBoxInv[Int(B0(t[1]))]
|
||||
let lb11 = (sBoxInv[Int(B1(t[0]))] << 8)
|
||||
let lb12 = (sBoxInv[Int(B2(t[3]))] << 16)
|
||||
let lb13 = (sBoxInv[Int(B3(t[2]))] << 24)
|
||||
b[1] = lb10 | lb11 | lb12 | lb13 ^ rk[0][1]
|
||||
|
||||
let lb20 = sBoxInv[Int(B0(t[2]))]
|
||||
let lb21 = (sBoxInv[Int(B1(t[1]))] << 8)
|
||||
let lb22 = (sBoxInv[Int(B2(t[0]))] << 16)
|
||||
let lb23 = (sBoxInv[Int(B3(t[3]))] << 24)
|
||||
b[2] = lb20 | lb21 | lb22 | lb23 ^ rk[0][2]
|
||||
|
||||
let lb30 = sBoxInv[Int(B0(t[3]))]
|
||||
let lb31 = (sBoxInv[Int(B1(t[2]))] << 8)
|
||||
let lb32 = (sBoxInv[Int(B2(t[1]))] << 16)
|
||||
let lb33 = (sBoxInv[Int(B3(t[0]))] << 24)
|
||||
b[3] = lb30 | lb31 | lb32 | lb33 ^ rk[0][3]
|
||||
|
||||
var out = [UInt8]()
|
||||
out.reserveCapacity(b.count * 4)
|
||||
for num in b {
|
||||
out.append(UInt8(num & 0xFF))
|
||||
out.append(UInt8((num >> 8) & 0xFF))
|
||||
out.append(UInt8((num >> 16) & 0xFF))
|
||||
out.append(UInt8((num >> 24) & 0xFF))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
fileprivate func expandKeyInv(_ key: Key, variant: Variant) -> Array<Array<UInt32>> {
|
||||
let rounds = variant.Nr
|
||||
var rk2: Array<Array<UInt32>> = expandKey(key, variant: variant)
|
||||
|
||||
for r in 1..<rounds {
|
||||
var w: UInt32
|
||||
|
||||
w = rk2[r][0]
|
||||
let u1 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
|
||||
let u2 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
|
||||
rk2[r][0] = u1 ^ u2
|
||||
|
||||
w = rk2[r][1]
|
||||
let u11 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
|
||||
let u12 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
|
||||
rk2[r][1] = u11 ^ u12
|
||||
|
||||
w = rk2[r][2]
|
||||
let u22 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
|
||||
let u23 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
|
||||
rk2[r][2] = u22 ^ u23
|
||||
|
||||
w = rk2[r][3]
|
||||
let u33 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
|
||||
let u34 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
|
||||
rk2[r][3] = u33 ^ u34
|
||||
}
|
||||
|
||||
return rk2
|
||||
}
|
||||
|
||||
fileprivate func expandKey(_ key: Key, variant: Variant) -> Array<Array<UInt32>> {
|
||||
|
||||
func convertExpandedKey(_ expanded: Array<UInt8>) -> Array<Array<UInt32>> {
|
||||
var arr = Array<UInt32>()
|
||||
for idx in stride(from: expanded.startIndex, to: expanded.endIndex, by: 4) {
|
||||
let four = Array(expanded[idx..<idx.advanced(by: 4)].reversed())
|
||||
let num = UInt32(bytes: four)
|
||||
arr.append(num)
|
||||
}
|
||||
|
||||
var allarr = Array<Array<UInt32>>()
|
||||
for idx in stride(from: arr.startIndex, to: arr.endIndex, by: 4) {
|
||||
allarr.append(Array(arr[idx..<idx.advanced(by: 4)]))
|
||||
}
|
||||
return allarr
|
||||
}
|
||||
|
||||
/*
|
||||
* Function used in the Key Expansion routine that takes a four-byte
|
||||
* input word and applies an S-box to each of the four bytes to
|
||||
* produce an output word.
|
||||
*/
|
||||
func subWord(_ word: Array<UInt8>) -> Array<UInt8> {
|
||||
var result = word
|
||||
for i in 0..<4 {
|
||||
result[i] = UInt8(sBox[Int(word[i])])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
var w = Array<UInt8>(repeating: 0, count: variant.Nb * (variant.Nr + 1) * 4)
|
||||
for i in 0..<variant.Nk {
|
||||
for wordIdx in 0..<4 {
|
||||
w[(4*i)+wordIdx] = key[(4*i)+wordIdx]
|
||||
}
|
||||
}
|
||||
|
||||
var tmp: Array<UInt8>
|
||||
|
||||
for i in variant.Nk..<variant.Nb * (variant.Nr + 1) {
|
||||
tmp = Array<UInt8>(repeating: 0, count: 4)
|
||||
|
||||
for wordIdx in 0..<4 {
|
||||
tmp[wordIdx] = w[4*(i-1)+wordIdx]
|
||||
}
|
||||
if (i % variant.Nk) == 0 {
|
||||
tmp = subWord(rotateLeft(UInt32(bytes: tmp), by: 8).bytes(totalBytes: MemoryLayout<UInt32>.size))
|
||||
tmp[0] = tmp.first! ^ Rcon[i/variant.Nk]
|
||||
} else if variant.Nk > 6 && (i % variant.Nk) == 4 {
|
||||
tmp = subWord(tmp)
|
||||
}
|
||||
|
||||
// xor array of bytes
|
||||
for wordIdx in 0..<4 {
|
||||
w[4*i+wordIdx] = w[4*(i-variant.Nk)+wordIdx]^tmp[wordIdx]
|
||||
}
|
||||
}
|
||||
return convertExpandedKey(w)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AESCipher {
|
||||
|
||||
fileprivate func B0(_ x: UInt32) -> UInt32 {
|
||||
return x & 0xFF
|
||||
}
|
||||
|
||||
fileprivate func B1(_ x: UInt32) -> UInt32 {
|
||||
return (x >> 8) & 0xFF
|
||||
}
|
||||
|
||||
fileprivate func B2(_ x: UInt32) -> UInt32 {
|
||||
return (x >> 16) & 0xFF
|
||||
}
|
||||
|
||||
fileprivate func B3(_ x: UInt32) -> UInt32 {
|
||||
return (x >> 24) & 0xFF
|
||||
}
|
||||
|
||||
fileprivate func F1(_ x0: UInt32, _ x1: UInt32, _ x2: UInt32, _ x3: UInt32) -> UInt32 {
|
||||
var result: UInt32 = 0
|
||||
result |= UInt32(B1(T0[Int(x0 & 255)]))
|
||||
result |= UInt32(B1(T0[Int((x1 >> 8) & 255)])) << 8
|
||||
result |= UInt32(B1(T0[Int((x2 >> 16) & 255)])) << 16
|
||||
result |= UInt32(B1(T0[Int(x3 >> 24)])) << 24
|
||||
return result
|
||||
}
|
||||
|
||||
fileprivate func calculateSBox() -> (sBox: Array<UInt32>, invSBox: Array<UInt32>) {
|
||||
var sbox = Array<UInt32>(repeating: 0, count: 256)
|
||||
var invsbox = sbox
|
||||
sbox[0] = 0x63
|
||||
|
||||
var p: UInt8 = 1, q: UInt8 = 1
|
||||
|
||||
repeat {
|
||||
#if swift(>=4.0)
|
||||
p = p ^ (UInt8(truncatingIfNeeded: Int(p) << 1) ^ ((p & 0x80) == 0x80 ? 0x1B : 0))
|
||||
#else
|
||||
p = p ^ (UInt8(truncatingBitPattern: Int(p) << 1) ^ ((p & 0x80) == 0x80 ? 0x1B : 0))
|
||||
|
||||
#endif
|
||||
q ^= q << 1
|
||||
q ^= q << 2
|
||||
q ^= q << 4
|
||||
q ^= (q & 0x80) == 0x80 ? 0x09 : 0
|
||||
|
||||
let s = 0x63 ^ q ^ rotateLeft(q, by: 1) ^ rotateLeft(q, by: 2) ^ rotateLeft(q, by: 3) ^ rotateLeft(q, by: 4)
|
||||
|
||||
sbox[Int(p)] = UInt32(s)
|
||||
invsbox[Int(s)] = UInt32(p)
|
||||
} while (p != 1)
|
||||
|
||||
return (sBox: sbox, invSBox: invsbox)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Foundation
|
||||
|
||||
extension AESCipher {
|
||||
|
||||
convenience init(key: String, iv: String) throws {
|
||||
guard let kkey = key.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes,
|
||||
// swiftlint:disable conditional_binding_cascade
|
||||
let iiv = iv.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes else {
|
||||
// swiftlint:disable conditional_binding_cascade
|
||||
throw Error.invalidKeyOrInitializationVector
|
||||
}
|
||||
|
||||
try self.init(key: kkey, iv: iiv)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CBC
|
||||
|
||||
// I have no better name for that
|
||||
private typealias CipherOperationOnBlock = ([UInt8]) -> [UInt8]?
|
||||
|
||||
private struct CBCBlockMode {
|
||||
enum BlockError: Swift.Error {
|
||||
case MissingInitializationVector
|
||||
}
|
||||
|
||||
func encrypt(blocks: [[UInt8]], iv: [UInt8]?, cipherOperation: CipherOperationOnBlock) throws -> [UInt8] {
|
||||
precondition(!blocks.isEmpty)
|
||||
guard let iv = iv else {
|
||||
throw BlockError.MissingInitializationVector
|
||||
}
|
||||
|
||||
var out: [UInt8] = [UInt8]()
|
||||
out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
|
||||
var prevCiphertext = iv // for the first time prevCiphertext = iv
|
||||
for plaintext in blocks {
|
||||
if let encrypted = cipherOperation(xor(prevCiphertext, plaintext)) {
|
||||
out.append(contentsOf: encrypted)
|
||||
prevCiphertext = encrypted
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func decrypt(blocks: [[UInt8]], iv: [UInt8]?, cipherOperation: CipherOperationOnBlock) throws -> [UInt8] {
|
||||
precondition(!blocks.isEmpty)
|
||||
guard let iv = iv else {
|
||||
throw BlockError.MissingInitializationVector
|
||||
}
|
||||
|
||||
var out: [UInt8] = [UInt8]()
|
||||
out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
|
||||
var prevCiphertext = iv // for the first time prevCiphertext = iv
|
||||
for ciphertext in blocks {
|
||||
if let decrypted = cipherOperation(ciphertext) { // decrypt
|
||||
out.append(contentsOf: xor(prevCiphertext, decrypted))
|
||||
}
|
||||
prevCiphertext = ciphertext
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Padding
|
||||
|
||||
private struct PKCS7 {
|
||||
|
||||
init() {
|
||||
}
|
||||
|
||||
func add(bytes: [UInt8], blockSize: Int) -> [UInt8] {
|
||||
let padding = UInt8(blockSize - (bytes.count % blockSize))
|
||||
var withPadding = bytes
|
||||
// The value of each added byte is the number of bytes that are added
|
||||
for _ in 0..<padding {
|
||||
withPadding.append(contentsOf: [UInt8(padding)])
|
||||
}
|
||||
return withPadding
|
||||
}
|
||||
|
||||
func remove(bytes: [UInt8], blockSize: Int?) -> [UInt8] {
|
||||
let lastByte = bytes.last!
|
||||
let padding = Int(lastByte) // last byte
|
||||
let finalLength = bytes.count - padding
|
||||
|
||||
if finalLength < 0 {
|
||||
return bytes
|
||||
}
|
||||
|
||||
if padding >= 1 {
|
||||
return Array(bytes[0..<finalLength])
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Utils
|
||||
|
||||
private func xor(_ a: Array<UInt8>, _ b: Array<UInt8>) -> Array<UInt8> {
|
||||
var xored = Array<UInt8>(repeating: 0, count: min(a.count, b.count))
|
||||
for i in 0..<xored.count {
|
||||
xored[i] = a[i] ^ b[i]
|
||||
}
|
||||
return xored
|
||||
}
|
||||
|
||||
private func rotateLeft(_ value: UInt8, by: UInt8) -> UInt8 {
|
||||
return ((value << by) & 0xFF) | (value >> (8 - by))
|
||||
}
|
||||
|
||||
private func rotateLeft(_ value: UInt32, by: UInt32) -> UInt32 {
|
||||
return ((value << by) & 0xFFFFFFFF) | (value >> (32 - by))
|
||||
}
|
||||
|
||||
private protocol BitshiftOperationsType {
|
||||
static func << (lhs: Self, rhs: Self) -> Self
|
||||
}
|
||||
|
||||
private protocol ByteConvertible {
|
||||
init(_ value: UInt8)
|
||||
init(truncatingBitPattern: UInt64)
|
||||
}
|
||||
|
||||
#if swift(>=4.0)
|
||||
#else
|
||||
extension UInt32: BitshiftOperationsType, ByteConvertible { }
|
||||
#endif
|
||||
|
||||
fileprivate extension UInt32 {
|
||||
init<T: Collection>(bytes: T) where T.Iterator.Element == UInt8, T.Index == Int {
|
||||
self = bytes.toInteger()
|
||||
}
|
||||
|
||||
func bytes(totalBytes: Int = MemoryLayout<UInt32>.size) -> Array<UInt8> {
|
||||
return arrayOfBytes(value: self, length: totalBytes)
|
||||
}
|
||||
}
|
||||
|
||||
private func toUInt32Array(slice: ArraySlice<UInt8>) -> Array<UInt32> {
|
||||
var result = Array<UInt32>()
|
||||
result.reserveCapacity(16)
|
||||
for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout<UInt32>.size) {
|
||||
var val: UInt32 = 0
|
||||
val |= slice.count > 3 ? UInt32(slice[idx.advanced(by: 3)]) << 24 : 0
|
||||
val |= slice.count > 2 ? UInt32(slice[idx.advanced(by: 2)]) << 16 : 0
|
||||
val |= slice.count > 1 ? UInt32(slice[idx.advanced(by: 1)]) << 8 : 0
|
||||
val |= !slice.isEmpty ? UInt32(slice[idx]) : 0
|
||||
result.append(val)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/// Array of bytes, little-endian representation. Don't use if not necessary.
|
||||
/// I found this method slow
|
||||
private func arrayOfBytes<T>(value: T, length: Int? = nil) -> Array<UInt8> {
|
||||
let totalBytes = length ?? MemoryLayout<T>.size
|
||||
|
||||
let valuePointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
|
||||
valuePointer.pointee = value
|
||||
|
||||
let bytesPointer = UnsafeMutablePointer<UInt8>(OpaquePointer(valuePointer))
|
||||
var bytes = Array<UInt8>(repeating: 0, count: totalBytes)
|
||||
for j in 0..<min(MemoryLayout<T>.size, totalBytes) {
|
||||
bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee
|
||||
}
|
||||
|
||||
#if swift(>=4.1)
|
||||
valuePointer.deinitialize(count: 1)
|
||||
valuePointer.deallocate()
|
||||
#else
|
||||
valuePointer.deinitialize()
|
||||
valuePointer.deallocate(capacity: 1)
|
||||
#endif
|
||||
|
||||
return bytes
|
||||
}
|
||||
|
||||
fileprivate extension Collection where Self.Iterator.Element == UInt8, Self.Index == Int {
|
||||
#if swift(>=4.0)
|
||||
func toInteger<T>() -> T where T: FixedWidthInteger {
|
||||
if self.isEmpty {
|
||||
return 0
|
||||
}
|
||||
|
||||
let size = MemoryLayout<T>.size
|
||||
var bytes = self.reversed()
|
||||
if bytes.count < MemoryLayout<T>.size {
|
||||
let paddingCount = MemoryLayout<T>.size - bytes.count
|
||||
if paddingCount > 0 {
|
||||
bytes += Array<UInt8>(repeating: 0, count: paddingCount)
|
||||
}
|
||||
}
|
||||
|
||||
if size == 1 {
|
||||
return T(truncatingIfNeeded: UInt64(bytes[0]))
|
||||
}
|
||||
|
||||
var result: T = 0
|
||||
for byte in bytes.reversed() {
|
||||
result = result << 8 | T(byte)
|
||||
}
|
||||
return result
|
||||
}
|
||||
#else
|
||||
func toInteger<T: Integer>() -> T where T: ByteConvertible, T: BitshiftOperationsType {
|
||||
if self.isEmpty {
|
||||
return 0
|
||||
}
|
||||
|
||||
var bytes = self.reversed()
|
||||
if bytes.count < MemoryLayout<T>.size {
|
||||
let paddingCount = MemoryLayout<T>.size - bytes.count
|
||||
if paddingCount > 0 {
|
||||
bytes += Array<UInt8>(repeating: 0, count: paddingCount)
|
||||
}
|
||||
}
|
||||
|
||||
if MemoryLayout<T>.size == 1 {
|
||||
return T(truncatingBitPattern: UInt64(bytes[0]))
|
||||
}
|
||||
|
||||
var result: T = 0
|
||||
for byte in bytes.reversed() {
|
||||
result = result << 8 | T(byte)
|
||||
}
|
||||
return result
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
fileprivate extension Array {
|
||||
/** split in chunks with given chunk size */
|
||||
func chunks(size chunksize: Int) -> Array<Array<Element>> {
|
||||
var words = Array<Array<Element>>()
|
||||
words.reserveCapacity(self.count / chunksize)
|
||||
for idx in stride(from: chunksize, through: self.count, by: chunksize) {
|
||||
words.append(Array(self[idx - chunksize..<idx])) // slow for large table
|
||||
}
|
||||
let reminder = self.suffix(self.count % chunksize)
|
||||
if !reminder.isEmpty {
|
||||
words.append(Array(reminder))
|
||||
}
|
||||
return words
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension Data {
|
||||
var bytes: Array<UInt8> {
|
||||
return Array(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
//
|
||||
// Base64.swift
|
||||
// SwiftyBeaver (macOS)
|
||||
//
|
||||
// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
|
||||
//
|
||||
#if os(Linux)
|
||||
import Foundation
|
||||
|
||||
struct InvalidBase64: Error {}
|
||||
|
||||
struct Base64 {
|
||||
static func decode(_ string: String) throws -> [UInt8] {
|
||||
return try decode([UInt8](string.utf8))
|
||||
}
|
||||
|
||||
/// Decodes a Base64 encoded String into Data
|
||||
///
|
||||
/// - throws: If the string isn't base64 encoded
|
||||
static func decode(_ string: [UInt8]) throws -> [UInt8] {
|
||||
let lookupTable: [UInt8] = [
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 62, 64, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
|
||||
64, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 63,
|
||||
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
|
||||
]
|
||||
|
||||
let remainder = string.count % 4
|
||||
let length = (string.count - remainder) / 4
|
||||
|
||||
var decoded = [UInt8]()
|
||||
decoded.reserveCapacity(length)
|
||||
|
||||
var index = 0
|
||||
var i0: UInt8 = 0
|
||||
var i1: UInt8 = 0
|
||||
var i2: UInt8 = 0
|
||||
var i3: UInt8 = 0
|
||||
|
||||
while index &+ 4 < string.count {
|
||||
i0 = lookupTable[numericCast(string[index])]
|
||||
i1 = lookupTable[numericCast(string[index &+ 1])]
|
||||
i2 = lookupTable[numericCast(string[index &+ 2])]
|
||||
i3 = lookupTable[numericCast(string[index &+ 3])]
|
||||
|
||||
if i0 > 63 || i1 > 63 || i2 > 63 || i3 > 63 {
|
||||
throw InvalidBase64()
|
||||
}
|
||||
|
||||
decoded.append(i0 << 2 | i1 >> 4)
|
||||
decoded.append(i1 << 4 | i2 >> 2)
|
||||
decoded.append(i2 << 6 | i3)
|
||||
index += 4
|
||||
}
|
||||
|
||||
if string.count &- index > 1 {
|
||||
i0 = lookupTable[numericCast(string[index])]
|
||||
i1 = lookupTable[numericCast(string[index &+ 1])]
|
||||
|
||||
if i1 > 63 {
|
||||
guard string[index] == 61 else {
|
||||
throw InvalidBase64()
|
||||
}
|
||||
|
||||
return decoded
|
||||
}
|
||||
|
||||
if i2 > 63 {
|
||||
guard string[index &+ 2] == 61 else {
|
||||
throw InvalidBase64()
|
||||
}
|
||||
|
||||
return decoded
|
||||
}
|
||||
|
||||
decoded.append(i0 << 2 | i1 >> 4)
|
||||
|
||||
if string.count &- index > 2 {
|
||||
i2 = lookupTable[numericCast(string[index &+ 2])]
|
||||
|
||||
if i2 > 63 {
|
||||
guard string[index &+ 2] == 61 else {
|
||||
throw InvalidBase64()
|
||||
}
|
||||
|
||||
return decoded
|
||||
}
|
||||
|
||||
decoded.append(i1 << 4 | i2 >> 2)
|
||||
|
||||
if string.count &- index > 3 {
|
||||
i3 = lookupTable[numericCast(string[index &+ 3])]
|
||||
|
||||
if i3 > 63 {
|
||||
guard string[index &+ 3] == 61 else {
|
||||
throw InvalidBase64()
|
||||
}
|
||||
|
||||
return decoded
|
||||
}
|
||||
|
||||
decoded.append(i2 << 6 | i3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return decoded
|
||||
}
|
||||
|
||||
static func encode(_ data: [UInt8]) -> String {
|
||||
let base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
var encoded: String = ""
|
||||
|
||||
func appendCharacterFromBase(_ character: Int) {
|
||||
encoded.append(base64[base64.index(base64.startIndex, offsetBy: character)])
|
||||
}
|
||||
|
||||
func byte(_ index: Int) -> Int {
|
||||
return Int(data[index])
|
||||
}
|
||||
|
||||
let decodedBytes = data.map { Int($0) }
|
||||
|
||||
var i = 0
|
||||
|
||||
while i < decodedBytes.count - 2 {
|
||||
appendCharacterFromBase((byte(i) >> 2) & 0x3F)
|
||||
appendCharacterFromBase(((byte(i) & 0x3) << 4) | ((byte(i + 1) & 0xF0) >> 4))
|
||||
appendCharacterFromBase(((byte(i + 1) & 0xF) << 2) | ((byte(i + 2) & 0xC0) >> 6))
|
||||
appendCharacterFromBase(byte(i + 2) & 0x3F)
|
||||
i += 3
|
||||
}
|
||||
|
||||
if i < decodedBytes.count {
|
||||
appendCharacterFromBase((byte(i) >> 2) & 0x3F)
|
||||
|
||||
if i == decodedBytes.count - 1 {
|
||||
appendCharacterFromBase(((byte(i) & 0x3) << 4))
|
||||
encoded.append("=")
|
||||
} else {
|
||||
appendCharacterFromBase(((byte(i) & 0x3) << 4) | ((byte(i + 1) & 0xF0) >> 4))
|
||||
appendCharacterFromBase(((byte(i + 1) & 0xF) << 2))
|
||||
}
|
||||
|
||||
encoded.append("=")
|
||||
}
|
||||
|
||||
return encoded
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,510 @@
|
||||
//
|
||||
// BaseDestination.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Sebastian Kreutzberger (Twitter @skreutzb) on 05.12.15.
|
||||
// Copyright © 2015 Sebastian Kreutzberger
|
||||
// Some rights reserved: http://opensource.org/licenses/MIT
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Dispatch
|
||||
|
||||
// store operating system / platform
|
||||
#if os(iOS)
|
||||
let OS = "iOS"
|
||||
#elseif os(OSX)
|
||||
let OS = "OSX"
|
||||
#elseif os(watchOS)
|
||||
let OS = "watchOS"
|
||||
#elseif os(tvOS)
|
||||
let OS = "tvOS"
|
||||
#elseif os(Linux)
|
||||
let OS = "Linux"
|
||||
#elseif os(FreeBSD)
|
||||
let OS = "FreeBSD"
|
||||
#elseif os(Windows)
|
||||
let OS = "Windows"
|
||||
#elseif os(Android)
|
||||
let OS = "Android"
|
||||
#else
|
||||
let OS = "Unknown"
|
||||
#endif
|
||||
|
||||
/// destination which all others inherit from. do not directly use
|
||||
open class BaseDestination: Hashable, Equatable {
|
||||
|
||||
/// output format pattern, see documentation for syntax
|
||||
open var format = "$DHH:mm:ss.SSS$d $C$L$c $N.$F:$l - $M"
|
||||
|
||||
/// runs in own serial background thread for better performance
|
||||
open var asynchronously = true
|
||||
|
||||
/// do not log any message which has a lower level than this one
|
||||
open var minLevel = SwiftyBeaver.Level.verbose
|
||||
|
||||
/// set custom log level words for each level
|
||||
open var levelString = LevelString()
|
||||
|
||||
/// set custom log level colors for each level
|
||||
open var levelColor = LevelColor()
|
||||
|
||||
public struct LevelString {
|
||||
public var verbose = "VERBOSE"
|
||||
public var debug = "DEBUG"
|
||||
public var info = "INFO"
|
||||
public var warning = "WARNING"
|
||||
public var error = "ERROR"
|
||||
}
|
||||
|
||||
// For a colored log level word in a logged line
|
||||
// empty on default
|
||||
public struct LevelColor {
|
||||
public var verbose = "" // silver
|
||||
public var debug = "" // green
|
||||
public var info = "" // blue
|
||||
public var warning = "" // yellow
|
||||
public var error = "" // red
|
||||
}
|
||||
|
||||
var reset = ""
|
||||
var escape = ""
|
||||
|
||||
var filters = [FilterType]()
|
||||
let formatter = DateFormatter()
|
||||
let startDate = Date()
|
||||
|
||||
// each destination class must have an own hashValue Int
|
||||
#if swift(>=4.2)
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(defaultHashValue)
|
||||
}
|
||||
#else
|
||||
lazy public var hashValue: Int = self.defaultHashValue
|
||||
#endif
|
||||
|
||||
open var defaultHashValue: Int {return 0}
|
||||
|
||||
// each destination instance must have an own serial queue to ensure serial output
|
||||
// GCD gives it a prioritization between User Initiated and Utility
|
||||
var queue: DispatchQueue? //dispatch_queue_t?
|
||||
var debugPrint = false // set to true to debug the internal filter logic of the class
|
||||
|
||||
public init() {
|
||||
let uuid = NSUUID().uuidString
|
||||
let queueLabel = "swiftybeaver-queue-" + uuid
|
||||
queue = DispatchQueue(label: queueLabel, target: queue)
|
||||
}
|
||||
|
||||
/// send / store the formatted log message to the destination
|
||||
/// returns the formatted log message for processing by inheriting method
|
||||
/// and for unit tests (nil if error)
|
||||
open func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, file: String,
|
||||
function: String, line: Int, context: Any? = nil) -> String? {
|
||||
|
||||
if format.hasPrefix("$J") {
|
||||
return messageToJSON(level, msg: msg, thread: thread,
|
||||
file: file, function: function, line: line, context: context)
|
||||
|
||||
} else {
|
||||
return formatMessage(format, level: level, msg: msg, thread: thread,
|
||||
file: file, function: function, line: line, context: context)
|
||||
}
|
||||
}
|
||||
|
||||
public func execute(synchronously: Bool, block: @escaping () -> Void) {
|
||||
guard let queue = queue else {
|
||||
fatalError("Queue not set")
|
||||
}
|
||||
if synchronously {
|
||||
queue.sync(execute: block)
|
||||
} else {
|
||||
queue.async(execute: block)
|
||||
}
|
||||
}
|
||||
|
||||
public func executeSynchronously<T>(block: @escaping () throws -> T) rethrows -> T {
|
||||
guard let queue = queue else {
|
||||
fatalError("Queue not set")
|
||||
}
|
||||
return try queue.sync(execute: block)
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// MARK: Format
|
||||
////////////////////////////////
|
||||
|
||||
/// returns (padding length value, offset in string after padding info)
|
||||
private func parsePadding(_ text: String) -> (Int, Int) {
|
||||
// look for digits followed by a alpha character
|
||||
var s: String!
|
||||
var sign: Int = 1
|
||||
if text.firstChar == "-" {
|
||||
sign = -1
|
||||
s = String(text.suffix(from: text.index(text.startIndex, offsetBy: 1)))
|
||||
} else {
|
||||
s = text
|
||||
}
|
||||
let numStr = String(s.prefix { $0 >= "0" && $0 <= "9" })
|
||||
if let num = Int(numStr) {
|
||||
return (sign * num, (sign == -1 ? 1 : 0) + numStr.count)
|
||||
} else {
|
||||
return (0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
private func paddedString(_ text: String, _ toLength: Int, truncating: Bool = false) -> String {
|
||||
if toLength > 0 {
|
||||
// Pad to the left of the string
|
||||
if text.count > toLength {
|
||||
// Hm... better to use suffix or prefix?
|
||||
return truncating ? String(text.suffix(toLength)) : text
|
||||
} else {
|
||||
return "".padding(toLength: toLength - text.count, withPad: " ", startingAt: 0) + text
|
||||
}
|
||||
} else if toLength < 0 {
|
||||
// Pad to the right of the string
|
||||
let maxLength = truncating ? -toLength : max(-toLength, text.count)
|
||||
return text.padding(toLength: maxLength, withPad: " ", startingAt: 0)
|
||||
} else {
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
/// returns the log message based on the format pattern
|
||||
func formatMessage(_ format: String, level: SwiftyBeaver.Level, msg: String, thread: String,
|
||||
file: String, function: String, line: Int, context: Any? = nil) -> String {
|
||||
|
||||
var text = ""
|
||||
// Prepend a $I for 'ignore' or else the first character is interpreted as a format character
|
||||
// even if the format string did not start with a $.
|
||||
let phrases: [String] = ("$I" + format).components(separatedBy: "$")
|
||||
|
||||
for phrase in phrases where !phrase.isEmpty {
|
||||
let (padding, offset) = parsePadding(phrase)
|
||||
let formatCharIndex = phrase.index(phrase.startIndex, offsetBy: offset)
|
||||
let formatChar = phrase[formatCharIndex]
|
||||
let rangeAfterFormatChar = phrase.index(formatCharIndex, offsetBy: 1)..<phrase.endIndex
|
||||
let remainingPhrase = phrase[rangeAfterFormatChar]
|
||||
|
||||
switch formatChar {
|
||||
case "I": // ignore
|
||||
text += remainingPhrase
|
||||
case "L":
|
||||
text += paddedString(levelWord(level), padding) + remainingPhrase
|
||||
case "M":
|
||||
text += paddedString(msg, padding) + remainingPhrase
|
||||
case "T":
|
||||
text += paddedString(thread, padding) + remainingPhrase
|
||||
case "N":
|
||||
// name of file without suffix
|
||||
text += paddedString(fileNameWithoutSuffix(file), padding) + remainingPhrase
|
||||
case "n":
|
||||
// name of file with suffix
|
||||
text += paddedString(fileNameOfFile(file), padding) + remainingPhrase
|
||||
case "F":
|
||||
text += paddedString(function, padding) + remainingPhrase
|
||||
case "l":
|
||||
text += paddedString(String(line), padding) + remainingPhrase
|
||||
case "D":
|
||||
// start of datetime format
|
||||
#if swift(>=3.2)
|
||||
text += paddedString(formatDate(String(remainingPhrase)), padding)
|
||||
#else
|
||||
text += paddedString(formatDate(remainingPhrase), padding)
|
||||
#endif
|
||||
case "d":
|
||||
text += remainingPhrase
|
||||
case "U":
|
||||
text += paddedString(uptime(), padding) + remainingPhrase
|
||||
case "Z":
|
||||
// start of datetime format in UTC timezone
|
||||
#if swift(>=3.2)
|
||||
text += paddedString(formatDate(String(remainingPhrase), timeZone: "UTC"), padding)
|
||||
#else
|
||||
text += paddedString(formatDate(remainingPhrase, timeZone: "UTC"), padding)
|
||||
#endif
|
||||
case "z":
|
||||
text += remainingPhrase
|
||||
case "C":
|
||||
// color code ("" on default)
|
||||
text += escape + colorForLevel(level) + remainingPhrase
|
||||
case "c":
|
||||
text += reset + remainingPhrase
|
||||
case "X":
|
||||
// add the context
|
||||
if let cx = context {
|
||||
text += paddedString(String(describing: cx).trimmingCharacters(in: .whitespacesAndNewlines), padding) + remainingPhrase
|
||||
} else {
|
||||
text += paddedString("", padding) + remainingPhrase
|
||||
}
|
||||
default:
|
||||
text += phrase
|
||||
}
|
||||
}
|
||||
// right trim only
|
||||
return text.replacingOccurrences(of: "\\s+$", with: "", options: .regularExpression)
|
||||
}
|
||||
|
||||
/// returns the log payload as optional JSON string
|
||||
func messageToJSON(_ level: SwiftyBeaver.Level, msg: String,
|
||||
thread: String, file: String, function: String, line: Int, context: Any? = nil) -> String? {
|
||||
var dict: [String: Any] = [
|
||||
"timestamp": Date().timeIntervalSince1970,
|
||||
"level": level.rawValue,
|
||||
"message": msg,
|
||||
"thread": thread,
|
||||
"file": file,
|
||||
"function": function,
|
||||
"line": line
|
||||
]
|
||||
if let cx = context {
|
||||
dict["context"] = cx
|
||||
}
|
||||
return jsonStringFromDict(dict)
|
||||
}
|
||||
|
||||
/// returns the string of a level
|
||||
func levelWord(_ level: SwiftyBeaver.Level) -> String {
|
||||
|
||||
var str = ""
|
||||
|
||||
switch level {
|
||||
case .debug:
|
||||
str = levelString.debug
|
||||
|
||||
case .info:
|
||||
str = levelString.info
|
||||
|
||||
case .warning:
|
||||
str = levelString.warning
|
||||
|
||||
case .error:
|
||||
str = levelString.error
|
||||
|
||||
default:
|
||||
// Verbose is default
|
||||
str = levelString.verbose
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
/// returns color string for level
|
||||
func colorForLevel(_ level: SwiftyBeaver.Level) -> String {
|
||||
var color = ""
|
||||
|
||||
switch level {
|
||||
case .debug:
|
||||
color = levelColor.debug
|
||||
|
||||
case .info:
|
||||
color = levelColor.info
|
||||
|
||||
case .warning:
|
||||
color = levelColor.warning
|
||||
|
||||
case .error:
|
||||
color = levelColor.error
|
||||
|
||||
default:
|
||||
color = levelColor.verbose
|
||||
}
|
||||
return color
|
||||
}
|
||||
|
||||
/// returns the filename of a path
|
||||
func fileNameOfFile(_ file: String) -> String {
|
||||
let fileParts = file.components(separatedBy: "/")
|
||||
if let lastPart = fileParts.last {
|
||||
return lastPart
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
/// returns the filename without suffix (= file ending) of a path
|
||||
func fileNameWithoutSuffix(_ file: String) -> String {
|
||||
let fileName = fileNameOfFile(file)
|
||||
|
||||
if !fileName.isEmpty {
|
||||
let fileNameParts = fileName.components(separatedBy: ".")
|
||||
if let firstPart = fileNameParts.first {
|
||||
return firstPart
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
/// returns a formatted date string
|
||||
/// optionally in a given abbreviated timezone like "UTC"
|
||||
func formatDate(_ dateFormat: String, timeZone: String = "") -> String {
|
||||
if !timeZone.isEmpty {
|
||||
formatter.timeZone = TimeZone(abbreviation: timeZone)
|
||||
}
|
||||
formatter.dateFormat = dateFormat
|
||||
//let dateStr = formatter.string(from: NSDate() as Date)
|
||||
let dateStr = formatter.string(from: Date())
|
||||
return dateStr
|
||||
}
|
||||
|
||||
/// returns a uptime string
|
||||
func uptime() -> String {
|
||||
let interval = Date().timeIntervalSince(startDate)
|
||||
|
||||
let hours = Int(interval) / 3600
|
||||
let minutes = Int(interval / 60) - Int(hours * 60)
|
||||
let seconds = Int(interval) - (Int(interval / 60) * 60)
|
||||
let milliseconds = Int(interval.truncatingRemainder(dividingBy: 1) * 1000)
|
||||
|
||||
return String(format: "%0.2d:%0.2d:%0.2d.%03d", arguments: [hours, minutes, seconds, milliseconds])
|
||||
}
|
||||
|
||||
/// returns the json-encoded string value
|
||||
/// after it was encoded by jsonStringFromDict
|
||||
func jsonStringValue(_ jsonString: String?, key: String) -> String {
|
||||
guard let str = jsonString else {
|
||||
return ""
|
||||
}
|
||||
|
||||
// remove the leading {"key":" from the json string and the final }
|
||||
let offset = key.length + 5
|
||||
let endIndex = str.index(str.startIndex,
|
||||
offsetBy: str.length - 2)
|
||||
let range = str.index(str.startIndex, offsetBy: offset)..<endIndex
|
||||
#if swift(>=3.2)
|
||||
return String(str[range])
|
||||
#else
|
||||
return str[range]
|
||||
#endif
|
||||
}
|
||||
|
||||
/// turns dict into JSON-encoded string
|
||||
func jsonStringFromDict(_ dict: [String: Any]) -> String? {
|
||||
var jsonString: String?
|
||||
|
||||
// try to create JSON string
|
||||
do {
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
|
||||
jsonString = String(data: jsonData, encoding: .utf8)
|
||||
} catch {
|
||||
print("SwiftyBeaver could not create JSON from dict.")
|
||||
}
|
||||
return jsonString
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// MARK: Filters
|
||||
////////////////////////////////
|
||||
|
||||
/// Add a filter that determines whether or not a particular message will be logged to this destination
|
||||
public func addFilter(_ filter: FilterType) {
|
||||
filters.append(filter)
|
||||
}
|
||||
|
||||
/// Remove a filter from the list of filters
|
||||
public func removeFilter(_ filter: FilterType) {
|
||||
#if swift(>=5)
|
||||
let index = filters.firstIndex {
|
||||
return ObjectIdentifier($0) == ObjectIdentifier(filter)
|
||||
}
|
||||
#else
|
||||
let index = filters.index {
|
||||
return ObjectIdentifier($0) == ObjectIdentifier(filter)
|
||||
}
|
||||
#endif
|
||||
|
||||
guard let filterIndex = index else {
|
||||
return
|
||||
}
|
||||
|
||||
filters.remove(at: filterIndex)
|
||||
}
|
||||
|
||||
/// Answer whether the destination has any message filters
|
||||
/// returns boolean and is used to decide whether to resolve
|
||||
/// the message before invoking shouldLevelBeLogged
|
||||
func hasMessageFilters() -> Bool {
|
||||
return !getFiltersTargeting(Filter.TargetType.Message(.Equals([], true)),
|
||||
fromFilters: self.filters).isEmpty
|
||||
}
|
||||
|
||||
/// checks if level is at least minLevel or if a minLevel filter for that path does exist
|
||||
/// returns boolean and can be used to decide if a message should be logged or not
|
||||
func shouldLevelBeLogged(_ level: SwiftyBeaver.Level, path: String,
|
||||
function: String, message: String? = nil) -> Bool {
|
||||
|
||||
if filters.isEmpty {
|
||||
if level.rawValue >= minLevel.rawValue {
|
||||
if debugPrint {
|
||||
print("filters are empty and level >= minLevel")
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
if debugPrint {
|
||||
print("filters are empty and level < minLevel")
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
let filterCheckResult = FilterValidator.validate(input: .init(filters: self.filters, level: level, path: path, function: function, message: message))
|
||||
|
||||
// Exclusion filters match if they do NOT meet the filter condition (see Filter.apply(_:) method)
|
||||
switch filterCheckResult[.excluded] {
|
||||
case .some(.someFiltersMatch):
|
||||
// Exclusion filters are present and at least one of them matches the log entry
|
||||
if debugPrint {
|
||||
print("filters are not empty and message was excluded")
|
||||
}
|
||||
return false
|
||||
case .some(.allFiltersMatch), .some(.noFiltersMatchingType), .none: break
|
||||
}
|
||||
|
||||
// If required filters exist, we should validate or invalidate the log if all of them pass or not
|
||||
switch filterCheckResult[.required] {
|
||||
case .some(.allFiltersMatch): return true
|
||||
case .some(.someFiltersMatch): return false
|
||||
case .some(.noFiltersMatchingType), .none: break
|
||||
}
|
||||
|
||||
let checkLogLevel: () -> Bool = {
|
||||
// Check if the log message's level matches or exceeds the minLevel of the destination
|
||||
return level.rawValue >= self.minLevel.rawValue
|
||||
}
|
||||
|
||||
// Non-required filters should only be applied if the log entry matches the filter condition (e.g. path)
|
||||
switch filterCheckResult[.nonRequired] {
|
||||
case .some(.allFiltersMatch): return true
|
||||
case .some(.noFiltersMatchingType), .none: return checkLogLevel()
|
||||
case .some(.someFiltersMatch(let partialMatchData)):
|
||||
if partialMatchData.fullMatchCount > 0 {
|
||||
// The log entry matches at least one filter condition and the destination's log level
|
||||
return true
|
||||
} else if partialMatchData.conditionMatchCount > 0 {
|
||||
// The log entry matches at least one filter condition, but does not match or exceed the destination's log level
|
||||
return false
|
||||
} else {
|
||||
// There is no filter with a matching filter condition. Check the destination's log level
|
||||
return checkLogLevel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getFiltersTargeting(_ target: Filter.TargetType, fromFilters: [FilterType]) -> [FilterType] {
|
||||
return fromFilters.filter { filter in
|
||||
return filter.getTarget() == target
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Triggered by main flush() method on each destination. Runs in background thread.
|
||||
Use for destinations that buffer log items, implement this function to flush those
|
||||
buffers to their final destination (web server...)
|
||||
*/
|
||||
func flush() {
|
||||
// no implementation in base destination needed
|
||||
}
|
||||
}
|
||||
|
||||
public func == (lhs: BaseDestination, rhs: BaseDestination) -> Bool {
|
||||
return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// ConsoleDestination.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Sebastian Kreutzberger on 05.12.15.
|
||||
// Copyright © 2015 Sebastian Kreutzberger
|
||||
// Some rights reserved: http://opensource.org/licenses/MIT
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class ConsoleDestination: BaseDestination {
|
||||
|
||||
/// use NSLog instead of print, default is false
|
||||
public var useNSLog = false
|
||||
/// uses colors compatible to Terminal instead of Xcode, default is false
|
||||
public var useTerminalColors: Bool = false {
|
||||
didSet {
|
||||
if useTerminalColors {
|
||||
// use Terminal colors
|
||||
reset = "\u{001b}[0m"
|
||||
escape = "\u{001b}[38;5;"
|
||||
levelColor.verbose = "251m" // silver
|
||||
levelColor.debug = "35m" // green
|
||||
levelColor.info = "38m" // blue
|
||||
levelColor.warning = "178m" // yellow
|
||||
levelColor.error = "197m" // red
|
||||
|
||||
} else {
|
||||
// use colored Emojis for better visual distinction
|
||||
// of log level for Xcode 8
|
||||
levelColor.verbose = "💜 " // silver
|
||||
levelColor.debug = "💚 " // green
|
||||
levelColor.info = "💙 " // blue
|
||||
levelColor.warning = "💛 " // yellow
|
||||
levelColor.error = "❤️ " // red
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public var defaultHashValue: Int { return 1 }
|
||||
|
||||
public override init() {
|
||||
super.init()
|
||||
levelColor.verbose = "💜 " // silver
|
||||
levelColor.debug = "💚 " // green
|
||||
levelColor.info = "💙 " // blue
|
||||
levelColor.warning = "💛 " // yellow
|
||||
levelColor.error = "❤️ " // red
|
||||
}
|
||||
|
||||
// print to Xcode Console. uses full base class functionality
|
||||
override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
|
||||
file: String, function: String, line: Int, context: Any? = nil) -> String? {
|
||||
let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line, context: context)
|
||||
|
||||
if let str = formattedString {
|
||||
if useNSLog {
|
||||
#if os(Linux)
|
||||
print(str)
|
||||
#else
|
||||
NSLog("%@", str)
|
||||
#endif
|
||||
} else {
|
||||
print(str)
|
||||
}
|
||||
}
|
||||
return formattedString
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Extensions.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Sebastian Kreutzberger on 13.12.17.
|
||||
// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension String {
|
||||
/// cross-Swift compatible characters count
|
||||
var length: Int {
|
||||
return self.count
|
||||
}
|
||||
|
||||
/// cross-Swift-compatible first character
|
||||
var firstChar: Character? {
|
||||
return self.first
|
||||
}
|
||||
|
||||
/// cross-Swift-compatible last character
|
||||
var lastChar: Character? {
|
||||
return self.last
|
||||
}
|
||||
|
||||
/// cross-Swift-compatible index
|
||||
func find(_ char: Character) -> Index? {
|
||||
#if swift(>=5)
|
||||
return self.firstIndex(of: char)
|
||||
#else
|
||||
return self.index(of: char)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
//
|
||||
// FileDestination.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Sebastian Kreutzberger on 05.12.15.
|
||||
// Copyright © 2015 Sebastian Kreutzberger
|
||||
// Some rights reserved: http://opensource.org/licenses/MIT
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
open class FileDestination: BaseDestination {
|
||||
|
||||
public var logFileURL: URL?
|
||||
public var syncAfterEachWrite: Bool = false
|
||||
public var colored: Bool = false {
|
||||
didSet {
|
||||
if colored {
|
||||
// bash font color, first value is intensity, second is color
|
||||
// see http://bit.ly/1Otu3Zr & for syntax http://bit.ly/1Tp6Fw9
|
||||
// uses the 256-color table from http://bit.ly/1W1qJuH
|
||||
reset = "\u{001b}[0m"
|
||||
escape = "\u{001b}[38;5;"
|
||||
levelColor.verbose = "251m" // silver
|
||||
levelColor.debug = "35m" // green
|
||||
levelColor.info = "38m" // blue
|
||||
levelColor.warning = "178m" // yellow
|
||||
levelColor.error = "197m" // red
|
||||
} else {
|
||||
reset = ""
|
||||
escape = ""
|
||||
levelColor.verbose = ""
|
||||
levelColor.debug = ""
|
||||
levelColor.info = ""
|
||||
levelColor.warning = ""
|
||||
levelColor.error = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LOGFILE ROTATION
|
||||
// ho many bytes should a logfile have until it is rotated?
|
||||
// default is 5 MB. Just is used if logFileAmount > 1
|
||||
public var logFileMaxSize = (5 * 1024 * 1024)
|
||||
// Number of log files used in rotation, default is 1 which deactivates file rotation
|
||||
public var logFileAmount = 1
|
||||
|
||||
override public var defaultHashValue: Int {return 2}
|
||||
let fileManager = FileManager.default
|
||||
|
||||
|
||||
public init(logFileURL: URL? = nil) {
|
||||
if let logFileURL = logFileURL {
|
||||
self.logFileURL = logFileURL
|
||||
super.init()
|
||||
return
|
||||
}
|
||||
|
||||
// platform-dependent logfile directory default
|
||||
var baseURL: URL?
|
||||
#if os(OSX)
|
||||
if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
|
||||
baseURL = url
|
||||
// try to use ~/Library/Caches/APP NAME instead of ~/Library/Caches
|
||||
if let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable") as? String {
|
||||
do {
|
||||
if let appURL = baseURL?.appendingPathComponent(appName, isDirectory: true) {
|
||||
try fileManager.createDirectory(at: appURL,
|
||||
withIntermediateDirectories: true, attributes: nil)
|
||||
baseURL = appURL
|
||||
}
|
||||
} catch {
|
||||
print("Warning! Could not create folder /Library/Caches/\(appName)")
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
#if os(Linux)
|
||||
baseURL = URL(fileURLWithPath: "/var/cache")
|
||||
#else
|
||||
// iOS, watchOS, etc. are using the caches directory
|
||||
if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
|
||||
baseURL = url
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if let baseURL = baseURL {
|
||||
self.logFileURL = baseURL.appendingPathComponent("swiftybeaver.log", isDirectory: false)
|
||||
}
|
||||
super.init()
|
||||
}
|
||||
|
||||
// append to file. uses full base class functionality
|
||||
override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
|
||||
file: String, function: String, line: Int, context: Any? = nil) -> String? {
|
||||
let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line, context: context)
|
||||
|
||||
if let str = formattedString {
|
||||
_ = validateSaveFile(str: str)
|
||||
}
|
||||
return formattedString
|
||||
}
|
||||
|
||||
// check if filesize is bigger than wanted and if yes then rotate them
|
||||
func validateSaveFile(str: String) -> Bool {
|
||||
if self.logFileAmount > 1 {
|
||||
guard let url = logFileURL else { return false }
|
||||
let filePath = url.path
|
||||
if FileManager.default.fileExists(atPath: filePath) == true {
|
||||
do {
|
||||
// Get file size
|
||||
let attr = try FileManager.default.attributesOfItem(atPath: filePath)
|
||||
let fileSize = attr[FileAttributeKey.size] as! UInt64
|
||||
// Do file rotation
|
||||
if fileSize > logFileMaxSize {
|
||||
rotateFile(filePath)
|
||||
}
|
||||
} catch {
|
||||
print("validateSaveFile error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
return saveToFile(str: str)
|
||||
}
|
||||
|
||||
private func rotateFile(_ filePath: String) {
|
||||
let lastIndex = (logFileAmount-1)
|
||||
let firstIndex = 1
|
||||
do {
|
||||
for index in stride(from: lastIndex, through: firstIndex, by: -1) {
|
||||
let oldFile = String.init(format: "%@.%d", filePath, index)
|
||||
|
||||
if FileManager.default.fileExists(atPath: oldFile) {
|
||||
if index == lastIndex {
|
||||
// Delete the last file
|
||||
try FileManager.default.removeItem(atPath: oldFile)
|
||||
} else {
|
||||
// Move the current file to next index
|
||||
let newFile = String.init(format: "%@.%d", filePath, index+1)
|
||||
try FileManager.default.moveItem(atPath: oldFile, toPath: newFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, move the current file
|
||||
let newFile = String.init(format: "%@.%d", filePath, firstIndex)
|
||||
try FileManager.default.moveItem(atPath: filePath, toPath: newFile)
|
||||
} catch {
|
||||
print("rotateFile error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
/// appends a string as line to a file.
|
||||
/// returns boolean about success
|
||||
func saveToFile(str: String) -> Bool {
|
||||
guard let url = logFileURL else { return false }
|
||||
|
||||
let line = str + "\n"
|
||||
guard let data = line.data(using: String.Encoding.utf8) else { return false }
|
||||
|
||||
return write(data: data, to: url)
|
||||
}
|
||||
|
||||
private func write(data: Data, to url: URL) -> Bool {
|
||||
|
||||
#if os(Linux)
|
||||
return true
|
||||
#else
|
||||
var success = false
|
||||
let coordinator = NSFileCoordinator(filePresenter: nil)
|
||||
var error: NSError?
|
||||
coordinator.coordinate(writingItemAt: url, error: &error) { url in
|
||||
do {
|
||||
if fileManager.fileExists(atPath: url.path) == false {
|
||||
|
||||
let directoryURL = url.deletingLastPathComponent()
|
||||
if fileManager.fileExists(atPath: directoryURL.path) == false {
|
||||
try fileManager.createDirectory(
|
||||
at: directoryURL,
|
||||
withIntermediateDirectories: true
|
||||
)
|
||||
}
|
||||
fileManager.createFile(atPath: url.path, contents: nil)
|
||||
|
||||
#if os(iOS) || os(watchOS)
|
||||
if #available(iOS 10.0, watchOS 3.0, *) {
|
||||
var attributes = try fileManager.attributesOfItem(atPath: url.path)
|
||||
attributes[FileAttributeKey.protectionKey] = FileProtectionType.none
|
||||
try fileManager.setAttributes(attributes, ofItemAtPath: url.path)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
let fileHandle = try FileHandle(forWritingTo: url)
|
||||
fileHandle.seekToEndOfFile()
|
||||
fileHandle.write(data)
|
||||
if syncAfterEachWrite {
|
||||
fileHandle.synchronizeFile()
|
||||
}
|
||||
fileHandle.closeFile()
|
||||
success = true
|
||||
} catch {
|
||||
print("SwiftyBeaver File Destination could not write to file \(url).")
|
||||
}
|
||||
}
|
||||
|
||||
if let error = error {
|
||||
print("Failed writing file with error: \(String(describing: error))")
|
||||
return false
|
||||
}
|
||||
|
||||
return success
|
||||
#endif
|
||||
}
|
||||
|
||||
/// deletes log file.
|
||||
/// returns true if file was removed or does not exist, false otherwise
|
||||
public func deleteLogFile() -> Bool {
|
||||
guard let url = logFileURL, fileManager.fileExists(atPath: url.path) == true else { return true }
|
||||
do {
|
||||
try fileManager.removeItem(at: url)
|
||||
return true
|
||||
} catch {
|
||||
print("SwiftyBeaver File Destination could not remove file \(url).")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
//
|
||||
// Filter.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Jeff Roberts on 5/31/16.
|
||||
// Copyright © 2015 Sebastian Kreutzberger
|
||||
// Some rights reserved: http://opensource.org/licenses/MIT
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// FilterType is a protocol that describes something that determines
|
||||
/// whether or not a message gets logged. A filter answers a Bool when it
|
||||
/// is applied to a value. If the filter passes, it shall return true,
|
||||
/// false otherwise.
|
||||
///
|
||||
/// A filter must contain a target, which identifies what it filters against
|
||||
/// A filter can be required meaning that all required filters against a specific
|
||||
/// target must pass in order for the message to be logged.
|
||||
public protocol FilterType : AnyObject {
|
||||
func apply(_ value: String?) -> Bool
|
||||
func getTarget() -> Filter.TargetType
|
||||
func isRequired() -> Bool
|
||||
func isExcluded() -> Bool
|
||||
func reachedMinLevel(_ level: SwiftyBeaver.Level) -> Bool
|
||||
}
|
||||
|
||||
/// Filters is syntactic sugar used to easily construct filters
|
||||
public class Filters {
|
||||
public static let Path = PathFilterFactory.self
|
||||
public static let Function = FunctionFilterFactory.self
|
||||
public static let Message = MessageFilterFactory.self
|
||||
}
|
||||
|
||||
/// Filter is an abstract base class for other filters
|
||||
public class Filter {
|
||||
public enum TargetType {
|
||||
case Path(Filter.ComparisonType)
|
||||
case Function(Filter.ComparisonType)
|
||||
case Message(Filter.ComparisonType)
|
||||
}
|
||||
|
||||
public enum ComparisonType {
|
||||
case StartsWith([String], Bool)
|
||||
case Contains([String], Bool)
|
||||
case Excludes([String], Bool)
|
||||
case EndsWith([String], Bool)
|
||||
case Equals([String], Bool)
|
||||
case Custom((String) -> Bool)
|
||||
}
|
||||
|
||||
let targetType: Filter.TargetType
|
||||
let required: Bool
|
||||
let minLevel: SwiftyBeaver.Level
|
||||
|
||||
public init(_ target: Filter.TargetType, required: Bool, minLevel: SwiftyBeaver.Level) {
|
||||
self.targetType = target
|
||||
self.required = required
|
||||
self.minLevel = minLevel
|
||||
}
|
||||
|
||||
public func getTarget() -> Filter.TargetType {
|
||||
return self.targetType
|
||||
}
|
||||
|
||||
public func isRequired() -> Bool {
|
||||
return self.required
|
||||
}
|
||||
|
||||
public func isExcluded() -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
/// returns true of set minLevel is >= as given level
|
||||
public func reachedMinLevel(_ level: SwiftyBeaver.Level) -> Bool {
|
||||
//print("checking if given level \(level) >= \(minLevel)")
|
||||
return level.rawValue >= minLevel.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
/// CompareFilter is a FilterType that can filter based upon whether a target
|
||||
/// starts with, contains or ends with a specific string. CompareFilters can be
|
||||
/// case sensitive.
|
||||
public class CompareFilter: Filter, FilterType {
|
||||
|
||||
private var filterComparisonType: Filter.ComparisonType?
|
||||
|
||||
override public init(_ target: Filter.TargetType, required: Bool, minLevel: SwiftyBeaver.Level) {
|
||||
super.init(target, required: required, minLevel: minLevel)
|
||||
|
||||
let comparisonType: Filter.ComparisonType?
|
||||
switch self.getTarget() {
|
||||
case let .Function(comparison):
|
||||
comparisonType = comparison
|
||||
|
||||
case let .Path(comparison):
|
||||
comparisonType = comparison
|
||||
|
||||
case let .Message(comparison):
|
||||
comparisonType = comparison
|
||||
|
||||
/*default:
|
||||
comparisonType = nil*/
|
||||
}
|
||||
self.filterComparisonType = comparisonType
|
||||
}
|
||||
|
||||
public func apply(_ value: String?) -> Bool {
|
||||
guard let value = value else {
|
||||
return false
|
||||
}
|
||||
|
||||
guard let filterComparisonType = self.filterComparisonType else {
|
||||
return false
|
||||
}
|
||||
|
||||
let matches: Bool
|
||||
switch filterComparisonType {
|
||||
case let .Contains(strings, caseSensitive):
|
||||
matches = !strings.filter { string in
|
||||
return caseSensitive ? value.contains(string) :
|
||||
value.lowercased().contains(string.lowercased())
|
||||
}.isEmpty
|
||||
|
||||
case let .Excludes(strings, caseSensitive):
|
||||
matches = !strings.filter { string in
|
||||
return caseSensitive ? !value.contains(string) :
|
||||
!value.lowercased().contains(string.lowercased())
|
||||
}.isEmpty
|
||||
|
||||
case let .StartsWith(strings, caseSensitive):
|
||||
matches = !strings.filter { string in
|
||||
return caseSensitive ? value.hasPrefix(string) :
|
||||
value.lowercased().hasPrefix(string.lowercased())
|
||||
}.isEmpty
|
||||
|
||||
case let .EndsWith(strings, caseSensitive):
|
||||
matches = !strings.filter { string in
|
||||
return caseSensitive ? value.hasSuffix(string) :
|
||||
value.lowercased().hasSuffix(string.lowercased())
|
||||
}.isEmpty
|
||||
|
||||
case let .Equals(strings, caseSensitive):
|
||||
matches = !strings.filter { string in
|
||||
return caseSensitive ? value == string :
|
||||
value.lowercased() == string.lowercased()
|
||||
}.isEmpty
|
||||
case let .Custom(predicate):
|
||||
matches = predicate(value)
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
override public func isExcluded() -> Bool {
|
||||
guard let filterComparisonType = self.filterComparisonType else { return false }
|
||||
|
||||
switch filterComparisonType {
|
||||
case .Excludes:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Syntactic sugar for creating a function comparison filter
|
||||
public class FunctionFilterFactory {
|
||||
public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Function(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func contains(_ strings: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Function(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func excludes(_ strings: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Function(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Function(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func equals(_ strings: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Function(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
|
||||
return CompareFilter(.Function(.Custom(filterPredicate)), required: required, minLevel: minLevel)
|
||||
}
|
||||
}
|
||||
|
||||
// Syntactic sugar for creating a message comparison filter
|
||||
public class MessageFilterFactory {
|
||||
public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Message(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func contains(_ strings: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Message(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func excludes(_ strings: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Message(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Message(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func equals(_ strings: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Message(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
|
||||
return CompareFilter(.Message(.Custom(filterPredicate)), required: required, minLevel: minLevel)
|
||||
}
|
||||
}
|
||||
|
||||
// Syntactic sugar for creating a path comparison filter
|
||||
public class PathFilterFactory {
|
||||
public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Path(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func contains(_ strings: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Path(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func excludes(_ strings: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Path(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Path(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func equals(_ strings: String..., caseSensitive: Bool = false,
|
||||
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
|
||||
return CompareFilter(.Path(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
|
||||
}
|
||||
|
||||
public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
|
||||
return CompareFilter(.Path(.Custom(filterPredicate)), required: required, minLevel: minLevel)
|
||||
}
|
||||
}
|
||||
|
||||
extension Filter.TargetType: Equatable {
|
||||
}
|
||||
|
||||
// The == does not compare associated values for each enum. Instead == evaluates to true
|
||||
// if both enums are the same "types", ignoring the associated values of each enum
|
||||
public func == (lhs: Filter.TargetType, rhs: Filter.TargetType) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
|
||||
case (.Path, .Path):
|
||||
return true
|
||||
|
||||
case (.Function, .Function):
|
||||
return true
|
||||
|
||||
case (.Message, .Message):
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// FilterValidator.swift
|
||||
// SwiftyBeaver (iOS)
|
||||
//
|
||||
// Created by Felix Lisczyk on 07.07.19.
|
||||
// Copyright © 2019 Sebastian Kreutzberger. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// FilterValidator is a utility class used by BaseDestination.
|
||||
/// It encapsulates the filtering logic for excluded, required
|
||||
/// and non-required filters.
|
||||
///
|
||||
/// FilterValidator evaluates a set of filters for a single log
|
||||
/// entry. It determines if these filters apply to the log entry
|
||||
/// based on their condition (path, function, message) and their
|
||||
/// minimum log level.
|
||||
|
||||
struct FilterValidator {
|
||||
|
||||
// These are the different filter types that the user can set
|
||||
enum ValidationType: CaseIterable {
|
||||
case excluded
|
||||
case required
|
||||
case nonRequired
|
||||
|
||||
func apply(to filters: [FilterType]) -> [FilterType] {
|
||||
switch self {
|
||||
case .excluded:
|
||||
return filters.filter { $0.isExcluded() }
|
||||
case .required:
|
||||
return filters.filter { $0.isRequired() && !$0.isExcluded() }
|
||||
case .nonRequired:
|
||||
return filters.filter { !$0.isRequired() && !$0.isExcluded() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper object for input parameters
|
||||
struct Input {
|
||||
let filters: [FilterType]
|
||||
let level: SwiftyBeaver.Level
|
||||
let path: String
|
||||
let function: String
|
||||
let message: String?
|
||||
}
|
||||
|
||||
// Result wrapper object
|
||||
enum Result {
|
||||
case allFiltersMatch // All filters fully match the log entry (condition + minimum log level)
|
||||
case someFiltersMatch(PartialMatchData) // Only some filters fully match the log entry (condition + minimum log level)
|
||||
case noFiltersMatchingType // There are no filters set for a particular type (excluded, required, nonRequired)
|
||||
|
||||
struct PartialMatchData {
|
||||
let fullMatchCount: Int // Number of filters that match both the condition and the minimum log level of the log entry
|
||||
let conditionMatchCount: Int // Number of filters that match ONLY the condition of the log entry (path, function, message)
|
||||
let logLevelMatchCount: Int // Number of filters that match ONLY the minimum log level of the log entry
|
||||
}
|
||||
}
|
||||
|
||||
static func validate(input: Input, for types: [ValidationType] = ValidationType.allCases) -> [ValidationType: Result] {
|
||||
var results = [ValidationType: Result]()
|
||||
for type in types {
|
||||
let filtersToValidate = type.apply(to: input.filters)
|
||||
|
||||
if filtersToValidate.isEmpty {
|
||||
// There are no filters set for this particular type
|
||||
results[type] = .noFiltersMatchingType
|
||||
} else {
|
||||
var fullMatchCount: Int = 0
|
||||
var conditionMatchCount: Int = 0
|
||||
var logLevelMatchCount: Int = 0
|
||||
|
||||
for filter in filtersToValidate {
|
||||
let filterMatchesCondition = self.filterMatchesCondition(filter, level: input.level, path: input.path, function: input.function, message: input.message)
|
||||
let filterMatchesMinLogLevel = self.filterMatchesMinLogLevel(filter, level: input.level)
|
||||
|
||||
switch (filterMatchesCondition, filterMatchesMinLogLevel) {
|
||||
// Filter matches both the condition and the minimum log level
|
||||
case (true, true): fullMatchCount += 1
|
||||
// Filter matches only the condition (path, function, message)
|
||||
case (true, false): conditionMatchCount += 1
|
||||
// Filter matches only the minimum log level
|
||||
case (false, true): logLevelMatchCount += 1
|
||||
// Filter does not match the condition nor the minimum log level
|
||||
case (false, false): break
|
||||
}
|
||||
}
|
||||
|
||||
if filtersToValidate.count == fullMatchCount {
|
||||
// All filters fully match the log entry
|
||||
results[type] = .allFiltersMatch
|
||||
} else {
|
||||
// Only some filters match the log entry
|
||||
results[type] = .someFiltersMatch(.init(fullMatchCount: fullMatchCount, conditionMatchCount: conditionMatchCount, logLevelMatchCount: logLevelMatchCount))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
private static func filterMatchesCondition(_ filter: FilterType, level: SwiftyBeaver.Level,
|
||||
path: String, function: String, message: String?) -> Bool {
|
||||
let passes: Bool
|
||||
|
||||
switch filter.getTarget() {
|
||||
case .Path(_):
|
||||
passes = filter.apply(path)
|
||||
|
||||
case .Function(_):
|
||||
passes = filter.apply(function)
|
||||
|
||||
case .Message(_):
|
||||
guard let message = message else {
|
||||
return false
|
||||
}
|
||||
|
||||
passes = filter.apply(message)
|
||||
}
|
||||
|
||||
return passes
|
||||
}
|
||||
|
||||
private static func filterMatchesMinLogLevel(_ filter: FilterType, level: SwiftyBeaver.Level) -> Bool {
|
||||
return filter.reachedMinLevel(level)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// GoogleCloudDestination.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class GoogleCloudDestination: BaseDestination {
|
||||
|
||||
private let serviceName: String
|
||||
|
||||
public init(serviceName: String) {
|
||||
self.serviceName = serviceName
|
||||
super.init()
|
||||
}
|
||||
|
||||
override public var asynchronously: Bool {
|
||||
get {
|
||||
return false
|
||||
}
|
||||
set {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
|
||||
file: String, function: String, line: Int, context: Any? = nil) -> String? {
|
||||
|
||||
let reportLocation: [String: Any] = ["filePath": file, "lineNumber": line, "functionName": function]
|
||||
var gcpContext: [String: Any] = ["reportLocation": reportLocation]
|
||||
if let context = context as? [String: Any] {
|
||||
if let httpRequestContext = context["httpRequest"] as? [String: Any] {
|
||||
gcpContext["httpRequest"] = httpRequestContext
|
||||
}
|
||||
|
||||
if let user = context["user"] as? String {
|
||||
gcpContext["user"] = user
|
||||
}
|
||||
}
|
||||
|
||||
let gcpJSON: [String: Any] = [
|
||||
"serviceContext": [
|
||||
"service": serviceName
|
||||
],
|
||||
"message": msg,
|
||||
"severity": level.severity,
|
||||
"context": gcpContext
|
||||
]
|
||||
|
||||
let finalLogString: String
|
||||
|
||||
do {
|
||||
finalLogString = try jsonString(obj: gcpJSON)
|
||||
} catch {
|
||||
let uncrashableLogString = "{\"context\":{\"reportLocation\":{\"filePath\": \"\(file)\"" +
|
||||
",\"functionName\":\"\(function)\"" +
|
||||
",\"lineNumber\":\(line)},\"severity\"" +
|
||||
":\"CRITICAL\",\"message\":\"Error encoding " +
|
||||
"JSON log entry. You may be losing log messages!\"}"
|
||||
finalLogString = uncrashableLogString.description
|
||||
}
|
||||
print(finalLogString)
|
||||
return finalLogString
|
||||
}
|
||||
|
||||
private func jsonString(obj: Dictionary<String, Any>) throws -> String {
|
||||
let json = try JSONSerialization.data(withJSONObject: obj, options: [])
|
||||
guard let string = String(data: json, encoding: .utf8) else {
|
||||
throw GCPError.serialization
|
||||
}
|
||||
return string
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
|
||||
extension SwiftyBeaver.Level {
|
||||
|
||||
/// Verbose is reported as Debug to GCP.
|
||||
/// Recommend you don't bother using it.
|
||||
var severity: String {
|
||||
switch self {
|
||||
// There is only one level below "Debug": "Default", which becomes "Any" and is considered as a potential error as well
|
||||
case .verbose: return "DEBUG"
|
||||
case .debug: return "DEBUG"
|
||||
case .info: return "INFO"
|
||||
case .warning: return "WARNING"
|
||||
case .error: return "ERROR"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum GCPError: Error {
|
||||
case serialization
|
||||
}
|
||||
@@ -0,0 +1,628 @@
|
||||
//
|
||||
// SBPlatformDestination
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Sebastian Kreutzberger on 22.01.16.
|
||||
// Copyright © 2016 Sebastian Kreutzberger
|
||||
// Some rights reserved: http://opensource.org/licenses/MIT
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
#if canImport(FoundationNetworking)
|
||||
import FoundationNetworking
|
||||
#endif
|
||||
|
||||
// platform-dependent import frameworks to get device details
|
||||
// valid values for os(): OSX, iOS, watchOS, tvOS, Linux
|
||||
// in Swift 3 the following were added: FreeBSD, Windows, Android
|
||||
#if os(iOS) || os(tvOS) || os(watchOS)
|
||||
import UIKit
|
||||
var DEVICE_MODEL: String {
|
||||
get {
|
||||
var systemInfo = utsname()
|
||||
uname(&systemInfo)
|
||||
let machineMirror = Mirror(reflecting: systemInfo.machine)
|
||||
let identifier = machineMirror.children.reduce("") { identifier, element in
|
||||
guard let value = element.value as? Int8, value != 0 else { return identifier }
|
||||
return identifier + String(UnicodeScalar(UInt8(value)))
|
||||
}
|
||||
return identifier
|
||||
}
|
||||
}
|
||||
#else
|
||||
let DEVICE_MODEL = ""
|
||||
#endif
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
var DEVICE_NAME = UIDevice.current.name
|
||||
#else
|
||||
// under watchOS UIDevice is not existing, http://apple.co/26ch5J1
|
||||
let DEVICE_NAME = ""
|
||||
#endif
|
||||
|
||||
public class SBPlatformDestination: BaseDestination {
|
||||
|
||||
public var appID = ""
|
||||
public var appSecret = ""
|
||||
public var encryptionKey = ""
|
||||
public var analyticsUserName = "" // user email, ID, name, etc.
|
||||
public var analyticsUUID: String { return uuid }
|
||||
|
||||
// when to send to server
|
||||
public struct SendingPoints {
|
||||
public var verbose = 0
|
||||
public var debug = 1
|
||||
public var info = 5
|
||||
public var warning = 8
|
||||
public var error = 10
|
||||
public var threshold = 10 // send to server if points reach that value
|
||||
}
|
||||
public var sendingPoints = SendingPoints()
|
||||
public var showNSLog = false // executes toNSLog statements to debug the class
|
||||
var points = 0
|
||||
|
||||
public var serverURL = URL(string: "https://api.swiftybeaver.com/api/entries/") // optional
|
||||
public var entriesFileURL = URL(fileURLWithPath: "") // not optional
|
||||
public var sendingFileURL = URL(fileURLWithPath: "")
|
||||
public var analyticsFileURL = URL(fileURLWithPath: "")
|
||||
|
||||
private let minAllowedThreshold = 1 // over-rules SendingPoints.Threshold
|
||||
private let maxAllowedThreshold = 1000 // over-rules SendingPoints.Threshold
|
||||
private var sendingInProgress = false
|
||||
private var initialSending = true
|
||||
|
||||
// analytics
|
||||
var uuid = ""
|
||||
|
||||
// destination
|
||||
override public var defaultHashValue: Int {return 3}
|
||||
let fileManager = FileManager.default
|
||||
let isoDateFormatter = DateFormatter()
|
||||
|
||||
/// init platform with default internal filenames
|
||||
public init(appID: String, appSecret: String, encryptionKey: String,
|
||||
serverURL: URL? = URL(string: "https://api.swiftybeaver.com/api/entries/"),
|
||||
entriesFileName: String = "sbplatform_entries.json",
|
||||
sendingfileName: String = "sbplatform_entries_sending.json",
|
||||
analyticsFileName: String = "sbplatform_analytics.json") {
|
||||
super.init()
|
||||
self.serverURL = serverURL
|
||||
self.appID = appID
|
||||
self.appSecret = appSecret
|
||||
self.encryptionKey = encryptionKey
|
||||
|
||||
// setup where to write the json files
|
||||
var baseURL: URL?
|
||||
#if os(OSX)
|
||||
if let url = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first {
|
||||
baseURL = url
|
||||
// try to use ~/Library/Application Support/APP NAME instead of ~/Library/Application Support
|
||||
if let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable") as? String {
|
||||
do {
|
||||
if let appURL = baseURL?.appendingPathComponent(appName, isDirectory: true) {
|
||||
try fileManager.createDirectory(at: appURL,
|
||||
withIntermediateDirectories: true, attributes: nil)
|
||||
baseURL = appURL
|
||||
}
|
||||
} catch {
|
||||
// it is too early in the class lifetime to be able to use toNSLog()
|
||||
print("Warning! Could not create folder ~/Library/Application Support/\(appName).")
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
#if os(tvOS)
|
||||
// tvOS can just use the caches directory
|
||||
if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
|
||||
baseURL = url
|
||||
}
|
||||
#elseif os(Linux)
|
||||
// Linux is using /var/cache
|
||||
let baseDir = "/var/cache/"
|
||||
entriesFileURL = URL(fileURLWithPath: baseDir + entriesFileName)
|
||||
sendingFileURL = URL(fileURLWithPath: baseDir + sendingfileName)
|
||||
analyticsFileURL = URL(fileURLWithPath: baseDir + analyticsFileName)
|
||||
#else
|
||||
// iOS and watchOS are using the app’s document directory
|
||||
if let url = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
|
||||
baseURL = url
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if os(Linux)
|
||||
// get, update loaded and save analytics data to file on start
|
||||
let dict = analytics(analyticsFileURL, update: true)
|
||||
_ = saveDictToFile(dict, url: analyticsFileURL)
|
||||
#else
|
||||
if let baseURL = baseURL {
|
||||
// is just set for everything but not Linux
|
||||
entriesFileURL = baseURL.appendingPathComponent(entriesFileName,
|
||||
isDirectory: false)
|
||||
sendingFileURL = baseURL.appendingPathComponent(sendingfileName,
|
||||
isDirectory: false)
|
||||
analyticsFileURL = baseURL.appendingPathComponent(analyticsFileName,
|
||||
isDirectory: false)
|
||||
|
||||
// get, update loaded and save analytics data to file on start
|
||||
let dict = analytics(analyticsFileURL, update: true)
|
||||
_ = saveDictToFile(dict, url: analyticsFileURL)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// append to file, each line is a JSON dict
|
||||
override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
|
||||
file: String, function: String, line: Int, context: Any? = nil) -> String? {
|
||||
|
||||
var jsonString: String?
|
||||
|
||||
let dict: [String: Any] = [
|
||||
"timestamp": Date().timeIntervalSince1970,
|
||||
"level": level.rawValue,
|
||||
"message": msg,
|
||||
"thread": thread,
|
||||
"fileName": file.components(separatedBy: "/").last!,
|
||||
"function": function,
|
||||
"line": line]
|
||||
|
||||
jsonString = jsonStringFromDict(dict)
|
||||
|
||||
if let str = jsonString {
|
||||
toNSLog("saving '\(msg)' to \(entriesFileURL)")
|
||||
_ = saveToFile(str, url: entriesFileURL)
|
||||
//toNSLog(entriesFileURL.path!)
|
||||
|
||||
// now decide if the stored log entries should be sent to the server
|
||||
// add level points to current points amount and send to server if threshold is hit
|
||||
let newPoints = sendingPointsForLevel(level)
|
||||
points += newPoints
|
||||
toNSLog("current sending points: \(points)")
|
||||
|
||||
if (points >= sendingPoints.threshold && points >= minAllowedThreshold) || points > maxAllowedThreshold {
|
||||
toNSLog("\(points) points is >= threshold")
|
||||
// above threshold, send to server
|
||||
sendNow()
|
||||
|
||||
} else if initialSending {
|
||||
initialSending = false
|
||||
// first logging at this session
|
||||
// send if json file still contains old log entries
|
||||
if let logEntries = logsFromFile(entriesFileURL) {
|
||||
let lines = logEntries.count
|
||||
if lines > 1 {
|
||||
var msg = "initialSending: \(points) points is below threshold "
|
||||
msg += "but json file already has \(lines) lines."
|
||||
toNSLog(msg)
|
||||
sendNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return jsonString
|
||||
}
|
||||
|
||||
// MARK: Send-to-Server Logic
|
||||
|
||||
/// does a (manual) sending attempt of all unsent log entries to SwiftyBeaver Platform
|
||||
public func sendNow() {
|
||||
|
||||
if sendFileExists() {
|
||||
toNSLog("reset points to 0")
|
||||
points = 0
|
||||
} else {
|
||||
if !renameJsonToSendFile() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !sendingInProgress {
|
||||
sendingInProgress = true
|
||||
//let (jsonString, lines) = logsFromFile(sendingFileURL)
|
||||
var lines = 0
|
||||
|
||||
guard let logEntries = logsFromFile(sendingFileURL) else {
|
||||
sendingInProgress = false
|
||||
return
|
||||
}
|
||||
|
||||
lines = logEntries.count
|
||||
|
||||
if lines > 0 {
|
||||
var payload = [String: Any]()
|
||||
// merge device and analytics dictionaries
|
||||
let deviceDetailsDict = deviceDetails()
|
||||
|
||||
var analyticsDict = analytics(analyticsFileURL)
|
||||
|
||||
for key in deviceDetailsDict.keys {
|
||||
analyticsDict[key] = deviceDetailsDict[key]
|
||||
}
|
||||
payload["device"] = analyticsDict
|
||||
payload["entries"] = logEntries
|
||||
|
||||
if let str = jsonStringFromDict(payload) {
|
||||
//toNSLog(str) // uncomment to see full payload
|
||||
toNSLog("Encrypting \(lines) log entries ...")
|
||||
if let encryptedStr = encrypt(str) {
|
||||
var msg = "Sending \(lines) encrypted log entries "
|
||||
msg += "(\(encryptedStr.length) chars) to server ..."
|
||||
toNSLog(msg)
|
||||
sendToServerAsync(encryptedStr) { ok, _ in
|
||||
|
||||
self.toNSLog("Sent \(lines) encrypted log entries to server, received ok: \(ok)")
|
||||
if ok {
|
||||
_ = self.deleteFile(self.sendingFileURL)
|
||||
}
|
||||
self.sendingInProgress = false
|
||||
self.points = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sendingInProgress = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// sends a string to the SwiftyBeaver Platform server, returns ok if status 200 and HTTP status
|
||||
func sendToServerAsync(_ str: String?, complete: @escaping (_ ok: Bool, _ status: Int) -> Void) {
|
||||
|
||||
let timeout = 10.0
|
||||
|
||||
if let payload = str, let queue = self.queue, let serverURL = serverURL {
|
||||
|
||||
// create operation queue which uses current serial queue of destination
|
||||
let operationQueue = OperationQueue()
|
||||
operationQueue.underlyingQueue = queue
|
||||
|
||||
let session = URLSession(configuration:
|
||||
URLSessionConfiguration.default,
|
||||
delegate: nil, delegateQueue: operationQueue)
|
||||
|
||||
toNSLog("assembling request ...")
|
||||
|
||||
// assemble request
|
||||
var request = URLRequest(url: serverURL,
|
||||
cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
|
||||
timeoutInterval: timeout)
|
||||
request.httpMethod = "POST"
|
||||
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.addValue("application/json", forHTTPHeaderField: "Accept")
|
||||
|
||||
// basic auth header (just works on Linux for Swift 3.1+, macOS is fine)
|
||||
guard let credentials = "\(appID):\(appSecret)".data(using: String.Encoding.utf8) else {
|
||||
toNSLog("Error! Could not set basic auth header")
|
||||
return complete(false, 0)
|
||||
}
|
||||
|
||||
#if os(Linux)
|
||||
let base64Credentials = Base64.encode([UInt8](credentials))
|
||||
#else
|
||||
let base64Credentials = credentials.base64EncodedString(options: [])
|
||||
#endif
|
||||
request.setValue("Basic \(base64Credentials)", forHTTPHeaderField: "Authorization")
|
||||
//toNSLog("\nrequest:")
|
||||
//print(request)
|
||||
|
||||
// POST parameters
|
||||
let params = ["payload": payload]
|
||||
if(JSONSerialization.isValidJSONObject(params)){
|
||||
do {
|
||||
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
|
||||
} catch {
|
||||
toNSLog("Error! Could not create JSON for server payload.")
|
||||
return complete(false, 0)
|
||||
}
|
||||
}else{
|
||||
return complete(false, 0)
|
||||
}
|
||||
toNSLog("sending params: \(params)")
|
||||
toNSLog("sending ...")
|
||||
|
||||
sendingInProgress = true
|
||||
|
||||
// send request async to server on destination queue
|
||||
let task = session.dataTask(with: request) { _, response, error in
|
||||
var ok = false
|
||||
var status = 0
|
||||
self.toNSLog("received response from server")
|
||||
|
||||
if let error = error {
|
||||
// an error did occur
|
||||
self.toNSLog("Error! Could not send entries to server. \(error)")
|
||||
} else {
|
||||
if let response = response as? HTTPURLResponse {
|
||||
status = response.statusCode
|
||||
if status == 200 {
|
||||
// all went well, entries were uploaded to server
|
||||
ok = true
|
||||
} else {
|
||||
// status code was not 200
|
||||
var msg = "Error! Sending entries to server failed "
|
||||
msg += "with status code \(status)"
|
||||
self.toNSLog(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
return complete(ok, status)
|
||||
}
|
||||
task.resume()
|
||||
session.finishTasksAndInvalidate()
|
||||
//while true {} // commenting this line causes a crash on Linux unit tests?!?
|
||||
}
|
||||
}
|
||||
|
||||
/// returns sending points based on level
|
||||
func sendingPointsForLevel(_ level: SwiftyBeaver.Level) -> Int {
|
||||
|
||||
switch level {
|
||||
case .debug:
|
||||
return sendingPoints.debug
|
||||
case .info:
|
||||
return sendingPoints.info
|
||||
case .warning:
|
||||
return sendingPoints.warning
|
||||
case .error:
|
||||
return sendingPoints.error
|
||||
default:
|
||||
return sendingPoints.verbose
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: File Handling
|
||||
|
||||
/// appends a string as line to a file.
|
||||
/// returns boolean about success
|
||||
func saveToFile(_ str: String, url: URL, overwrite: Bool = false) -> Bool {
|
||||
do {
|
||||
if fileManager.fileExists(atPath: url.path) == false || overwrite {
|
||||
// create file if not existing
|
||||
let line = str + "\n"
|
||||
try line.write(to: url, atomically: true, encoding: String.Encoding.utf8)
|
||||
} else {
|
||||
// append to end of file
|
||||
let fileHandle = try FileHandle(forWritingTo: url)
|
||||
_ = fileHandle.seekToEndOfFile()
|
||||
let line = str + "\n"
|
||||
if let data = line.data(using: String.Encoding.utf8) {
|
||||
fileHandle.write(data)
|
||||
fileHandle.closeFile()
|
||||
}
|
||||
}
|
||||
return true
|
||||
} catch {
|
||||
toNSLog("Error! Could not write to file \(url).")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func sendFileExists() -> Bool {
|
||||
return fileManager.fileExists(atPath: sendingFileURL.path)
|
||||
}
|
||||
|
||||
func renameJsonToSendFile() -> Bool {
|
||||
do {
|
||||
try fileManager.moveItem(at: entriesFileURL, to: sendingFileURL)
|
||||
return true
|
||||
} catch {
|
||||
toNSLog("SwiftyBeaver Platform Destination could not rename json file.")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// returns optional array of log dicts from a file which has 1 json string per line
|
||||
func logsFromFile(_ url: URL) -> [[String: Any]]? {
|
||||
var lines = 0
|
||||
do {
|
||||
// try to read file, decode every JSON line and put dict from each line in array
|
||||
let fileContent = try String(contentsOfFile: url.path, encoding: .utf8)
|
||||
let linesArray = fileContent.components(separatedBy: "\n")
|
||||
var dicts = [[String: Any]()] // array of dictionaries
|
||||
for lineJSON in linesArray {
|
||||
lines += 1
|
||||
if lineJSON.firstChar == "{" && lineJSON.lastChar == "}" {
|
||||
// try to parse json string into dict
|
||||
if let data = lineJSON.data(using: .utf8) {
|
||||
do {
|
||||
if let dict = try JSONSerialization.jsonObject(with: data,
|
||||
options: .mutableContainers) as? [String: Any] {
|
||||
if !dict.isEmpty {
|
||||
dicts.append(dict)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
var msg = "Error! Could not parse "
|
||||
msg += "line \(lines) in file \(url)."
|
||||
toNSLog(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dicts.removeFirst()
|
||||
return dicts
|
||||
} catch {
|
||||
toNSLog("Error! Could not read file \(url).")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// returns AES-256 CBC encrypted optional string
|
||||
func encrypt(_ str: String) -> String? {
|
||||
return AES256CBC.encryptString(str, password: encryptionKey)
|
||||
}
|
||||
|
||||
/// Delete file to get started again
|
||||
func deleteFile(_ url: URL) -> Bool {
|
||||
do {
|
||||
try FileManager.default.removeItem(at: url)
|
||||
return true
|
||||
} catch {
|
||||
toNSLog("Warning! Could not delete file \(url).")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MARK: Device & Analytics
|
||||
|
||||
// returns dict with device details. Amount depends on platform
|
||||
func deviceDetails() -> [String: String] {
|
||||
var details = [String: String]()
|
||||
|
||||
details["os"] = OS
|
||||
let osVersion = ProcessInfo.processInfo.operatingSystemVersion
|
||||
// becomes for example 10.11.2 for El Capitan
|
||||
var osVersionStr = String(osVersion.majorVersion)
|
||||
osVersionStr += "." + String(osVersion.minorVersion)
|
||||
osVersionStr += "." + String(osVersion.patchVersion)
|
||||
details["osVersion"] = osVersionStr
|
||||
details["deviceName"] = ""
|
||||
details["deviceModel"] = ""
|
||||
details["hostName"] = ""
|
||||
if DEVICE_NAME != "" {
|
||||
details["deviceName"] = DEVICE_NAME
|
||||
}
|
||||
if DEVICE_MODEL != "" {
|
||||
details["deviceModel"] = DEVICE_MODEL
|
||||
}
|
||||
return details
|
||||
}
|
||||
|
||||
/// returns (updated) analytics dict, optionally loaded from file.
|
||||
func analytics(_ url: URL, update: Bool = false) -> [String: Any] {
|
||||
|
||||
var dict = [String: Any]()
|
||||
let now = NSDate().timeIntervalSince1970
|
||||
|
||||
uuid = NSUUID().uuidString
|
||||
dict["uuid"] = uuid
|
||||
dict["firstStart"] = now
|
||||
dict["lastStart"] = now
|
||||
dict["starts"] = 1
|
||||
dict["userName"] = analyticsUserName
|
||||
dict["firstAppVersion"] = appVersion()
|
||||
dict["appVersion"] = appVersion()
|
||||
dict["firstAppBuild"] = appBuild()
|
||||
dict["appBuild"] = appBuild()
|
||||
|
||||
if let loadedDict = dictFromFile(analyticsFileURL) {
|
||||
if let val = loadedDict["firstStart"] as? Double {
|
||||
dict["firstStart"] = val
|
||||
}
|
||||
if let val = loadedDict["lastStart"] as? Double {
|
||||
if update {
|
||||
dict["lastStart"] = now
|
||||
} else {
|
||||
dict["lastStart"] = val
|
||||
}
|
||||
}
|
||||
if let val = loadedDict["starts"] as? Int {
|
||||
if update {
|
||||
dict["starts"] = val + 1
|
||||
} else {
|
||||
dict["starts"] = val
|
||||
}
|
||||
}
|
||||
if let val = loadedDict["uuid"] as? String {
|
||||
dict["uuid"] = val
|
||||
uuid = val
|
||||
}
|
||||
if let val = loadedDict["userName"] as? String {
|
||||
if update && !analyticsUserName.isEmpty {
|
||||
dict["userName"] = analyticsUserName
|
||||
} else {
|
||||
if !val.isEmpty {
|
||||
dict["userName"] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
if let val = loadedDict["firstAppVersion"] as? String {
|
||||
dict["firstAppVersion"] = val
|
||||
}
|
||||
if let val = loadedDict["firstAppBuild"] as? Int {
|
||||
dict["firstAppBuild"] = val
|
||||
}
|
||||
}
|
||||
return dict
|
||||
}
|
||||
|
||||
/// Returns the current app version string (like 1.2.5) or empty string on error
|
||||
func appVersion() -> String {
|
||||
if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String {
|
||||
return version
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
/// Returns the current app build as integer (like 563, always incrementing) or 0 on error
|
||||
func appBuild() -> Int {
|
||||
if let version = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
|
||||
if let intVersion = Int(version) {
|
||||
return intVersion
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/// returns optional dict from a json encoded file
|
||||
func dictFromFile(_ url: URL) -> [String: Any]? {
|
||||
do {
|
||||
let fileContent = try String(contentsOfFile: url.path, encoding: .utf8)
|
||||
if let data = fileContent.data(using: .utf8) {
|
||||
return try JSONSerialization.jsonObject(with: data,
|
||||
options: .mutableContainers) as? [String: Any]
|
||||
}
|
||||
} catch {
|
||||
toNSLog("SwiftyBeaver Platform Destination could not read file \(url)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// turns dict into JSON and saves it to file
|
||||
func saveDictToFile(_ dict: [String: Any], url: URL) -> Bool {
|
||||
let jsonString = jsonStringFromDict(dict)
|
||||
|
||||
if let str = jsonString {
|
||||
toNSLog("saving '\(str)' to \(url)")
|
||||
return saveToFile(str, url: url, overwrite: true)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MARK: Debug Helpers
|
||||
|
||||
/// log String to toNSLog. Used to debug the class logic
|
||||
func toNSLog(_ str: String) {
|
||||
if showNSLog {
|
||||
#if os(Linux)
|
||||
print("SBPlatform: \(str)")
|
||||
#else
|
||||
NSLog("SBPlatform: \(str)")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// returns the current thread name
|
||||
class func threadName() -> String {
|
||||
|
||||
#if os(Linux)
|
||||
// on 9/30/2016 not yet implemented in server-side Swift:
|
||||
// > import Foundation
|
||||
// > Thread.isMainThread
|
||||
return ""
|
||||
#else
|
||||
if Thread.isMainThread {
|
||||
return ""
|
||||
} else {
|
||||
let threadName = Thread.current.name
|
||||
if let threadName = threadName, !threadName.isEmpty {
|
||||
return threadName
|
||||
} else {
|
||||
return String(format: "%p", Thread.current)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
//
|
||||
// SwiftyBeaver.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Sebastian Kreutzberger (Twitter @skreutzb) on 28.11.15.
|
||||
// Copyright © 2015 Sebastian Kreutzberger
|
||||
// Some rights reserved: http://opensource.org/licenses/MIT
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
open class SwiftyBeaver {
|
||||
|
||||
/// version string of framework
|
||||
public static let version = "1.9.4" // UPDATE ON RELEASE!
|
||||
/// build number of framework
|
||||
public static let build = 1950 // version 1.6.2 -> 1620, UPDATE ON RELEASE!
|
||||
|
||||
public enum Level: Int {
|
||||
case verbose = 0
|
||||
case debug = 1
|
||||
case info = 2
|
||||
case warning = 3
|
||||
case error = 4
|
||||
}
|
||||
|
||||
// a set of active destinations
|
||||
public private(set) static var destinations = Set<BaseDestination>()
|
||||
|
||||
// MARK: Destination Handling
|
||||
|
||||
/// returns boolean about success
|
||||
@discardableResult
|
||||
open class func addDestination(_ destination: BaseDestination) -> Bool {
|
||||
if destinations.contains(destination) {
|
||||
return false
|
||||
}
|
||||
destinations.insert(destination)
|
||||
return true
|
||||
}
|
||||
|
||||
/// returns boolean about success
|
||||
@discardableResult
|
||||
open class func removeDestination(_ destination: BaseDestination) -> Bool {
|
||||
if destinations.contains(destination) == false {
|
||||
return false
|
||||
}
|
||||
destinations.remove(destination)
|
||||
return true
|
||||
}
|
||||
|
||||
/// if you need to start fresh
|
||||
open class func removeAllDestinations() {
|
||||
destinations.removeAll()
|
||||
}
|
||||
|
||||
/// returns the amount of destinations
|
||||
open class func countDestinations() -> Int {
|
||||
return destinations.count
|
||||
}
|
||||
|
||||
/// returns the current thread name
|
||||
class func threadName() -> String {
|
||||
|
||||
#if os(Linux)
|
||||
// on 9/30/2016 not yet implemented in server-side Swift:
|
||||
// > import Foundation
|
||||
// > Thread.isMainThread
|
||||
return ""
|
||||
#else
|
||||
if Thread.isMainThread {
|
||||
return ""
|
||||
} else {
|
||||
let name = __dispatch_queue_get_label(nil)
|
||||
return String(cString: name, encoding: .utf8) ?? Thread.current.description
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// MARK: Levels
|
||||
|
||||
/// log something generally unimportant (lowest priority)
|
||||
open class func verbose(_ message: @autoclosure () -> Any, _
|
||||
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
|
||||
#if swift(>=5)
|
||||
custom(level: .verbose, message: message(), file: file, function: function, line: line, context: context)
|
||||
#else
|
||||
custom(level: .verbose, message: message, file: file, function: function, line: line, context: context)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// log something which help during debugging (low priority)
|
||||
open class func debug(_ message: @autoclosure () -> Any, _
|
||||
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
|
||||
#if swift(>=5)
|
||||
custom(level: .debug, message: message(), file: file, function: function, line: line, context: context)
|
||||
#else
|
||||
custom(level: .debug, message: message, file: file, function: function, line: line, context: context)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// log something which you are really interested but which is not an issue or error (normal priority)
|
||||
open class func info(_ message: @autoclosure () -> Any, _
|
||||
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
|
||||
#if swift(>=5)
|
||||
custom(level: .info, message: message(), file: file, function: function, line: line, context: context)
|
||||
#else
|
||||
custom(level: .info, message: message, file: file, function: function, line: line, context: context)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// log something which may cause big trouble soon (high priority)
|
||||
open class func warning(_ message: @autoclosure () -> Any, _
|
||||
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
|
||||
#if swift(>=5)
|
||||
custom(level: .warning, message: message(), file: file, function: function, line: line, context: context)
|
||||
#else
|
||||
custom(level: .warning, message: message, file: file, function: function, line: line, context: context)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// log something which will keep you awake at night (highest priority)
|
||||
open class func error(_ message: @autoclosure () -> Any, _
|
||||
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
|
||||
#if swift(>=5)
|
||||
custom(level: .error, message: message(), file: file, function: function, line: line, context: context)
|
||||
#else
|
||||
custom(level: .error, message: message, file: file, function: function, line: line, context: context)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// custom logging to manually adjust values, should just be used by other frameworks
|
||||
open class func custom(level: SwiftyBeaver.Level, message: @autoclosure () -> Any,
|
||||
file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) {
|
||||
#if swift(>=5)
|
||||
dispatch_send(level: level, message: message(), thread: threadName(),
|
||||
file: file, function: function, line: line, context: context)
|
||||
#else
|
||||
dispatch_send(level: level, message: message, thread: threadName(),
|
||||
file: file, function: function, line: line, context: context)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// internal helper which dispatches send to dedicated queue if minLevel is ok
|
||||
class func dispatch_send(level: SwiftyBeaver.Level, message: @autoclosure () -> Any,
|
||||
thread: String, file: String, function: String, line: Int, context: Any?) {
|
||||
var resolvedMessage: String?
|
||||
for dest in destinations {
|
||||
|
||||
guard let queue = dest.queue else {
|
||||
continue
|
||||
}
|
||||
|
||||
resolvedMessage = resolvedMessage == nil && dest.hasMessageFilters() ? "\(message())" : resolvedMessage
|
||||
if dest.shouldLevelBeLogged(level, path: file, function: function, message: resolvedMessage) {
|
||||
// try to convert msg object to String and put it on queue
|
||||
let msgStr = resolvedMessage == nil ? "\(message())" : resolvedMessage!
|
||||
let f = stripParams(function: function)
|
||||
|
||||
if dest.asynchronously {
|
||||
queue.async {
|
||||
_ = dest.send(level, msg: msgStr, thread: thread, file: file, function: f, line: line, context: context)
|
||||
}
|
||||
} else {
|
||||
queue.sync {
|
||||
_ = dest.send(level, msg: msgStr, thread: thread, file: file, function: f, line: line, context: context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
DEPRECATED & NEEDS COMPLETE REWRITE DUE TO SWIFT 3 AND GENERAL INCORRECT LOGIC
|
||||
Flush all destinations to make sure all logging messages have been written out
|
||||
Returns after all messages flushed or timeout seconds
|
||||
|
||||
- returns: true if all messages flushed, false if timeout or error occurred
|
||||
*/
|
||||
public class func flush(secondTimeout: Int64) -> Bool {
|
||||
|
||||
/*
|
||||
guard let grp = dispatch_group_create() else { return false }
|
||||
for dest in destinations {
|
||||
if let queue = dest.queue {
|
||||
dispatch_group_enter(grp)
|
||||
queue.asynchronously(execute: {
|
||||
dest.flush()
|
||||
grp.leave()
|
||||
})
|
||||
}
|
||||
}
|
||||
let waitUntil = DispatchTime.now(dispatch_time_t(DISPATCH_TIME_NOW), secondTimeout * 1000000000)
|
||||
return dispatch_group_wait(grp, waitUntil) == 0
|
||||
*/
|
||||
return true
|
||||
}
|
||||
|
||||
/// removes the parameters from a function because it looks weird with a single param
|
||||
class func stripParams(function: String) -> String {
|
||||
var f = function
|
||||
if let indexOfBrace = f.find("(") {
|
||||
#if swift(>=4.0)
|
||||
f = String(f[..<indexOfBrace])
|
||||
#else
|
||||
f = f.substring(to: indexOfBrace)
|
||||
#endif
|
||||
}
|
||||
f += "()"
|
||||
return f
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "SwiftyBeaver"
|
||||
s.version = "1.9.5"
|
||||
s.summary = "Convenient logging during development & release in Swift 4 & 5."
|
||||
|
||||
# This description is used to generate tags and improve search results.
|
||||
# * Think: What does it do? Why did you write it? What is the focus?
|
||||
# * Try to keep it short, snappy and to the point.
|
||||
# * Write the description between the DESC delimiters below.
|
||||
# * Finally, don't worry about the indent, CocoaPods strips it!
|
||||
s.description = <<-DESC
|
||||
Easy-to-use, extensible & powerful logging & analytics for Swift 4 and Swift 5.
|
||||
Great for development & release due to its support for many logging destinations & platforms.
|
||||
DESC
|
||||
|
||||
s.homepage = "https://github.com/SwiftyBeaver/SwiftyBeaver"
|
||||
s.screenshots = "https://cloud.githubusercontent.com/assets/564725/11452558/17fd5f04-95ec-11e5-96d2-427f62ed4f05.jpg", "https://cloud.githubusercontent.com/assets/564725/11452560/33225d16-95ec-11e5-8461-78f50b9e8da7.jpg"
|
||||
s.license = "MIT"
|
||||
s.author = { "Sebastian Kreutzberger" => "s.kreutzberger@googlemail.com" }
|
||||
s.ios.deployment_target = "9.0"
|
||||
s.watchos.deployment_target = "2.0"
|
||||
s.tvos.deployment_target = "9.0"
|
||||
s.osx.deployment_target = "10.10"
|
||||
s.source = { :git => "https://github.com/SwiftyBeaver/SwiftyBeaver.git", :tag => "1.9.5" }
|
||||
s.source_files = "Sources"
|
||||
s.swift_versions = ['4.0', '4.2', '5.0', '5.1']
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
import XCTest
|
||||
@testable import SwiftyBeaverTests
|
||||
|
||||
XCTMain([
|
||||
testCase(AES256CBCTests.allTests), // takes too long
|
||||
testCase(BaseDestinationTests.allTests),
|
||||
//testCase(DestinationSetTests.allTests),
|
||||
testCase(ConsoleDestinationTests.allTests),
|
||||
//testCase(FileDestinationTests.allTests),
|
||||
//testCase(SBPlatformDestinationTests.allTests),
|
||||
testCase(SwiftyBeaverTests.allTests),
|
||||
//testCase(GoogleCloudDestinationTests.allTests)
|
||||
])
|
||||
|
||||
// All tests:
|
||||
// the SBPlatformDestinationTests crashes testing under Linux with a linker issue?
|
||||
// Log into Docker container to find issue:
|
||||
// docker run --rm -it -v $PWD:/app swiftybeaver /bin/bash -c "cd /app ; swift build ; swift test"
|
||||
|
||||
/*
|
||||
|
||||
testCase(AES256CBCTests.allTests),
|
||||
testCase(BaseDestinationTests.allTests),
|
||||
testCase(DestinationSetTests.allTests),
|
||||
testCase(FileDestinationTests.allTests),
|
||||
testCase(SBPlatformDestinationTests.allTests),
|
||||
testCase(SwiftyBeaverTests.allTests),
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,138 @@
|
||||
//
|
||||
// AES256CBCTests.swift
|
||||
// AES256CBCTests
|
||||
//
|
||||
// Created by Sebastian Kreutzberger on 2/9/16.
|
||||
// Copyright © 2016 SwiftyBeaver. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import SwiftyBeaver
|
||||
|
||||
class AES256CBCTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testEncryptDecryptStringCycle() {
|
||||
// encrypt. password must be 32 chars long
|
||||
let str = AES256CBC.randomText(32)
|
||||
let password = AES256CBC.randomText(32)
|
||||
let encrypted = AES256CBC.encryptString(str, password: password)
|
||||
XCTAssertNotNil(encrypted)
|
||||
//print("str: \(str)")
|
||||
//print("password: \(password)")
|
||||
//printn("encrypted secret (IV is at first 16 chars): \(encrypted)")
|
||||
|
||||
if let encrypted = encrypted {
|
||||
XCTAssertGreaterThan(encrypted.length, 16)
|
||||
// decrypt
|
||||
let decrypted = AES256CBC.decryptString(encrypted, password: password)
|
||||
//print("decrypted str: \(decrypted)")
|
||||
XCTAssertNotNil(decrypted)
|
||||
XCTAssertEqual(decrypted, str)
|
||||
}
|
||||
}
|
||||
|
||||
func testEncryptString() {
|
||||
// str length can vary due to padding, password must be 32 chars long
|
||||
var str = ""
|
||||
var password = ""
|
||||
var encrypted = AES256CBC.encryptString(str, password: password)
|
||||
XCTAssertNil(encrypted)
|
||||
|
||||
// str must be longer than 0 characters
|
||||
str = ""
|
||||
password = AES256CBC.randomText(32)
|
||||
encrypted = AES256CBC.encryptString(str, password: password)
|
||||
XCTAssertNil(encrypted)
|
||||
|
||||
// password must be exactly 32 chars long
|
||||
str = AES256CBC.randomText(20)
|
||||
password = AES256CBC.randomText(31)
|
||||
encrypted = AES256CBC.encryptString(str, password: password)
|
||||
XCTAssertNil(encrypted)
|
||||
|
||||
password = AES256CBC.randomText(33)
|
||||
encrypted = AES256CBC.encryptString(str, password: password)
|
||||
XCTAssertNil(encrypted)
|
||||
|
||||
// that works, even with str length of 3 due to padding
|
||||
str = "x"
|
||||
password = AES256CBC.randomText(32)
|
||||
encrypted = AES256CBC.encryptString(str, password: password)
|
||||
XCTAssertNotNil(encrypted)
|
||||
//NSLog("str: \(str)")
|
||||
//NSLog("password: \(password)")
|
||||
//NSLog("encrypted: \(encrypted)")
|
||||
|
||||
if let encrypted = encrypted {
|
||||
XCTAssertGreaterThan(encrypted.length, 16)
|
||||
// decrypt
|
||||
let decrypted = AES256CBC.decryptString(encrypted, password: password)
|
||||
XCTAssertNotNil(decrypted)
|
||||
XCTAssertEqual(decrypted, str)
|
||||
}
|
||||
}
|
||||
|
||||
func testDecryptString() {
|
||||
// the encrypted string must be at least 17 chars long because the first
|
||||
// 16 chars are the IV. password must be 32 chars long
|
||||
var str = ""
|
||||
var password = ""
|
||||
var decrypted = AES256CBC.decryptString(str, password: password)
|
||||
XCTAssertNil(decrypted)
|
||||
|
||||
// str must be at least 17 chars long
|
||||
str = AES256CBC.randomText(16)
|
||||
password = AES256CBC.randomText(32)
|
||||
decrypted = AES256CBC.decryptString(str, password: password)
|
||||
XCTAssertNil(decrypted)
|
||||
|
||||
str = AES256CBC.randomText(17)
|
||||
// password must be exactly 32 chars long
|
||||
password = AES256CBC.randomText(31)
|
||||
decrypted = AES256CBC.decryptString(str, password: password)
|
||||
XCTAssertNil(decrypted)
|
||||
|
||||
password = AES256CBC.randomText(33)
|
||||
decrypted = AES256CBC.decryptString(str, password: password)
|
||||
XCTAssertNil(decrypted)
|
||||
}
|
||||
|
||||
func testRandomText() {
|
||||
let length = 6
|
||||
let text = AES256CBC.randomText(length)
|
||||
let text2 = AES256CBC.randomText(length)
|
||||
XCTAssertEqual(text.length, length)
|
||||
XCTAssertEqual(text2.length, length)
|
||||
XCTAssertNotEqual(text, text2)
|
||||
XCTAssertNil(text.range(of: " "))
|
||||
XCTAssertNil(text2.range(of: " "))
|
||||
}
|
||||
|
||||
func testGeneratePassword() {
|
||||
let pw = AES256CBC.generatePassword()
|
||||
let pw2 = AES256CBC.generatePassword()
|
||||
XCTAssertEqual(pw.length, 32)
|
||||
XCTAssertEqual(pw2.length, 32)
|
||||
XCTAssertNotEqual(pw, pw2)
|
||||
XCTAssertNil(pw.range(of: " "))
|
||||
XCTAssertNil(pw2.range(of: " "))
|
||||
}
|
||||
|
||||
// MARK: Linux allTests
|
||||
|
||||
static let allTests = [
|
||||
("testEncryptDecryptStringCycle", testEncryptDecryptStringCycle),
|
||||
("testEncryptString", testEncryptString),
|
||||
("testDecryptString", testDecryptString),
|
||||
("testRandomText", testRandomText),
|
||||
("testGeneratePassword", testGeneratePassword)
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,699 @@
|
||||
//
|
||||
// BaseDestinationTests.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Sebastian Kreutzberger on 05.12.15.
|
||||
// Copyright © 2015 Sebastian Kreutzberger
|
||||
// Some rights reserved: http://opensource.org/licenses/MIT
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
@testable import SwiftyBeaver
|
||||
|
||||
class BaseDestinationTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testInit() {
|
||||
let obj = BaseDestination()
|
||||
XCTAssertNotNil(obj.queue)
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// MARK: Format
|
||||
////////////////////////////////
|
||||
|
||||
func testFormatMessage() {
|
||||
let obj = BaseDestination()
|
||||
var str = ""
|
||||
var format = ""
|
||||
|
||||
// empty format
|
||||
format = ""
|
||||
str = obj.formatMessage(format, level: .verbose, msg: "Hello", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50)
|
||||
XCTAssertEqual(str, "")
|
||||
|
||||
// format without variables
|
||||
format = "Hello"
|
||||
str = obj.formatMessage(format, level: .verbose, msg: "Hello", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50)
|
||||
XCTAssertEqual(str, "Hello")
|
||||
|
||||
// format without variables (make sure the L is not interpreted as format character)
|
||||
format = "Linda"
|
||||
str = obj.formatMessage(format, level: .verbose, msg: "Hello", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50)
|
||||
XCTAssertEqual(str, "Linda")
|
||||
format = "$Linda $M"
|
||||
str = obj.formatMessage(format, level: .verbose, msg: "Hello", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50)
|
||||
XCTAssertEqual(str, "VERBOSEinda Hello")
|
||||
|
||||
// weird format
|
||||
format = "$"
|
||||
str = obj.formatMessage(format, level: .verbose, msg: "Hello", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50)
|
||||
XCTAssertEqual(str, "")
|
||||
|
||||
// basic format with ignored color and thread
|
||||
format = "|$T| $C$L$c: $M"
|
||||
str = obj.formatMessage(format, level: .verbose, msg: "Hello", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50)
|
||||
XCTAssertEqual(str, "|main| VERBOSE: Hello")
|
||||
|
||||
// format with date and color
|
||||
let obj2 = BaseDestination()
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
||||
let dateStr = formatter.string(from: Date())
|
||||
|
||||
obj2.levelColor.verbose = "?"
|
||||
obj2.escape = ">"
|
||||
obj2.reset = "<"
|
||||
|
||||
format = "[$Dyyyy-MM-dd HH:mm:ss$d] |$T| $N.$F:$l $C$L$c: $M"
|
||||
str = obj2.formatMessage(format, level: .verbose, msg: "Hello", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50)
|
||||
XCTAssertEqual(str, "[\(dateStr)] |main| ViewController.testFunction():50 >?VERBOSE<: Hello")
|
||||
|
||||
// UTC datetime
|
||||
let obj3 = BaseDestination()
|
||||
let utcFormatter = DateFormatter()
|
||||
utcFormatter.timeZone = TimeZone(abbreviation: "UTC")
|
||||
utcFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
||||
let utcDateStr = utcFormatter.string(from: Date())
|
||||
str = BaseDestination().formatDate(utcFormatter.dateFormat, timeZone: "UTC")
|
||||
|
||||
format = "$Zyyyy-MM-dd HH:mm:ss$z"
|
||||
str = obj3.formatMessage(format, level: .verbose, msg: "Hello", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50)
|
||||
XCTAssertEqual(str, "\(utcDateStr)")
|
||||
|
||||
// context in different formats
|
||||
let obj4 = BaseDestination()
|
||||
format = "$L: $M $X"
|
||||
str = obj4.formatMessage(format, level: .verbose, msg: "Hello", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50, context: "Context!")
|
||||
XCTAssertEqual(str, "VERBOSE: Hello Context!")
|
||||
|
||||
str = obj4.formatMessage(format, level: .verbose, msg: "Hello", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50, context: 123)
|
||||
XCTAssertEqual(str, "VERBOSE: Hello 123")
|
||||
|
||||
str = obj4.formatMessage(format, level: .verbose, msg: "Hello", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50, context: [1, "a", 2])
|
||||
XCTAssertEqual(str, "VERBOSE: Hello [1, \"a\", 2]")
|
||||
|
||||
str = obj4.formatMessage(format, level: .verbose, msg: "Hello", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50, context: nil)
|
||||
XCTAssertEqual(str, "VERBOSE: Hello")
|
||||
|
||||
// context in the middle
|
||||
let obj5 = BaseDestination()
|
||||
format = "$L: [$X] $M"
|
||||
str = obj5.formatMessage(format, level: .verbose, msg: "Hello", thread: "main", file: "/path/to/ViewController.swift", function: "testFunction()", line: 50, context: "Context!")
|
||||
XCTAssertEqual(str, "VERBOSE: [Context!] Hello")
|
||||
// no context
|
||||
str = obj5.formatMessage(format, level: .verbose, msg: "Hello", thread: "main", file: "/path/to/ViewController.swift", function: "testFunction()", line: 50)
|
||||
XCTAssertEqual(str, "VERBOSE: [] Hello")
|
||||
|
||||
// misc. paddings
|
||||
let obj6 = BaseDestination()
|
||||
format = "[$-8L]"
|
||||
str = obj6.formatMessage(format, level: .debug, msg: "Hello", thread: "main", file: "/path/to/ViewController.swift", function: "testFunction()", line: 50)
|
||||
XCTAssertEqual(str, "[DEBUG ]")
|
||||
format = "$-8L"
|
||||
str = obj6.formatMessage(format, level: .debug, msg: "Hello", thread: "main", file: "/path/to/ViewController.swift", function: "testFunction()", line: 50)
|
||||
XCTAssertEqual(str, "DEBUG")
|
||||
format = "$8L"
|
||||
str = obj6.formatMessage(format, level: .debug, msg: "Hello", thread: "main", file: "/path/to/ViewController.swift", function: "testFunction()", line: 50)
|
||||
XCTAssertEqual(str, " DEBUG")
|
||||
format = "$-8L:_$10X___$M"
|
||||
str = obj6.formatMessage(format, level: .debug, msg: "Hello", thread: "main", file: "/path/to/ViewController.swift", function: "testFunction()", line: 50, context: "Context!")
|
||||
|
||||
obj6.levelColor.verbose = "?"
|
||||
obj6.escape = ">"
|
||||
obj6.reset = "<"
|
||||
XCTAssertEqual(str, "DEBUG :_ Context!___Hello")
|
||||
format = "[$Dyyyy-MM-dd HH:mm:ss$d] |$T| $N.$F:$l $C$L$c: $M"
|
||||
str = obj6.formatMessage(format, level: .verbose, msg: "Hello", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50)
|
||||
XCTAssertEqual(str, "[\(dateStr)] |main| ViewController.testFunction():50 >?VERBOSE<: Hello")
|
||||
}
|
||||
|
||||
func testMessageToJSON() {
|
||||
let obj = BaseDestination()
|
||||
guard let str = obj.messageToJSON(.info, msg: "hello world", thread: "main",
|
||||
file: "/path/to/ViewController.swift", function: "testFunction()", line: 50, context: ["foo": "bar", "hello": 2]) else {
|
||||
XCTFail("str should not be nil"); return
|
||||
}
|
||||
print(str)
|
||||
// decode JSON string into dict and compare if it is the the same
|
||||
guard let data = str.data(using: .utf8),
|
||||
let json = try? JSONSerialization.jsonObject(with: data, options: []),
|
||||
let dict = json as? [String: Any],
|
||||
let timestamp = dict["timestamp"] as? Double,
|
||||
let level = dict["level"] as? Int,
|
||||
let message = dict["message"] as? String,
|
||||
let thread = dict["thread"] as? String,
|
||||
let file = dict["file"] as? String,
|
||||
let function = dict["function"] as? String,
|
||||
let line = dict["line"] as? Int,
|
||||
let context = dict["context"] as? [String: Any] else {
|
||||
XCTFail("dict and its properties should not be nil"); return
|
||||
}
|
||||
XCTAssertGreaterThanOrEqual(timestamp, Date().timeIntervalSince1970 - 10)
|
||||
XCTAssertEqual(level, SwiftyBeaver.Level.info.rawValue)
|
||||
XCTAssertEqual(message, "hello world")
|
||||
XCTAssertEqual(thread, "main")
|
||||
XCTAssertEqual(file, "/path/to/ViewController.swift")
|
||||
XCTAssertEqual(function, "testFunction()")
|
||||
XCTAssertEqual(line, 50)
|
||||
XCTAssertEqual(context["foo"] as? String, "bar")
|
||||
XCTAssertEqual(context["hello"] as? Int, 2)
|
||||
}
|
||||
|
||||
func testLevelWord() {
|
||||
let obj = BaseDestination()
|
||||
var str = ""
|
||||
|
||||
str = obj.levelWord(SwiftyBeaver.Level.verbose)
|
||||
XCTAssertNotNil(str, "VERBOSE")
|
||||
str = obj.levelWord(SwiftyBeaver.Level.debug)
|
||||
XCTAssertNotNil(str, "DEBUG")
|
||||
str = obj.levelWord(SwiftyBeaver.Level.info)
|
||||
XCTAssertNotNil(str, "INFO")
|
||||
str = obj.levelWord(SwiftyBeaver.Level.warning)
|
||||
XCTAssertNotNil(str, "WARNING")
|
||||
str = obj.levelWord(SwiftyBeaver.Level.error)
|
||||
XCTAssertNotNil(str, "ERROR")
|
||||
|
||||
// custom level strings
|
||||
obj.levelString.verbose = "Who cares"
|
||||
obj.levelString.debug = "Look"
|
||||
obj.levelString.info = "Interesting"
|
||||
obj.levelString.warning = "Oh oh"
|
||||
obj.levelString.error = "OMG!!!"
|
||||
|
||||
str = obj.levelWord(SwiftyBeaver.Level.verbose)
|
||||
XCTAssertNotNil(str, "Who cares")
|
||||
str = obj.levelWord(SwiftyBeaver.Level.debug)
|
||||
XCTAssertNotNil(str, "Look")
|
||||
str = obj.levelWord(SwiftyBeaver.Level.info)
|
||||
XCTAssertNotNil(str, "Interesting")
|
||||
str = obj.levelWord(SwiftyBeaver.Level.warning)
|
||||
XCTAssertNotNil(str, "Oh oh")
|
||||
str = obj.levelWord(SwiftyBeaver.Level.error)
|
||||
XCTAssertNotNil(str, "OMG!!!")
|
||||
}
|
||||
|
||||
func testColorForLevel() {
|
||||
let obj = BaseDestination()
|
||||
var str = ""
|
||||
|
||||
// empty on default
|
||||
str = obj.colorForLevel(SwiftyBeaver.Level.verbose)
|
||||
XCTAssertNotNil(str, "")
|
||||
str = obj.colorForLevel(SwiftyBeaver.Level.debug)
|
||||
XCTAssertNotNil(str, "")
|
||||
str = obj.colorForLevel(SwiftyBeaver.Level.info)
|
||||
XCTAssertNotNil(str, "")
|
||||
str = obj.colorForLevel(SwiftyBeaver.Level.warning)
|
||||
XCTAssertNotNil(str, "")
|
||||
str = obj.colorForLevel(SwiftyBeaver.Level.error)
|
||||
XCTAssertNotNil(str, "")
|
||||
|
||||
// custom level color strings
|
||||
obj.levelString.verbose = "silver"
|
||||
obj.levelString.debug = "green"
|
||||
obj.levelString.info = "blue"
|
||||
obj.levelString.warning = "yellow"
|
||||
obj.levelString.error = "red"
|
||||
|
||||
str = obj.colorForLevel(SwiftyBeaver.Level.verbose)
|
||||
XCTAssertNotNil(str, "silver")
|
||||
str = obj.colorForLevel(SwiftyBeaver.Level.debug)
|
||||
XCTAssertNotNil(str, "green")
|
||||
str = obj.colorForLevel(SwiftyBeaver.Level.info)
|
||||
XCTAssertNotNil(str, "blue")
|
||||
str = obj.colorForLevel(SwiftyBeaver.Level.warning)
|
||||
XCTAssertNotNil(str, "yellow")
|
||||
str = obj.colorForLevel(SwiftyBeaver.Level.error)
|
||||
XCTAssertNotNil(str, "red")
|
||||
}
|
||||
|
||||
func testFileNameOfFile() {
|
||||
let obj = BaseDestination()
|
||||
var str = ""
|
||||
|
||||
str = obj.fileNameOfFile("")
|
||||
XCTAssertEqual(str, "")
|
||||
str = obj.fileNameOfFile("foo.bar")
|
||||
XCTAssertEqual(str, "foo.bar")
|
||||
str = obj.fileNameOfFile("path/to/ViewController.swift")
|
||||
XCTAssertEqual(str, "ViewController.swift")
|
||||
}
|
||||
|
||||
func testFileNameOfFileWithoutSuffix() {
|
||||
let obj = BaseDestination()
|
||||
var str = ""
|
||||
|
||||
str = obj.fileNameWithoutSuffix("")
|
||||
XCTAssertEqual(str, "")
|
||||
str = obj.fileNameWithoutSuffix("/")
|
||||
XCTAssertEqual(str, "")
|
||||
str = obj.fileNameWithoutSuffix("foo")
|
||||
XCTAssertEqual(str, "foo")
|
||||
str = obj.fileNameWithoutSuffix("foo.bar")
|
||||
XCTAssertEqual(str, "foo")
|
||||
str = obj.fileNameWithoutSuffix("path/to/ViewController.swift")
|
||||
XCTAssertEqual(str, "ViewController")
|
||||
}
|
||||
|
||||
func testFormatDate() {
|
||||
// empty format
|
||||
var str = BaseDestination().formatDate("")
|
||||
XCTAssertEqual(str, "")
|
||||
// no time format
|
||||
str = BaseDestination().formatDate("--")
|
||||
XCTAssertGreaterThanOrEqual(str, "--")
|
||||
// HH:mm:ss
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "HH:mm:ss"
|
||||
let dateStr = formatter.string(from: Date())
|
||||
str = BaseDestination().formatDate(formatter.dateFormat)
|
||||
XCTAssertEqual(str, dateStr)
|
||||
// test UTC
|
||||
let utcFormatter = DateFormatter()
|
||||
utcFormatter.timeZone = TimeZone(abbreviation: "UTC")
|
||||
utcFormatter.dateFormat = "HH:mm:ss"
|
||||
let utcDateStr = utcFormatter.string(from: Date())
|
||||
str = BaseDestination().formatDate(utcFormatter.dateFormat, timeZone: "UTC")
|
||||
XCTAssertEqual(str, utcDateStr)
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// MARK: Filters
|
||||
////////////////////////////////
|
||||
|
||||
func test_init_noMinLevelSet() {
|
||||
let destination = BaseDestination()
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.verbose, path: "", function: ""))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.debug, path: "", function: ""))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.info, path: "", function: ""))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.warning, path: "", function: ""))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.error, path: "", function: ""))
|
||||
}
|
||||
|
||||
func test_init_minLevelSet() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.info
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(SwiftyBeaver.Level.verbose, path: "", function: ""))
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(SwiftyBeaver.Level.debug, path: "", function: ""))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.info, path: "", function: ""))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.warning, path: "", function: ""))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.error, path: "", function: ""))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasMinLevel_True() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.verbose
|
||||
destination.addFilter(Filters.Path.equals("/world/beaver.swift", caseSensitive: true, required: true))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.warning,
|
||||
path: "/world/beaver.swift", function: "initialize"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasMinLevel_False() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.info
|
||||
destination.addFilter(Filters.Path.equals("/world/beaver.swift", caseSensitive: true, required: true))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.warning,
|
||||
path: "/world/beaver.swift", function: "initialize"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasMinLevelAndMatchingLevelAndEqualPath_True() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.verbose
|
||||
let filter = Filters.Path.equals("/world/beaver.swift", caseSensitive: true, required: true, minLevel: .debug)
|
||||
destination.addFilter(filter)
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/world/beaver.swift", function: "initialize"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasMinLevelAndNoMatchingLevelButEqualPath_False() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.info
|
||||
let filter = Filters.Path.equals("/world/beaver.swift", caseSensitive: true, required: true, minLevel: .debug)
|
||||
destination.addFilter(filter)
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/world/beaver.swift", function: "initialize"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasMinLevelAndOneEqualsPathFilterAndDoesNotPass_False() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.info
|
||||
destination.addFilter(Filters.Path.equals("/world/beaver.swift", caseSensitive: true, required: true))
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/hello/foo.swift", function: "initialize"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasMinLevelAndOneRequiredMessageFilterAndDoesNotPass_False() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = .error
|
||||
destination.addFilter(Filters.Message.contains("Required", caseSensitive: false, required: true, minLevel: .info))
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(.info, path: "/hello/foo.swift", function: "initialize", message: "Test"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasMinLevelAndOneRequiredMessageFilterAndDoesPass_True() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.error
|
||||
destination.addFilter(Filters.Message.contains("Required", caseSensitive: false, required: true, minLevel: .info))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(.info, path: "/hello/foo.swift", function: "initialize", message: "Required Test"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasLevelFilterAndTwoRequiredPathFiltersAndPasses_True() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.info
|
||||
destination.addFilter(Filters.Path.startsWith("/world", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Path.endsWith("beaver.swift", caseSensitive: true, required: true))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.warning,
|
||||
path: "/world/beaver.swift", function: "initialize"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasLevelFilterAndTwoRequiredPathFiltersAndDoesNotPass_False() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.info
|
||||
destination.addFilter(Filters.Path.startsWith("/world", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Path.endsWith("foo.swift", caseSensitive: true, required: true))
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/hello/foo.swift", function: "initialize"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasLevelFilterARequiredPathFilterAndTwoRequiredMessageFiltersAndPasses_True() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.info
|
||||
destination.addFilter(Filters.Path.startsWith("/world", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Message.startsWith("SQL:", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Message.contains("insert", caseSensitive: false, required: true))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.warning,
|
||||
path: "/world/beaver.swift", function: "executeSQLStatement",
|
||||
message: "SQL: INSERT INTO table (c1, c2) VALUES (1, 2)"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasLevelFilterARequiredPathFilterAndTwoRequiredMessageFiltersAndDoesNotPass_False() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.info
|
||||
destination.addFilter(Filters.Path.startsWith("/world", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Message.startsWith("SQL:", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Message.contains("insert", caseSensitive: false, required: true))
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/world/beaver.swift",
|
||||
function: "executeSQLStatement",
|
||||
message: "SQL: DELETE FROM table WHERE c1 = 1"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasLevelFilterCombinationOfAllOtherFiltersAndPasses_True() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.info
|
||||
destination.addFilter(Filters.Path.startsWith("/world", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Path.endsWith("/beaver.swift", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Function.equals("executeSQLStatement", required: true))
|
||||
destination.addFilter(Filters.Message.startsWith("SQL:", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Message.contains("insert", "update", "delete", required: true))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.warning,
|
||||
path: "/world/beaver.swift",
|
||||
function: "executeSQLStatement",
|
||||
message: "SQL: INSERT INTO table (c1, c2) VALUES (1, 2)"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasLevelFilterCombinationOfAllOtherFiltersAndDoesNotPass_False() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.info
|
||||
destination.addFilter(Filters.Path.startsWith("/world", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Path.endsWith("/beaver.swift", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Function.equals("executeSQLStatement", required: true))
|
||||
destination.addFilter(Filters.Message.startsWith("SQL:", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Message.contains("insert", "update", "delete", required: true))
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/world/beaver.swift",
|
||||
function: "executeSQLStatement",
|
||||
message: "SQL: CREATE TABLE sample (c1 INTEGER, c2 VARCHAR)"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasLevelFilterCombinationOfOtherFiltersIncludingNonRequiredAndPasses_True() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.info
|
||||
destination.addFilter(Filters.Path.startsWith("/world", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Path.endsWith("/beaver.swift", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Function.equals("executeSQLStatement", required: true))
|
||||
destination.addFilter(Filters.Message.startsWith("SQL:", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Message.contains("insert"))
|
||||
destination.addFilter(Filters.Message.contains("update"))
|
||||
destination.addFilter(Filters.Message.contains("delete"))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.warning,
|
||||
path: "/world/beaver.swift",
|
||||
function: "executeSQLStatement",
|
||||
message: "SQL: INSERT INTO table (c1, c2) VALUES (1, 2)"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasLevelFilterCombinationOfOtherFiltersIncludingNonRequired_True() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.info
|
||||
destination.addFilter(Filters.Path.startsWith("/world", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Path.endsWith("/beaver.swift", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Function.equals("executeSQLStatement", required: true))
|
||||
destination.addFilter(Filters.Message.startsWith("SQL:", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Message.contains("insert", caseSensitive: true))
|
||||
destination.addFilter(Filters.Message.contains("update"))
|
||||
destination.addFilter(Filters.Message.contains("delete"))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(SwiftyBeaver.Level.warning,
|
||||
path: "/world/beaver.swift",
|
||||
function: "executeSQLStatement",
|
||||
message: "SQL: INSERT INTO table (c1, c2) VALUES (1, 2)"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasLevelFilterCombinationOfOtherFiltersIncludingNonRequired_False() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = SwiftyBeaver.Level.info
|
||||
destination.addFilter(Filters.Path.startsWith("/world", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Path.endsWith("/beaver.swift", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Function.equals("executeSQLStatement", required: true))
|
||||
destination.addFilter(Filters.Message.startsWith("SQL:", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Message.contains("rename", caseSensitive: true, required: true))
|
||||
destination.addFilter(Filters.Message.contains("update"))
|
||||
destination.addFilter(Filters.Message.contains("delete"))
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/world/beaver.swift",
|
||||
function: "executeSQLStatement",
|
||||
message: "SQL: INSERT INTO table (c1, c2) VALUES (1, 2)"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasMatchingNonRequiredFilter_True() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = .info
|
||||
destination.addFilter(Filters.Path.contains("/ViewController"))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/world/ViewController.swift",
|
||||
function: "myFunc",
|
||||
message: "Hello World"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasNoMatchingNonRequiredFilter_False() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = .info
|
||||
destination.addFilter(Filters.Path.contains("/ViewController"))
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/world/beaver.swift",
|
||||
function: "myFunc",
|
||||
message: "Hello World"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasNoMatchingNonRequiredFilterAndMinLevel_True() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = .debug
|
||||
destination.addFilter(Filters.Path.contains("/ViewController", minLevel: .info))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/world/beaver.swift",
|
||||
function: "myFunc",
|
||||
message: "Hello World"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasNoMatchingNonRequiredFilterAndMinLevel_False() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = .verbose
|
||||
destination.addFilter(Filters.Path.contains("/ViewController", minLevel: .debug))
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(.verbose,
|
||||
path: "/world/ViewController.swift",
|
||||
function: "myFunc",
|
||||
message: "Hello World"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasMultipleNonMatchingNonRequiredFilterAndMinLevel_True() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = .debug
|
||||
destination.addFilter(Filters.Path.contains("/ViewController", minLevel: .info))
|
||||
destination.addFilter(Filters.Path.contains("/test", minLevel: .debug))
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/world/beaver.swift",
|
||||
function: "myFunc",
|
||||
message: "Hello World"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_hasMultipleNonMatchingNonRequiredFilterAndMinLevel_False() {
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = .verbose
|
||||
destination.addFilter(Filters.Path.contains("/ViewController", minLevel: .debug))
|
||||
destination.addFilter(Filters.Path.contains("/test", minLevel: .verbose))
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(.verbose,
|
||||
path: "/world/ViewController.swift",
|
||||
function: "myFunc",
|
||||
message: "Hello World"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_noFilters_True() {
|
||||
// everything is logged on default
|
||||
let destination = BaseDestination()
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/world/ViewController.swift",
|
||||
function: "myFunc",
|
||||
message: "Hello World"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_multipleNonRequiredFiltersAndGlobal_True() {
|
||||
// everything is logged on default
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = .info
|
||||
|
||||
destination.addFilter(Filters.Path.contains("/ViewController", minLevel: .debug))
|
||||
destination.addFilter(Filters.Function.contains("Func", minLevel: .debug))
|
||||
destination.addFilter(Filters.Message.contains("World", minLevel: .debug))
|
||||
//destination.debugPrint = true
|
||||
|
||||
// covered by filters
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/world/ViewController.swift",
|
||||
function: "myFunc",
|
||||
message: "Hello World"))
|
||||
|
||||
// not in filter and below global minLevel
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(.debug,
|
||||
path: "hello.swift",
|
||||
function: "foo",
|
||||
message: "bar"))
|
||||
}
|
||||
|
||||
func test_shouldLevelBeLogged_excludeFilter_True() {
|
||||
// everything is logged on default
|
||||
let destination = BaseDestination()
|
||||
destination.minLevel = .error
|
||||
|
||||
destination.addFilter(Filters.Path.contains("/ViewController", minLevel: .debug))
|
||||
destination.addFilter(Filters.Function.excludes("myFunc", minLevel: .debug))
|
||||
//destination.debugPrint = true
|
||||
|
||||
// excluded
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/world/ViewController.swift",
|
||||
function: "myFunc",
|
||||
message: "Hello World"))
|
||||
|
||||
// excluded
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(.error,
|
||||
path: "/world/ViewController.swift",
|
||||
function: "myFunc",
|
||||
message: "Hello World"))
|
||||
|
||||
// not excluded, but below minLevel
|
||||
XCTAssertFalse(destination.shouldLevelBeLogged(.debug,
|
||||
path: "/world/OtherViewController.swift",
|
||||
function: "otherFunc",
|
||||
message: "Hello World"))
|
||||
|
||||
// not excluded, above minLevel, no matching filter
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(.error,
|
||||
path: "/world/OtherViewController.swift",
|
||||
function: "otherFunc",
|
||||
message: "Hello World"))
|
||||
|
||||
// not excluded, above minLevel, matching path filter
|
||||
XCTAssertTrue(destination.shouldLevelBeLogged(.error,
|
||||
path: "/ViewController.swift",
|
||||
function: "otherFunc",
|
||||
message: "Hello World"))
|
||||
|
||||
}
|
||||
|
||||
/// turns dict into JSON-encoded string
|
||||
func jsonStringFromDict(_ dict: [String: Any]) -> String? {
|
||||
var jsonString: String?
|
||||
|
||||
// try to create JSON string
|
||||
do {
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
|
||||
jsonString = String(data: jsonData, encoding: .utf8)
|
||||
} catch {
|
||||
print("SwiftyBeaver could not create JSON from dict.")
|
||||
}
|
||||
return jsonString
|
||||
}
|
||||
|
||||
// MARK: Linux allTests
|
||||
|
||||
static let allTests = [
|
||||
("testFormatMessage", testFormatMessage),
|
||||
("testLevelWord", testLevelWord),
|
||||
("testColorForLevel", testColorForLevel),
|
||||
("testFileNameOfFile", testFileNameOfFile),
|
||||
("testFileNameOfFileWithoutSuffix", testFileNameOfFileWithoutSuffix),
|
||||
("testFormatDate", testFormatDate),
|
||||
("test_init_noMinLevelSet", test_init_noMinLevelSet),
|
||||
("test_init_minLevelSet", test_init_minLevelSet),
|
||||
("test_shouldLevelBeLogged_hasMinLevel_True",
|
||||
test_shouldLevelBeLogged_hasMinLevel_True),
|
||||
("test_shouldLevelBeLogged_hasMinLevel_False",
|
||||
test_shouldLevelBeLogged_hasMinLevel_False),
|
||||
("test_shouldLevelBeLogged_hasMinLevelAndMatchingLevelAndEqualPath_True",
|
||||
test_shouldLevelBeLogged_hasMinLevelAndMatchingLevelAndEqualPath_True),
|
||||
("test_shouldLevelBeLogged_hasMinLevelAndNoMatchingLevelButEqualPath_False",
|
||||
test_shouldLevelBeLogged_hasMinLevelAndNoMatchingLevelButEqualPath_False),
|
||||
("test_shouldLevelBeLogged_hasMinLevelAndOneEqualsPathFilterAndDoesNotPass_False",
|
||||
test_shouldLevelBeLogged_hasMinLevelAndOneEqualsPathFilterAndDoesNotPass_False),
|
||||
("test_shouldLevelBeLogged_hasLevelFilterAndTwoRequiredPathFiltersAndPasses_True",
|
||||
test_shouldLevelBeLogged_hasLevelFilterAndTwoRequiredPathFiltersAndPasses_True),
|
||||
("test_shouldLevelBeLogged_hasLevelFilterAndTwoRequiredPathFiltersAndDoesNotPass_False",
|
||||
test_shouldLevelBeLogged_hasLevelFilterAndTwoRequiredPathFiltersAndDoesNotPass_False),
|
||||
("test_shouldLevelBeLogged_hasLevelFilterARequiredPathFilterAndTwoRequiredMessageFiltersAndPasses_True",
|
||||
test_shouldLevelBeLogged_hasLevelFilterARequiredPathFilterAndTwoRequiredMessageFiltersAndPasses_True),
|
||||
("test_shouldLevelBeLogged_hasLevelFilterARequiredPathFilterAndTwoRequiredMessageFiltersAndDoesNotPass_False",
|
||||
test_shouldLevelBeLogged_hasLevelFilterARequiredPathFilterAndTwoRequiredMessageFiltersAndDoesNotPass_False),
|
||||
("test_shouldLevelBeLogged_hasLevelFilterCombinationOfAllOtherFiltersAndPasses_True",
|
||||
test_shouldLevelBeLogged_hasLevelFilterCombinationOfAllOtherFiltersAndPasses_True),
|
||||
("test_shouldLevelBeLogged_hasLevelFilterCombinationOfAllOtherFiltersAndDoesNotPass_False",
|
||||
test_shouldLevelBeLogged_hasLevelFilterCombinationOfAllOtherFiltersAndDoesNotPass_False),
|
||||
("test_shouldLevelBeLogged_hasLevelFilterCombinationOfOtherFiltersIncludingNonRequiredAndPasses_True",
|
||||
test_shouldLevelBeLogged_hasLevelFilterCombinationOfOtherFiltersIncludingNonRequiredAndPasses_True),
|
||||
("test_shouldLevelBeLogged_hasLevelFilterCombinationOfOtherFiltersIncludingNonRequired_True",
|
||||
test_shouldLevelBeLogged_hasLevelFilterCombinationOfOtherFiltersIncludingNonRequired_True),
|
||||
("test_shouldLevelBeLogged_hasLevelFilterCombinationOfOtherFiltersIncludingNonRequired_False",
|
||||
test_shouldLevelBeLogged_hasLevelFilterCombinationOfOtherFiltersIncludingNonRequired_False),
|
||||
("test_shouldLevelBeLogged_hasMatchingNonRequiredFilter_True",
|
||||
test_shouldLevelBeLogged_hasMatchingNonRequiredFilter_True),
|
||||
("test_shouldLevelBeLogged_hasNoMatchingNonRequiredFilter_False",
|
||||
test_shouldLevelBeLogged_hasNoMatchingNonRequiredFilter_False),
|
||||
("test_shouldLevelBeLogged_hasNoMatchingNonRequiredFilterAndMinLevel_False",
|
||||
test_shouldLevelBeLogged_hasNoMatchingNonRequiredFilterAndMinLevel_False),
|
||||
("test_shouldLevelBeLogged_noFilters_True", test_shouldLevelBeLogged_noFilters_True),
|
||||
("test_shouldLevelBeLogged_multipleNonRequiredFiltersAndGlobal_True",
|
||||
test_shouldLevelBeLogged_multipleNonRequiredFiltersAndGlobal_True),
|
||||
("test_shouldLevelBeLogged_excludeFilter_True", test_shouldLevelBeLogged_excludeFilter_True)
|
||||
]
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// ConsoleDestinationTests.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Sebastian Kreutzberger on 3/28/17.
|
||||
// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
|
||||
//
|
||||
// run tests for this class only:
|
||||
// swift test -s SwiftyBeaverTests.ConsoleDestinationTests
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
@testable import SwiftyBeaver
|
||||
|
||||
class ConsoleDestinationTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
SwiftyBeaver.removeAllDestinations()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testUseTerminalColors() {
|
||||
let log = SwiftyBeaver.self
|
||||
let console = ConsoleDestination()
|
||||
XCTAssertTrue(log.addDestination(console))
|
||||
|
||||
// default xcode colors
|
||||
XCTAssertFalse(console.useTerminalColors)
|
||||
XCTAssertEqual(console.levelColor.verbose, "💜 ")
|
||||
XCTAssertEqual(console.reset, "")
|
||||
XCTAssertEqual(console.escape, "")
|
||||
|
||||
// switch to terminal colors
|
||||
console.useTerminalColors = true
|
||||
XCTAssertTrue(console.useTerminalColors)
|
||||
XCTAssertEqual(console.levelColor.verbose, "251m" )
|
||||
XCTAssertEqual(console.reset, "\u{001b}[0m")
|
||||
XCTAssertEqual(console.escape, "\u{001b}[38;5;")
|
||||
}
|
||||
|
||||
// MARK: Linux allTests
|
||||
|
||||
static let allTests = [
|
||||
("testUseTerminalColors", testUseTerminalColors)
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// DestinationSetTests.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Mark Schultz on 5/5/16.
|
||||
// Copyright © 2016 Sebastian Kreutzberger. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import SwiftyBeaver
|
||||
|
||||
class DestinationSetTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
SwiftyBeaver.removeAllDestinations()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
SwiftyBeaver.removeAllDestinations()
|
||||
}
|
||||
|
||||
func testChangeDestinationsMinLogLevels() {
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
// Test for default state
|
||||
XCTAssertEqual(log.countDestinations(), 0)
|
||||
|
||||
// add valid destinations
|
||||
let console = ConsoleDestination()
|
||||
let console2 = ConsoleDestination()
|
||||
let file = FileDestination()
|
||||
|
||||
XCTAssertTrue(log.addDestination(console))
|
||||
XCTAssertTrue(log.addDestination(console2))
|
||||
XCTAssertTrue(log.addDestination(file))
|
||||
|
||||
// Test that destinations are successfully added
|
||||
XCTAssertEqual(log.countDestinations(), 3)
|
||||
|
||||
// Test default log level of destinations
|
||||
log.destinations.forEach {
|
||||
XCTAssertEqual($0.minLevel, SwiftyBeaver.Level.verbose)
|
||||
}
|
||||
|
||||
// Change min log level for all destinations
|
||||
log.destinations.forEach { $0.minLevel = .info }
|
||||
|
||||
// Test min level of destinations has changed
|
||||
log.destinations.forEach {
|
||||
XCTAssertEqual($0.minLevel, SwiftyBeaver.Level.info)
|
||||
}
|
||||
}
|
||||
|
||||
func testRemoveConsoleDestinations() {
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
// Test for default state
|
||||
XCTAssertEqual(log.countDestinations(), 0)
|
||||
|
||||
// add valid destinations
|
||||
let console = ConsoleDestination()
|
||||
let console2 = ConsoleDestination()
|
||||
let file = FileDestination()
|
||||
|
||||
XCTAssertTrue(log.addDestination(console))
|
||||
XCTAssertTrue(log.addDestination(console2))
|
||||
XCTAssertTrue(log.addDestination(file))
|
||||
|
||||
// Test that destinations are successfully added
|
||||
XCTAssertEqual(log.countDestinations(), 3)
|
||||
|
||||
// Remove console destinations
|
||||
log.destinations.forEach {
|
||||
if let consoleDestination = $0 as? ConsoleDestination {
|
||||
XCTAssertTrue(log.removeDestination(consoleDestination))
|
||||
}
|
||||
}
|
||||
|
||||
// Test that console destinations are removed
|
||||
XCTAssertEqual(log.countDestinations(), 1)
|
||||
}
|
||||
|
||||
// MARK: Linux allTests
|
||||
|
||||
static let allTests = [
|
||||
("testChangeDestinationsMinLogLevels", testChangeDestinationsMinLogLevels),
|
||||
("testRemoveConsoleDestinations", testRemoveConsoleDestinations)
|
||||
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
//
|
||||
// FileDestinationTests.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Sebastian Kreutzberger on 6/21/16.
|
||||
// Copyright © 2016 Sebastian Kreutzberger. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
@testable import SwiftyBeaver
|
||||
|
||||
class FileDestinationTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
SwiftyBeaver.removeAllDestinations()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testFileIsWritten() {
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
let path = "/tmp/testSBF.log"
|
||||
deleteFile(path: path)
|
||||
|
||||
// add file
|
||||
let file = FileDestination()
|
||||
file.logFileURL = URL(string: "file://" + path)!
|
||||
file.format = "$L: $M $X"
|
||||
_ = log.addDestination(file)
|
||||
|
||||
log.verbose("first line to log")
|
||||
log.debug("second line to log")
|
||||
log.info("third line to log")
|
||||
log.warning("fourth line with context", context: 123)
|
||||
_ = log.flush(secondTimeout: 3)
|
||||
|
||||
// wait a bit until the logs are written to file
|
||||
for i in 1...100000 {
|
||||
let x = sqrt(Double(i))
|
||||
XCTAssertEqual(x, sqrt(Double(i)))
|
||||
}
|
||||
|
||||
// was the file written and does it contain the lines?
|
||||
let fileLines = self.linesOfFile(path: path)
|
||||
XCTAssertNotNil(fileLines)
|
||||
guard let lines = fileLines else { return }
|
||||
XCTAssertEqual(lines.count, 5)
|
||||
XCTAssertEqual(lines[0], "VERBOSE: first line to log")
|
||||
XCTAssertEqual(lines[1], "DEBUG: second line to log")
|
||||
XCTAssertEqual(lines[2], "INFO: third line to log")
|
||||
XCTAssertEqual(lines[3], "WARNING: fourth line with context 123")
|
||||
XCTAssertEqual(lines[4], "")
|
||||
}
|
||||
|
||||
func testFileIsWrittenToFolderWithSpaces() {
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
let folder = "/tmp/folder with spaces"
|
||||
createFolder(path: folder)
|
||||
|
||||
let path = folder + "/testSBF.log"
|
||||
deleteFile(path: path)
|
||||
|
||||
// in conversion from path String to URL you need to replace " " with "%20"
|
||||
let pathReadyForURL = path.replacingOccurrences(of: " ", with: "%20")
|
||||
let fileURL = URL(string: "file://" + pathReadyForURL)
|
||||
XCTAssertNotNil(fileURL)
|
||||
guard let url = fileURL else { return }
|
||||
|
||||
// add file
|
||||
let file = FileDestination()
|
||||
file.logFileURL = url
|
||||
file.format = "$L: $M"
|
||||
_ = log.addDestination(file)
|
||||
|
||||
log.verbose("first line to log")
|
||||
log.debug("second line to log")
|
||||
log.info("third line to log")
|
||||
_ = log.flush(secondTimeout: 3)
|
||||
|
||||
// wait a bit until the logs are written to file
|
||||
for i in 1...100000 {
|
||||
let x = sqrt(Double(i))
|
||||
XCTAssertEqual(x, sqrt(Double(i)))
|
||||
}
|
||||
|
||||
// was the file written and does it contain the lines?
|
||||
let fileLines = self.linesOfFile(path: path)
|
||||
XCTAssertNotNil(fileLines)
|
||||
guard let lines = fileLines else { return }
|
||||
XCTAssertEqual(lines.count, 4)
|
||||
XCTAssertEqual(lines[0], "VERBOSE: first line to log")
|
||||
XCTAssertEqual(lines[1], "DEBUG: second line to log")
|
||||
XCTAssertEqual(lines[2], "INFO: third line to log")
|
||||
XCTAssertEqual(lines[3], "")
|
||||
}
|
||||
|
||||
func testFileIsWrittenToDeletedFolder() {
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
let path = "/tmp/\(UUID().uuidString)/testSBF.log"
|
||||
deleteFile(path: path)
|
||||
deleteFile(path: "/tmp/\(UUID().uuidString)/testSBF.log.1")
|
||||
|
||||
|
||||
// add file
|
||||
let file = FileDestination()
|
||||
file.logFileURL = URL(string: "file://" + path)!
|
||||
file.format = "$L: $M $X"
|
||||
// active logfile rotation
|
||||
file.logFileAmount = 2
|
||||
file.logFileMaxSize = 1000
|
||||
_ = log.addDestination(file)
|
||||
|
||||
log.verbose("first line to log")
|
||||
log.debug("second line to log")
|
||||
log.info("third line to log")
|
||||
log.warning("fourth line with context", context: 123)
|
||||
_ = log.flush(secondTimeout: 3)
|
||||
|
||||
// wait a bit until the logs are written to file
|
||||
for i in 1...100000 {
|
||||
let x = sqrt(Double(i))
|
||||
XCTAssertEqual(x, sqrt(Double(i)))
|
||||
}
|
||||
|
||||
// was the file written and does it contain the lines?
|
||||
let fileLines = self.linesOfFile(path: path)
|
||||
XCTAssertNotNil(fileLines)
|
||||
guard let lines = fileLines else { return }
|
||||
XCTAssertEqual(lines.count, 5)
|
||||
XCTAssertEqual(lines[0], "VERBOSE: first line to log") // is in first rotation file
|
||||
XCTAssertEqual(lines[1], "DEBUG: second line to log")
|
||||
XCTAssertEqual(lines[2], "INFO: third line to log")
|
||||
XCTAssertEqual(lines[3], "WARNING: fourth line with context 123")
|
||||
XCTAssertEqual(lines[4], "")
|
||||
}
|
||||
|
||||
// MARK: Helper Functions
|
||||
|
||||
// deletes a file if it is existing
|
||||
func deleteFile(path: String) {
|
||||
do {
|
||||
try FileManager.default.removeItem(atPath: path)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// returns the lines of a file as optional array which is nil on error
|
||||
func linesOfFile(path: String) -> [String]? {
|
||||
do {
|
||||
// try to read file
|
||||
let fileContent = try NSString(contentsOfFile: path, encoding: String.Encoding.utf8.rawValue)
|
||||
return fileContent.components(separatedBy: "\n")
|
||||
} catch let error {
|
||||
print(error)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// creates a folder if not already existing
|
||||
func createFolder(path: String) {
|
||||
do {
|
||||
try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
|
||||
} catch {
|
||||
print("Unable to create directory")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Linux allTests
|
||||
|
||||
static let allTests = [
|
||||
("testFileIsWritten", testFileIsWritten),
|
||||
("testFileIsWrittenToFolderWithSpaces", testFileIsWrittenToFolderWithSpaces)
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,944 @@
|
||||
//
|
||||
// Created by Jeff Roberts on 6/1/16.
|
||||
// Copyright (c) 2016 Sebastian Kreutzberger. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
@testable import SwiftyBeaver
|
||||
|
||||
class FilterTests: XCTestCase {
|
||||
|
||||
//
|
||||
// Path filtering tests (identity)
|
||||
//
|
||||
func test_path_getTarget_isPathFilter() {
|
||||
let filter = Filters.Path.startsWith("/some/path")
|
||||
let isCorrectTargetType: Bool
|
||||
switch filter.getTarget() {
|
||||
case .Path:
|
||||
isCorrectTargetType = true
|
||||
|
||||
default:
|
||||
isCorrectTargetType = false
|
||||
}
|
||||
XCTAssertTrue(isCorrectTargetType)
|
||||
}
|
||||
|
||||
//
|
||||
// Path filtering tests (isRequired)
|
||||
//
|
||||
func test_path_startsWithAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Path.startsWith("/some/path", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_path_containsAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Path.contains("/some/path", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_path_excludesAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Path.excludes("/some/path", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_path_endsWithAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Path.endsWith("/some/path", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_path_equalsAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Path.equals("/some/path", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_path_startsWithAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Path.startsWith("/some/path", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_path_containsAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Path.contains("/some/path", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_path_excludesAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Path.excludes("/some/path", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_path_endsWithAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Path.endsWith("/some/path", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_path_equalsAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Path.equals("/some/path", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
//
|
||||
// Path filtering tests (case sensitivity)
|
||||
//
|
||||
func test_path_startsWithAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Path.startsWith("/some/path", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_path_containsAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Path.contains("/some/path", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_path_excludesAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Path.excludes("/some/path", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_path_endsWithAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Path.endsWith("/some/path", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_path_equalsAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Path.equals("/some/path", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_path_startsWithAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Path.startsWith("/some/path", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_path_containsAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Path.contains("/some/path", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_path_excludesAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Path.excludes("/some/path", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_path_endsWithAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Path.endsWith("/some/path", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_path_equalsAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Path.equals("/some/path", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
//
|
||||
// Path filtering tests (comparison testing)
|
||||
//
|
||||
func test_pathStartsWith_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.startsWith("/first", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathStartsWith_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.startsWith("/First", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathStartsWith_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Path.startsWith("/First", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathStartsWith_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.startsWith("/first", "/second", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("/second/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathStartsWith_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.startsWith("/First", "/Second", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("/second/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathStartsWith_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Path.startsWith("/First", "/Second", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("/second/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathContains_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.contains("/path", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathContains_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.contains("/Path", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathContains_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Path.contains("/Path", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathContains_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.contains("/pathway", "/path", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathContains_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.contains("/Pathway", "/Path", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathContains_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Path.contains("/Pathway", "/Path", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathExcludes_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.excludes("/path", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("/first/epath/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathExcludes_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.excludes("/Path", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("/first/epath/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathExcludes_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Path.excludes("/Path", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathExcludes_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.excludes("/pathway", "/path", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathExcludes_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.excludes("/Pathway", "/Path", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathExcludes_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Path.excludes("/Pathway", "/Path", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathEndsWith_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.endsWith("/anywhere", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathEndsWith_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.endsWith("/Anywhere", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathEndsWith_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Path.endsWith("/Anywhere", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathEndsWith_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.endsWith("/nowhere", "/anywhere", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathEndsWith_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.endsWith("/Nowhere", "/Anywhere", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathEndsWith_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Path.endsWith("/Nowhere", "/Anywhere", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathEquals_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.equals("/first/path/to/anywhere", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathEquals_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.equals("/First/path/to/Anywhere", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathEquals_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Path.equals("/First/path/to/Anywhere", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathEquals_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.equals("/second/path/to/anywhere", "/first/path/to/anywhere", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathEquals_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Path.equals("/Second/path/to/nowhere", "/First/Path/To/Anywhere", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathEquals_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Path.equals("/Second/path/to/anywhere", "/First/path/to/Anywhere", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("/first/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathCustomSimple_answersTrue() {
|
||||
let filter = Filters.Path.custom { string in
|
||||
return string == "/Second/path/to/anywhere"
|
||||
}
|
||||
XCTAssertTrue(filter.apply("/Second/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathCustomComplexMatches_answersFalse() {
|
||||
let filter = Filters.Path.custom { string in
|
||||
let disallowedValues = ["/Second/path/to/anywhere"]
|
||||
let allowedValues = ["/First/path/to/anywhere"]
|
||||
return !disallowedValues.contains(string) && allowedValues.contains(string)
|
||||
}
|
||||
XCTAssertFalse(filter.apply("/Second/path/to/anywhere"))
|
||||
}
|
||||
|
||||
func test_pathCustomComplexMatches_answersTrue() {
|
||||
let filter = Filters.Path.custom { string in
|
||||
let disallowedValues = ["/Second/path/to/anywhere"]
|
||||
let allowedValues = ["/First/path/to/anywhere"]
|
||||
return !disallowedValues.contains(string) && allowedValues.contains(string)
|
||||
}
|
||||
XCTAssertTrue(filter.apply("/First/path/to/anywhere"))
|
||||
}
|
||||
|
||||
//
|
||||
// Function filtering tests (identity)
|
||||
//
|
||||
func test_function_getTarget_isFunctionFilter() {
|
||||
let filter = Filters.Function.startsWith("myFunc")
|
||||
let isCorrectTargetType: Bool
|
||||
switch filter.getTarget() {
|
||||
case .Function:
|
||||
isCorrectTargetType = true
|
||||
|
||||
default:
|
||||
isCorrectTargetType = false
|
||||
}
|
||||
XCTAssertTrue(isCorrectTargetType)
|
||||
}
|
||||
|
||||
//
|
||||
// Function filtering tests (isRequired)
|
||||
//
|
||||
func test_function_startsWithAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Function.startsWith("myFunc", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_function_containsAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Function.contains("myFunc", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_function_excludesAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Function.excludes("myFunc", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_function_endsWithAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Function.endsWith("myFunc", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_function_equalsAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Function.equals("myFunc", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_function_startsWithAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Function.startsWith("myFunc", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_function_containsAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Function.contains("myFunc", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_function_excludesAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Function.excludes("myFunc", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_function_endsWithAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Function.endsWith("myFunc", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_function_equalsAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Function.equals("myFunc", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
//
|
||||
// Function filtering tests (case sensitivity)
|
||||
//
|
||||
func test_function_startsWithAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Function.startsWith("myFunc", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_function_containsAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Function.contains("myFunc", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_function_excludesAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Function.excludes("myFunc", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_function_endsWithAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Function.endsWith("myFunc", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_function_startsWithAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Function.startsWith("myFunc", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_function_containsAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Function.contains("myFunc", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_function_excludesAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Function.excludes("myFunc", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_function_endsWithAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Function.endsWith("myFunc", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
//
|
||||
// Function filtering tests (comparison testing)
|
||||
//
|
||||
func test_functionStartsWith_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.startsWith("myFunc", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("myFunction"))
|
||||
}
|
||||
|
||||
func test_functionStartsWith_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.startsWith("MyFunc", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionStartsWith_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Function.startsWith("MyFunc", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionStartsWith_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.startsWith("yourFunc", "myFunc", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionStartsWith_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.startsWith("YourFunc", "MyFunc", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionStartsWith_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Function.startsWith("YourFunc", "MyFunc", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionContains_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.contains("Func", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionContains_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.contains("Func", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionContains_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Function.contains("Func", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionContains_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.contains("doSomething", "Func", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionContains_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.contains("DoSomething", "func", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionContains_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Function.contains("DoSomething", "Func", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionExcludes_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.excludes("Func", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionExcludes_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.excludes("Func", caseSensitive: false)
|
||||
XCTAssertFalse(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionExcludes_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Function.excludes("Func", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionExcludes_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.excludes("doSomething", "Func", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionExcludes_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.excludes("DoSomething", "func", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionExcludes_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Function.excludes("DoSomething", "Func", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionEndsWith_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.endsWith("Func", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionEndsWith_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.endsWith("Func", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionEndsWith_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Function.endsWith("Func", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionEndsWith_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.endsWith("doSomething", "Func", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionEndsWith_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.endsWith("DoSomething", "Func", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionEndsWith_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Function.endsWith("DoSomething", "Func", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionEquals_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.equals("myFunc", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionEquals_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.equals("myFunc", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionEquals_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Function.equals("myFunc", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionEquals_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.equals("yourFunc", "myFunc", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionEquals_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Function.equals("yourFunc", "myFunc", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionEquals_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Function.equals("yourFunc", "myFunc", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionCustomSimple_answersTrue() {
|
||||
let filter = Filters.Function.custom { string in
|
||||
return string == "myfunc"
|
||||
}
|
||||
XCTAssertTrue(filter.apply("myfunc"))
|
||||
}
|
||||
|
||||
func test_functionCustomComplexMatches_answersTrue() {
|
||||
let filter = Filters.Function.custom { string in
|
||||
let disallowedValues = ["yourFunc", "yourOtherFunc"]
|
||||
let allowedValues = ["myFunc"]
|
||||
return !disallowedValues.contains(string) && allowedValues.contains(string)
|
||||
}
|
||||
XCTAssertTrue(filter.apply("myFunc"))
|
||||
}
|
||||
|
||||
func test_functionCustomComplexMatches_answersFalse() {
|
||||
let filter = Filters.Function.custom { string in
|
||||
let disallowedValues = ["yourFunc", "yourOtherFunc"]
|
||||
let allowedValues = ["myFunc"]
|
||||
return !disallowedValues.contains(string) && allowedValues.contains(string)
|
||||
}
|
||||
XCTAssertFalse(filter.apply("yourFunc"))
|
||||
}
|
||||
|
||||
//
|
||||
// Message filtering tests (identity)
|
||||
//
|
||||
func test_message_getTarget_isMessageFilter() {
|
||||
let filter = Filters.Message.startsWith("Hello there, SwiftyBeaver!")
|
||||
let isCorrectTargetType: Bool
|
||||
switch filter.getTarget() {
|
||||
case .Message:
|
||||
isCorrectTargetType = true
|
||||
|
||||
default:
|
||||
isCorrectTargetType = false
|
||||
}
|
||||
XCTAssertTrue(isCorrectTargetType)
|
||||
}
|
||||
|
||||
//
|
||||
// Message filtering tests (isRequired)
|
||||
//
|
||||
func test_message_startsWithAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Message.startsWith("Hello", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_message_containsAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Message.contains("there", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_message_excludesAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Message.excludes("there", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_message_endsWithAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Message.endsWith("SwifyBeaver!", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_message_equalsAndIsRequired_isRequiredFilter() {
|
||||
let filter = Filters.Message.equals("SwifyBeaver!", required: true)
|
||||
XCTAssertTrue(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_message_startsWithAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Message.startsWith("Hello", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_message_containsAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Message.contains("there", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_message_excludesAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Message.excludes("there", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_message_endsWithAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Message.endsWith("SwiftyBeaver!", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
func test_message_equalsAndIsNotRequired_isNotRequiredFilter() {
|
||||
let filter = Filters.Message.equals("SwiftyBeaver!", required: false)
|
||||
XCTAssertFalse(filter.isRequired())
|
||||
}
|
||||
|
||||
//
|
||||
// Message filtering tests (case sensitivity)
|
||||
//
|
||||
func test_message_startsWithAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Message.startsWith("Hello", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_message_containsAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Message.contains("there", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_message_excludesAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Message.excludes("there", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_message_endsWithAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Message.endsWith("SwiftyBeaver!", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_message_equalsAndIsCaseSensitive_isCaseSensitive() {
|
||||
let filter = Filters.Message.equals("SwiftyBeaver!", caseSensitive: true)
|
||||
XCTAssertTrue(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_message_startsWithAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Message.startsWith("Hello", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_message_containsAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Message.contains("there", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_message_excludesAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Message.excludes("there", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_message_endsWithAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Message.endsWith("SwiftyBeaver!", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
func test_message_equalsAndIsNotCaseSensitive_isNotCaseSensitive() {
|
||||
let filter = Filters.Message.equals("SwiftyBeaver!", caseSensitive: false)
|
||||
XCTAssertFalse(isCaseSensitive(filter.getTarget()))
|
||||
}
|
||||
|
||||
//
|
||||
// Function filtering tests (comparison testing)
|
||||
//
|
||||
func test_messageStartsWith_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.startsWith("Hello", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageStartsWith_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.startsWith("hello", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageStartsWith_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Message.startsWith("hello", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageStartsWith_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.startsWith("Goodbye", "Hello", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageStartsWith_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.startsWith("goodbye", "hello", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageStartsWith_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Message.startsWith("goodbye", "hello", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageContains_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.contains("there", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageContains_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.contains("There", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageContains_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Message.contains("There", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageContains_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.contains("their", "there", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageContains_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.contains("Their", "There", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageContains_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Message.contains("Their", "There", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageExcludes_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.excludes("there", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageExcludes_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.excludes("There", caseSensitive: false)
|
||||
XCTAssertFalse(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageExcludes_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Message.excludes("There", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageExcludes_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.excludes("their", "there", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageExcludes_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.excludes("Their", "There", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageExcludes_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Message.excludes("Their", "There", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageEndsWith_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.endsWith("SwiftyBeaver!", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageEndsWith_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.endsWith("swiftybeaver!", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageEndsWith_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Message.endsWith("swiftybeaver!", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageEndsWith_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.endsWith("SluggishMink!", "SwiftyBeaver!", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageEndsWith_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.endsWith("sluggishmink!", "swiftybeaver!", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageEndsWith_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Message.endsWith("sluggishmink!!", "swiftybeaver!", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageEquals_hasOneValueAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.equals("Hello there, SwiftyBeaver!", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageEquals_hasOneValueAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.equals("hello there, swiftybeaver!", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageEquals_hasOneValueAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Message.equals("hello there, swiftybeaver!", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageEquals_hasMultipleValuesAndIsCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.equals("Goodbye, SluggishMink!", "Hello there, SwiftyBeaver!", caseSensitive: true)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageEquals_hasMultipleValuesAndIsNotCaseSensitiveAndMatches_answersTrue() {
|
||||
let filter = Filters.Message.equals("goodbye, sluggishmink!", "hello there, swiftybeaver!", caseSensitive: false)
|
||||
XCTAssertTrue(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageEquals_hasMultipleValuesAndIsCaseSensitiveAndDoesNotMatch_answersFalse() {
|
||||
let filter = Filters.Message.equals("goodbye, sluggishmink!", "hello there, swiftybeaver!", caseSensitive: true)
|
||||
XCTAssertFalse(filter.apply("Hello there, SwiftyBeaver!"))
|
||||
}
|
||||
|
||||
func test_messageCustomSimple_answersTrue() {
|
||||
let filter = Filters.Message.custom { string in
|
||||
return string == "hello"
|
||||
}
|
||||
XCTAssertTrue(filter.apply("hello"))
|
||||
}
|
||||
|
||||
func test_messageCustomComplexMatches_answersTrue() {
|
||||
let filter = Filters.Message.custom { string in
|
||||
let disallowedValues = ["goodbye", "see you later"]
|
||||
let allowedValues = ["hello"]
|
||||
return !disallowedValues.contains(string) && allowedValues.contains(string)
|
||||
}
|
||||
XCTAssertTrue(filter.apply("hello"))
|
||||
}
|
||||
|
||||
func test_messageCustomComplexMatches_answersFalse() {
|
||||
let filter = Filters.Function.custom { string in
|
||||
let disallowedValues = ["goodbye", "see you later"]
|
||||
let allowedValues = ["hello"]
|
||||
return !disallowedValues.contains(string) && allowedValues.contains(string)
|
||||
}
|
||||
XCTAssertFalse(filter.apply("goodbye"))
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
private func isCaseSensitive(_ targetType: Filter.TargetType) -> Bool {
|
||||
let comparisonType: Filter.ComparisonType?
|
||||
switch targetType {
|
||||
case let .Path(type):
|
||||
comparisonType = type
|
||||
|
||||
case let .Function(type):
|
||||
comparisonType = type
|
||||
|
||||
case let .Message(type):
|
||||
comparisonType = type
|
||||
}
|
||||
|
||||
guard let compareType = comparisonType else {
|
||||
return false
|
||||
}
|
||||
|
||||
let isCaseSensitive: Bool
|
||||
|
||||
switch compareType {
|
||||
case let .Contains(_, caseSensitive):
|
||||
isCaseSensitive = caseSensitive
|
||||
|
||||
case let .Excludes(_, caseSensitive):
|
||||
isCaseSensitive = caseSensitive
|
||||
|
||||
case let .StartsWith(_, caseSensitive):
|
||||
isCaseSensitive = caseSensitive
|
||||
|
||||
case let .EndsWith(_, caseSensitive):
|
||||
isCaseSensitive = caseSensitive
|
||||
|
||||
case let .Equals(_, caseSensitive):
|
||||
isCaseSensitive = caseSensitive
|
||||
case .Custom:
|
||||
isCaseSensitive = false
|
||||
}
|
||||
|
||||
return isCaseSensitive
|
||||
}
|
||||
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// GoogleCloudDestinationTests.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Laurent Gaches on 10/04/2017.
|
||||
// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
|
||||
//
|
||||
import Foundation
|
||||
import XCTest
|
||||
@testable import SwiftyBeaver
|
||||
|
||||
class GoogleCloudDestinationTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
SwiftyBeaver.removeAllDestinations()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testUseGoogleCloudPDestination() {
|
||||
let log = SwiftyBeaver.self
|
||||
let gcpDestination = GoogleCloudDestination(serviceName: "TEST")
|
||||
gcpDestination.minLevel = .verbose
|
||||
XCTAssertTrue(log.addDestination(gcpDestination))
|
||||
}
|
||||
|
||||
func testSend() {
|
||||
// let dateStr = formatter.stringFromDate(NSDate())
|
||||
//let platform = SBPlatformDestination()
|
||||
let msg = "test message\nNewlineäößø"
|
||||
let thread = ""
|
||||
let file = "/file/path.swift"
|
||||
let function = "TestFunction()"
|
||||
let line = 123
|
||||
|
||||
let gcpDestination = GoogleCloudDestination(serviceName: "TEST")
|
||||
let str = gcpDestination.send(.verbose, msg: msg, thread: thread, file: file, function: function, line: line)
|
||||
XCTAssertNotNil(str)
|
||||
if let str = str {
|
||||
XCTAssertEqual(str.firstChar, "{")
|
||||
XCTAssertEqual(str.lastChar, "}")
|
||||
XCTAssertNotNil(str.range(of: "{\"service\":\"TEST\"}"))
|
||||
XCTAssertNotNil(str.range(of: "\"severity\":\"DEBUG\""))
|
||||
XCTAssertNotNil(str.range(of: "\"message\":\"test message\\nNewlineäößø\""))
|
||||
XCTAssertNotNil(str.range(of: "\"functionName\":\"TestFunction()\""))
|
||||
}
|
||||
}
|
||||
|
||||
func testContextMessage() {
|
||||
let msg = "test message\nNewlineäößø"
|
||||
let thread = ""
|
||||
let file = "/file/path.swift"
|
||||
let function = "TestFunction()"
|
||||
let line = 123
|
||||
|
||||
let gcd = GoogleCloudDestination(serviceName: "SwiftyBeaver")
|
||||
|
||||
let str = gcd.send(.verbose, msg: msg, thread: thread, file: file, function: function, line: line,
|
||||
context: ["user": "Beaver", "httpRequest": ["method": "GET", "responseStatusCode": 200]])
|
||||
|
||||
XCTAssertNotNil(str)
|
||||
if let str = str {
|
||||
XCTAssertEqual(str.firstChar, "{")
|
||||
XCTAssertEqual(str.lastChar, "}")
|
||||
XCTAssertNotNil(str.range(of: "{\"service\":\"SwiftyBeaver\"}"))
|
||||
XCTAssertNotNil(str.range(of: "\"severity\":\"DEBUG\""))
|
||||
XCTAssertNotNil(str.range(of: "\"message\":\"test message\\nNewlineäößø\""))
|
||||
XCTAssertNotNil(str.range(of: "\"functionName\":\"TestFunction()\""))
|
||||
XCTAssertNotNil(str.range(of: "\"user\":\"Beaver\""))
|
||||
XCTAssertNotNil(str.range(of: "\"method\":\"GET\""))
|
||||
XCTAssertNotNil(str.range(of: "\"responseStatusCode\":200"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static var allTests = [
|
||||
("testUseGoogleCloudPDestination", testUseGoogleCloudPDestination),
|
||||
("testSend", testSend),
|
||||
("testContextMessage", testContextMessage)
|
||||
]
|
||||
|
||||
}
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
//
|
||||
// SBPlatformDestinationTests
|
||||
// SwiftyBeaverTests
|
||||
//
|
||||
// Created by Sebastian Kreutzberger on 22.01.16.
|
||||
// Copyright © 2016 Sebastian Kreutzberger
|
||||
// Some rights reserved: http://opensource.org/licenses/MIT
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
@testable import SwiftyBeaver
|
||||
|
||||
class SBPlatformDestinationTests: XCTestCase {
|
||||
|
||||
var platform = SBPlatformDestination(appID: "", appSecret: "", encryptionKey: "")
|
||||
|
||||
struct SBPlatformCredentials {
|
||||
/*
|
||||
use environment variables to inject platform credentials into the tests
|
||||
set in Terminal via:
|
||||
|
||||
export SBPLATFORM_SERVER_URL=
|
||||
export SBPLATFORM_APP_ID=
|
||||
export SBPLATFORM_APP_SECRET=
|
||||
export SBPLATFORM_ENCRYPTION_KEY=
|
||||
*/
|
||||
static let serverURL = ProcessInfo.processInfo.environment["SBPLATFORM_SERVER_URL"] ?? "https://api.swiftybeaver.com/api/entries/"
|
||||
static let appID = ProcessInfo.processInfo.environment["SBPLATFORM_APP_ID"] ?? ""
|
||||
static let appSecret = ProcessInfo.processInfo.environment["SBPLATFORM_APP_SECRET"] ?? ""
|
||||
static let encryptionKey = ProcessInfo.processInfo.environment["SBPLATFORM_ENCRYPTION_KEY"] ?? ""
|
||||
}
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
SwiftyBeaver.removeAllDestinations()
|
||||
platform = SBPlatformDestination(
|
||||
appID: SBPlatformCredentials.appID,
|
||||
appSecret: SBPlatformCredentials.appSecret,
|
||||
encryptionKey: SBPlatformCredentials.encryptionKey,
|
||||
serverURL: URL(string: SBPlatformCredentials.serverURL)
|
||||
)
|
||||
// uncomment to verify that the env vars "arrive" in the tests
|
||||
print("\nTesting SBPlatform using")
|
||||
print("Server URL: \(platform.serverURL!)")
|
||||
print("App ID: \(platform.appID)")
|
||||
print("App Secret: \(platform.appSecret)")
|
||||
print("Encryption Key: \(platform.encryptionKey)\n")
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testLoggingWithoutDestination() {
|
||||
let log = SwiftyBeaver.self
|
||||
// no destination was set, yet
|
||||
log.verbose("Where do I log to?")
|
||||
}
|
||||
|
||||
func testSend() {
|
||||
// let dateStr = formatter.stringFromDate(NSDate())
|
||||
//let platform = SBPlatformDestination()
|
||||
let msg = "test message\nNewlineäößø"
|
||||
let thread = ""
|
||||
let file = "/file/path.swift"
|
||||
let function = "TestFunction()"
|
||||
let line = 123
|
||||
platform.showNSLog = true
|
||||
let str = platform.send(.verbose, msg: msg, thread: thread, file: file, function: function, line: line)
|
||||
XCTAssertNotNil(str)
|
||||
if let str = str {
|
||||
XCTAssertEqual(str.first, "{")
|
||||
XCTAssertEqual(str.last, "}")
|
||||
XCTAssertNotNil(str.range(of: "\"line\":123"))
|
||||
XCTAssertNotNil(str.range(of: "\"message\":\"test message\\nNewlineäößø\""))
|
||||
XCTAssertNotNil(str.range(of: "\"fileName\":\"path.swift\""))
|
||||
XCTAssertNotNil(str.range(of: "\"timestamp\":"))
|
||||
XCTAssertNotNil(str.range(of: "\"level\":0"))
|
||||
XCTAssertNotNil(str.range(of: "\"thread\":\"\""))
|
||||
XCTAssertNotNil(str.range(of: "\"function\":\"TestFunction()\""))
|
||||
}
|
||||
}
|
||||
|
||||
func testSendingPointsFromLevel() {
|
||||
var points = platform.sendingPointsForLevel(SwiftyBeaver.Level.verbose)
|
||||
XCTAssertEqual(points, platform.sendingPoints.verbose)
|
||||
points = platform.sendingPointsForLevel(SwiftyBeaver.Level.debug)
|
||||
XCTAssertEqual(points, platform.sendingPoints.debug)
|
||||
points = platform.sendingPointsForLevel(SwiftyBeaver.Level.info)
|
||||
XCTAssertEqual(points, platform.sendingPoints.info)
|
||||
points = platform.sendingPointsForLevel(SwiftyBeaver.Level.warning)
|
||||
XCTAssertEqual(points, platform.sendingPoints.warning)
|
||||
points = platform.sendingPointsForLevel(SwiftyBeaver.Level.error)
|
||||
XCTAssertEqual(points, platform.sendingPoints.error)
|
||||
}
|
||||
|
||||
func testSendToServerAsync() {
|
||||
if platform.appID.isEmpty || platform.appSecret.isEmpty || platform.encryptionKey.isEmpty {
|
||||
// leave the test on missing credentials
|
||||
print("leaving SBPlatform tests due to empty credentials")
|
||||
return
|
||||
}
|
||||
|
||||
let jsonStr = "foobar"
|
||||
let correctURL = platform.serverURL
|
||||
|
||||
// invalid address
|
||||
if let serverURL = URL(string: "https://notexisting.swiftybeaver.com") {
|
||||
platform.serverURL = serverURL
|
||||
}
|
||||
let exp = expectation(description: "returns false due to invalid URL")
|
||||
|
||||
platform.sendToServerAsync(jsonStr) { ok, status in
|
||||
XCTAssertFalse(ok)
|
||||
XCTAssertEqual(status, 0)
|
||||
exp.fulfill()
|
||||
}
|
||||
waitForExpectations(timeout: 11, handler: nil)
|
||||
|
||||
// invalid app ID
|
||||
platform.serverURL = correctURL
|
||||
platform.appID = "abc"
|
||||
let exp2 = expectation(description: "returns false due to invalid app ID")
|
||||
|
||||
platform.sendToServerAsync(jsonStr) { ok, status in
|
||||
XCTAssertFalse(ok)
|
||||
XCTAssertEqual(status, 401)
|
||||
exp2.fulfill()
|
||||
}
|
||||
waitForExpectations(timeout: 11, handler: nil)
|
||||
|
||||
// invalid secret
|
||||
platform.appID = SBPlatformCredentials.appID
|
||||
platform.appSecret += "invalid"
|
||||
let exp3 = expectation(description: "returns false due to invalid secret")
|
||||
|
||||
platform.sendToServerAsync(jsonStr) { ok, status in
|
||||
XCTAssertFalse(ok)
|
||||
XCTAssertEqual(status, 401)
|
||||
exp3.fulfill()
|
||||
}
|
||||
|
||||
/*
|
||||
// that should work. deactivated to avoid "foobar" messages on serverpost
|
||||
platform.appID = SBPlatformCredentials.appID
|
||||
platform.appSecret = SBPlatformCredentials.appSecret
|
||||
platform.encryptionKey = SBPlatformCredentials.encryptionKey
|
||||
let exp4 = expectationWithDescription("returns ok on valid request")
|
||||
|
||||
platform.sendToServerAsync(jsonStr) {
|
||||
ok, status in
|
||||
XCTAssertTrue(ok)
|
||||
XCTAssertEqual(status, 200)
|
||||
exp4.fulfill()
|
||||
}
|
||||
*/
|
||||
waitForExpectations(timeout: 11, handler: nil)
|
||||
|
||||
}
|
||||
|
||||
func testSingleSending() {
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
// add logging to SwiftyBeaver Platform
|
||||
platform.showNSLog = true
|
||||
//let jsonFile = NSURL(fileURLWithPath: "/tmp/testSBPlatform.json")!
|
||||
//deleteFile(NSURL(string: String(jsonFile) + ".send")!)
|
||||
|
||||
if platform.appID.isEmpty || platform.appSecret.isEmpty || platform.encryptionKey.isEmpty {
|
||||
// leave the test on missing credentials
|
||||
print("leaving SBPlatform test testIntegration() due to empty credentials")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertTrue(log.addDestination(platform))
|
||||
//XCTAssertEqual(log.countDestinations(), 2)
|
||||
|
||||
// send logs in chunks, use high threshold value to test performance
|
||||
platform.sendingPoints.threshold = 10
|
||||
log.verbose("a verbose message 1")
|
||||
log.debug("a debug message 2")
|
||||
log.info("an info message 3")
|
||||
log.error("an error message 4")
|
||||
|
||||
print("waiting")
|
||||
// do some further waiting for sending to complete
|
||||
for _ in 1...platform.sendingPoints.threshold + 3 {
|
||||
// simulate work by doing a computing
|
||||
var x = 1.0
|
||||
for index2 in 1...50000 {
|
||||
x = sqrt(Double(index2))
|
||||
XCTAssertEqual(x, sqrt(Double(index2)))
|
||||
}
|
||||
}
|
||||
sleep(5)
|
||||
print("finished")
|
||||
}
|
||||
|
||||
func testIntegration() {
|
||||
let log = SwiftyBeaver.self
|
||||
let formatter = DateFormatter()
|
||||
|
||||
// add logging to SwiftyBeaver Platform
|
||||
platform.showNSLog = true
|
||||
//let jsonFile = NSURL(fileURLWithPath: "/tmp/testSBPlatform.json")!
|
||||
//deleteFile(NSURL(string: String(jsonFile) + ".send")!)
|
||||
|
||||
if platform.appID.isEmpty || platform.appSecret.isEmpty || platform.encryptionKey.isEmpty {
|
||||
// leave the test on missing credentials
|
||||
print("leaving SBPlatform test testIntegration() due to empty credentials")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertTrue(log.addDestination(platform))
|
||||
//XCTAssertEqual(log.countDestinations(), 2)
|
||||
|
||||
// send logs in chunks, use high threshold value to test performance
|
||||
platform.sendingPoints.threshold = 10
|
||||
for index in 1...platform.sendingPoints.threshold + 3 {
|
||||
// simulate work by doing a computing
|
||||
var x = 1.0
|
||||
for index2 in 1...50000 {
|
||||
x = sqrt(Double(index2))
|
||||
XCTAssertEqual(x, sqrt(Double(index2)))
|
||||
}
|
||||
|
||||
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
|
||||
let dateStr = formatter.string(from: Date())
|
||||
|
||||
log.debug("msg \(index) - \(dateStr)")
|
||||
}
|
||||
XCTAssertTrue(log.flush(secondTimeout: 3))
|
||||
|
||||
// do some further waiting for sending to complete
|
||||
for _ in 1...platform.sendingPoints.threshold + 3 {
|
||||
// simulate work by doing a computing
|
||||
var x = 1.0
|
||||
for index2 in 1...50000 {
|
||||
x = sqrt(Double(index2))
|
||||
XCTAssertEqual(x, sqrt(Double(index2)))
|
||||
}
|
||||
}
|
||||
print("waiting")
|
||||
sleep(5)
|
||||
print("finished")
|
||||
}
|
||||
|
||||
func testDeviceDetails() {
|
||||
let device = platform.deviceDetails()
|
||||
XCTAssertEqual(device["os"], OS)
|
||||
XCTAssertGreaterThan(device["os"]!.length, 0)
|
||||
XCTAssertGreaterThan(device["osVersion"]!.length, 4)
|
||||
XCTAssertEqual(device["hostName"], "")
|
||||
XCTAssertEqual(device["deviceName"], DEVICE_NAME)
|
||||
XCTAssertEqual(device["deviceModel"], DEVICE_MODEL)
|
||||
//NSLog(stats)
|
||||
}
|
||||
|
||||
func testAnalytics() {
|
||||
if platform.appID.isEmpty || platform.appSecret.isEmpty || platform.encryptionKey.isEmpty {
|
||||
// leave the test on missing credentials or Travis will fail
|
||||
return
|
||||
}
|
||||
|
||||
let dict = platform.analytics(platform.analyticsFileURL, update: false)
|
||||
print(dict)
|
||||
if let uuid = dict["uuid"] as? String {
|
||||
XCTAssertEqual(uuid.length, 36)
|
||||
XCTAssertEqual(uuid, platform.analyticsUUID)
|
||||
}
|
||||
if let firstStart = dict["firstStart"] as? String {
|
||||
XCTAssertEqual(firstStart.length, 23)
|
||||
}
|
||||
if let lastStart = dict["lastStart"] as? String {
|
||||
XCTAssertEqual(lastStart.length, 23)
|
||||
}
|
||||
if let starts = dict["starts"] as? Int {
|
||||
XCTAssertGreaterThanOrEqual(starts, 1)
|
||||
}
|
||||
if let userName = dict["userName"] as? String {
|
||||
XCTAssertEqual(userName, "")
|
||||
}
|
||||
|
||||
XCTAssertTrue(platform.saveDictToFile(dict, url: platform.analyticsFileURL))
|
||||
|
||||
// set userName
|
||||
platform.analyticsUserName = "foo@bar.com"
|
||||
let dict2 = platform.analytics(platform.analyticsFileURL, update: false)
|
||||
if let userName = dict2["userName"] as? String {
|
||||
XCTAssertEqual(userName, "foo@bar.com")
|
||||
}
|
||||
XCTAssertEqual(platform.analyticsUserName, "foo@bar.com")
|
||||
}
|
||||
|
||||
/// helper function to delete temp file before test
|
||||
func deleteFile(url: URL) -> Bool {
|
||||
do {
|
||||
try FileManager.default.removeItem(at: url as URL)
|
||||
return true
|
||||
} catch let error {
|
||||
NSLog("Unit test: could not delete file \(url). \(error)")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MARK: Linux allTests
|
||||
|
||||
static let allTests = [
|
||||
("testLoggingWithoutDestination", testLoggingWithoutDestination),
|
||||
("testSend", testSend),
|
||||
("testSendingPointsFromLevel", testSendingPointsFromLevel),
|
||||
("testSendToServerAsync", testSendToServerAsync),
|
||||
("testIntegration", testIntegration),
|
||||
("testDeviceDetails", testDeviceDetails),
|
||||
("testAnalytics", testAnalytics)
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// SecretsExample.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Sebastian Kreutzberger on 1/25/16.
|
||||
// Copyright © 2016 Sebastian Kreutzberger. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/*
|
||||
WARNING:
|
||||
========
|
||||
|
||||
Never put this file with values under source control!
|
||||
|
||||
Instead copy & rename this file to Secrets.swift (!!!), uncomment the Secrets struct
|
||||
and add your credentials there. Secrets.swift is excluded from Git via .gitignore and can contain secrets.
|
||||
|
||||
*/
|
||||
|
||||
struct Secrets {
|
||||
|
||||
struct Platform {
|
||||
static let appID = ""
|
||||
static let appSecret = ""
|
||||
static let encryptionKey = ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
//
|
||||
// SwiftyBeaverTests.swift
|
||||
// SwiftyBeaverTests
|
||||
//
|
||||
// Created by Sebastian Kreutzberger (Twitter @skreutzb) on 28.11.15.
|
||||
// Copyright © 2015 Sebastian Kreutzberger
|
||||
// Some rights reserved: http://opensource.org/licenses/MIT
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
@testable import SwiftyBeaver
|
||||
|
||||
class SwiftyBeaverTests: XCTestCase {
|
||||
|
||||
var instanceVar = "an instance variable"
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
SwiftyBeaver.removeAllDestinations()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testAddDestination() {
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
// add invalid destination
|
||||
XCTAssertEqual(log.countDestinations(), 0)
|
||||
|
||||
// add valid destinations
|
||||
let console = ConsoleDestination()
|
||||
let console2 = ConsoleDestination()
|
||||
let file = FileDestination()
|
||||
|
||||
XCTAssertEqual(log.countDestinations(), 0)
|
||||
XCTAssertTrue(log.addDestination(console))
|
||||
XCTAssertEqual(log.countDestinations(), 1)
|
||||
XCTAssertFalse(log.addDestination(console))
|
||||
XCTAssertEqual(log.countDestinations(), 1)
|
||||
XCTAssertTrue(log.addDestination(console2))
|
||||
XCTAssertEqual(log.countDestinations(), 2)
|
||||
XCTAssertFalse(log.addDestination(console2))
|
||||
XCTAssertTrue(log.addDestination(file))
|
||||
XCTAssertEqual(log.countDestinations(), 3)
|
||||
}
|
||||
|
||||
func testRemoveDestination() {
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
// remove invalid destination
|
||||
XCTAssertEqual(log.countDestinations(), 0)
|
||||
|
||||
// remove valid destinations
|
||||
let console = ConsoleDestination()
|
||||
let console2 = ConsoleDestination()
|
||||
let file = FileDestination()
|
||||
|
||||
// add destinations
|
||||
XCTAssertTrue(log.addDestination(console))
|
||||
XCTAssertTrue(log.addDestination(console2))
|
||||
XCTAssertTrue(log.addDestination(file))
|
||||
XCTAssertEqual(log.countDestinations(), 3)
|
||||
// remove destinations
|
||||
XCTAssertTrue(log.removeDestination(console))
|
||||
XCTAssertEqual(log.countDestinations(), 2)
|
||||
XCTAssertFalse(log.removeDestination(console))
|
||||
XCTAssertEqual(log.countDestinations(), 2)
|
||||
XCTAssertTrue(log.removeDestination(console2))
|
||||
XCTAssertFalse(log.removeDestination(console2))
|
||||
XCTAssertEqual(log.countDestinations(), 1)
|
||||
XCTAssertTrue(log.removeDestination(file))
|
||||
XCTAssertEqual(log.countDestinations(), 0)
|
||||
}
|
||||
|
||||
func testLogVerifiesIfShouldLogOnAllDestinations() {
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
let dest1 = MockDestination()
|
||||
dest1.asynchronously = false
|
||||
let dest2 = MockDestination()
|
||||
dest2.asynchronously = false
|
||||
|
||||
log.addDestination(dest1)
|
||||
log.addDestination(dest2)
|
||||
|
||||
log.dispatch_send(level: .warning, message: "Message", thread: "Thread", file: "File", function: "Function()", line: 123, context: "Context")
|
||||
|
||||
XCTAssertEqual(dest1.shouldLogToLevel, SwiftyBeaver.Level.warning)
|
||||
XCTAssertEqual(dest2.shouldLogToLevel, SwiftyBeaver.Level.warning)
|
||||
|
||||
XCTAssertEqual(dest1.shouldLogPath, "File")
|
||||
XCTAssertEqual(dest2.shouldLogPath, "File")
|
||||
|
||||
XCTAssertEqual(dest1.shouldLogFunction, "Function()")
|
||||
XCTAssertEqual(dest2.shouldLogFunction, "Function()")
|
||||
|
||||
XCTAssertEqual(dest1.shouldLogMessage, "Message")
|
||||
XCTAssertEqual(dest2.shouldLogMessage, "Message")
|
||||
}
|
||||
|
||||
func testLogCallsAllDestinations() {
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
let dest1 = MockDestination()
|
||||
dest1.asynchronously = false
|
||||
let dest2 = MockDestination()
|
||||
dest2.asynchronously = false
|
||||
|
||||
log.addDestination(dest1)
|
||||
log.addDestination(dest2)
|
||||
|
||||
log.dispatch_send(level: .warning, message: "Message", thread: "Thread", file: "File", function: "Function()", line: 123, context: "Context")
|
||||
|
||||
XCTAssertEqual(dest1.didSendToLevel, SwiftyBeaver.Level.warning)
|
||||
XCTAssertEqual(dest2.didSendToLevel, SwiftyBeaver.Level.warning)
|
||||
|
||||
XCTAssertEqual(dest1.didSendMessage, "Message")
|
||||
XCTAssertEqual(dest2.didSendMessage, "Message")
|
||||
|
||||
XCTAssertEqual(dest1.didSendToThread, "Thread")
|
||||
XCTAssertEqual(dest2.didSendToThread, "Thread")
|
||||
|
||||
XCTAssertEqual(dest1.didSendFile, "File")
|
||||
XCTAssertEqual(dest2.didSendFile, "File")
|
||||
|
||||
XCTAssertEqual(dest1.didSendFunction, "Function()")
|
||||
XCTAssertEqual(dest2.didSendFunction, "Function()")
|
||||
|
||||
XCTAssertEqual(dest1.didSendLine, 123)
|
||||
XCTAssertEqual(dest2.didSendLine, 123)
|
||||
|
||||
XCTAssertEqual(dest1.didSendContext as? String, "Context")
|
||||
XCTAssertEqual(dest2.didSendContext as? String, "Context")
|
||||
}
|
||||
|
||||
func testLoggingWithoutDestination() {
|
||||
let log = SwiftyBeaver.self
|
||||
// no destination was set, yet
|
||||
log.verbose("Where do I log to?")
|
||||
}
|
||||
|
||||
func testDestinationIntegration() {
|
||||
let log = SwiftyBeaver.self
|
||||
log.verbose("that should lead to nowhere")
|
||||
|
||||
// add console
|
||||
let console = ConsoleDestination()
|
||||
XCTAssertTrue(log.addDestination(console))
|
||||
log.verbose("the default console destination")
|
||||
// add another console and set it to be less chatty
|
||||
let console2 = ConsoleDestination()
|
||||
XCTAssertTrue(log.addDestination(console2))
|
||||
XCTAssertEqual(log.countDestinations(), 2)
|
||||
console2.format = "$L: $M"
|
||||
console2.minLevel = SwiftyBeaver.Level.debug
|
||||
log.verbose("a verbose hello from hopefully just 1 console!")
|
||||
log.debug("a debug hello from 2 different consoles!")
|
||||
|
||||
// add file
|
||||
let file = FileDestination()
|
||||
file.logFileURL = URL(string: "file:///tmp/testSwiftyBeaver.log")!
|
||||
XCTAssertTrue(log.addDestination(file))
|
||||
XCTAssertEqual(log.countDestinations(), 3)
|
||||
log.verbose("default file msg 1")
|
||||
log.verbose("default file msg 2")
|
||||
log.verbose("default file msg 3")
|
||||
|
||||
// log to another file
|
||||
let file2 = FileDestination()
|
||||
file2.logFileURL = URL(string: "file:///tmp/testSwiftyBeaver2.log")!
|
||||
console2.format = "$L: $M"
|
||||
file2.minLevel = SwiftyBeaver.Level.debug
|
||||
XCTAssertTrue(log.addDestination(file2))
|
||||
XCTAssertEqual(log.countDestinations(), 4)
|
||||
log.verbose("this should be in file 1")
|
||||
log.debug("this should be in both files, msg 1")
|
||||
log.info("this should be in both files, msg 2")
|
||||
|
||||
// log to default file location
|
||||
let file3 = FileDestination()
|
||||
console2.format = "$L: $M"
|
||||
XCTAssertTrue(log.addDestination(file3))
|
||||
XCTAssertEqual(log.countDestinations(), 5)
|
||||
guard let f3URL = file3.logFileURL else {
|
||||
return
|
||||
}
|
||||
log.info("Logging to default log file \(f3URL)")
|
||||
}
|
||||
|
||||
func testColors() {
|
||||
let log = SwiftyBeaver.self
|
||||
log.verbose("that should lead to nowhere")
|
||||
|
||||
// add console
|
||||
let console = ConsoleDestination()
|
||||
XCTAssertTrue(log.addDestination(console))
|
||||
|
||||
// add file
|
||||
let file = FileDestination()
|
||||
file.logFileURL = URL(string: "file:///tmp/testSwiftyBeaver.log")!
|
||||
file.format = "$L: $M"
|
||||
XCTAssertTrue(log.addDestination(file))
|
||||
|
||||
log.verbose("not so important")
|
||||
log.debug("something to debug")
|
||||
log.info("a nice information")
|
||||
log.warning("oh no, that won’t be good")
|
||||
log.error("ouch, an error did occur!")
|
||||
|
||||
XCTAssertEqual(log.countDestinations(), 2)
|
||||
}
|
||||
|
||||
func testUptime() {
|
||||
let log = SwiftyBeaver.self
|
||||
log.verbose("that should lead to nowhere")
|
||||
|
||||
// add console
|
||||
let console = ConsoleDestination()
|
||||
console.format = "$U: $M"
|
||||
XCTAssertTrue(log.addDestination(console))
|
||||
|
||||
// add file
|
||||
let file = FileDestination()
|
||||
file.logFileURL = URL(string: "file:///tmp/testSwiftyBeaver.log")!
|
||||
file.format = "$U: $M"
|
||||
XCTAssertTrue(log.addDestination(file))
|
||||
|
||||
log.verbose("not so important")
|
||||
log.debug("something to debug")
|
||||
log.info("a nice information")
|
||||
log.warning("oh no, that won’t be good")
|
||||
log.error("ouch, an error did occur!")
|
||||
|
||||
XCTAssertEqual(log.countDestinations(), 2)
|
||||
}
|
||||
|
||||
func testModifiedColors() {
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
// add console
|
||||
let console = ConsoleDestination()
|
||||
XCTAssertTrue(log.addDestination(console))
|
||||
|
||||
// change default color
|
||||
console.levelColor.verbose = "fg255,0,255;"
|
||||
console.levelColor.debug = "fg255,100,0;"
|
||||
console.levelColor.info = ""
|
||||
console.levelColor.warning = "fg255,255,255;"
|
||||
console.levelColor.error = "fg100,0,200;"
|
||||
|
||||
log.verbose("not so important, level in magenta")
|
||||
log.debug("something to debug, level in orange")
|
||||
log.info("a nice information, level in black")
|
||||
log.warning("oh no, that won’t be good, level in white")
|
||||
log.error("ouch, an error did occur!, level in purple")
|
||||
}
|
||||
|
||||
func testDifferentMessageTypes() {
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
// add console
|
||||
let console = ConsoleDestination()
|
||||
console.format = "$L: $M"
|
||||
console.levelString.info = "interesting number"
|
||||
XCTAssertTrue(log.addDestination(console))
|
||||
|
||||
log.verbose("My name is üÄölèå")
|
||||
log.verbose(123)
|
||||
log.info(-123.45678)
|
||||
log.warning(NSDate())
|
||||
log.error(["I", "like", "logs!"])
|
||||
log.error(["beaver": "yeah", "age": 12])
|
||||
|
||||
// JSON Logging
|
||||
let jsonConsole = ConsoleDestination()
|
||||
jsonConsole.format = "$J"
|
||||
XCTAssertTrue(log.addDestination(jsonConsole))
|
||||
|
||||
log.verbose("My name is üÄölèå")
|
||||
log.verbose(123)
|
||||
log.info(-123.45678)
|
||||
log.warning(NSDate())
|
||||
log.error(["I", "like", "logs!"])
|
||||
log.error(["beaver": "yeah", "age": 12])
|
||||
|
||||
XCTAssertEqual(log.countDestinations(), 2)
|
||||
}
|
||||
|
||||
func testAutoClosure() {
|
||||
let log = SwiftyBeaver.self
|
||||
// add console
|
||||
let console = ConsoleDestination()
|
||||
XCTAssertTrue(log.addDestination(console))
|
||||
// should not create a compile error relating autoclosure
|
||||
log.info(instanceVar)
|
||||
}
|
||||
|
||||
func testLongRunningTaskIsNotExecutedWhenLoggingUnderMinLevel() {
|
||||
|
||||
let log = SwiftyBeaver.self
|
||||
|
||||
// add console
|
||||
let console = ConsoleDestination()
|
||||
// set info level on default
|
||||
console.minLevel = .info
|
||||
|
||||
XCTAssertTrue(log.addDestination(console))
|
||||
|
||||
func longRunningTask() -> String {
|
||||
XCTAssert(false, "A block passed should not be executed if the log should not be logged.")
|
||||
return "This should NOT BE VISIBLE!"
|
||||
}
|
||||
|
||||
log.verbose(longRunningTask())
|
||||
}
|
||||
|
||||
func testVersionAndBuild() {
|
||||
XCTAssertGreaterThan(SwiftyBeaver.version.length, 4)
|
||||
XCTAssertGreaterThan(SwiftyBeaver.build, 500)
|
||||
}
|
||||
|
||||
func testStripParams() {
|
||||
var f = "singleParam"
|
||||
XCTAssertEqual(SwiftyBeaver.stripParams(function: f), "singleParam()")
|
||||
f = "logWithParamFunc(_:foo:hello:)"
|
||||
XCTAssertEqual(SwiftyBeaver.stripParams(function: f), "logWithParamFunc()")
|
||||
f = "aFunc()"
|
||||
XCTAssertEqual(SwiftyBeaver.stripParams(function: f), "aFunc()")
|
||||
}
|
||||
|
||||
func testGetCorrectThread() {
|
||||
let log = SwiftyBeaver.self
|
||||
let mock = MockDestination()
|
||||
// set info level on default
|
||||
mock.minLevel = .verbose
|
||||
mock.asynchronously = false
|
||||
|
||||
log.addDestination(mock)
|
||||
|
||||
//main thread
|
||||
log.verbose("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "")
|
||||
log.debug("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "")
|
||||
log.info("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "")
|
||||
log.warning("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "")
|
||||
log.error("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "")
|
||||
|
||||
var expectation = XCTestExpectation(description: "thread check")
|
||||
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
log.verbose("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "com.apple.root.background-qos")
|
||||
log.debug("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "com.apple.root.background-qos")
|
||||
log.info("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "com.apple.root.background-qos")
|
||||
log.warning("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "com.apple.root.background-qos")
|
||||
log.error("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "com.apple.root.background-qos")
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
self.wait(for: [expectation], timeout: 2)
|
||||
|
||||
expectation = XCTestExpectation(description: "thread check custom")
|
||||
|
||||
DispatchQueue.init(label: "MyTestLabel").async {
|
||||
log.verbose("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "MyTestLabel")
|
||||
log.debug("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "MyTestLabel")
|
||||
log.info("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "MyTestLabel")
|
||||
log.warning("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "MyTestLabel")
|
||||
log.error("Hi")
|
||||
XCTAssertEqual(mock.didSendToThread, "MyTestLabel")
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
self.wait(for: [expectation], timeout: 2)
|
||||
|
||||
}
|
||||
|
||||
static let allTests = [
|
||||
("testAddDestination", testAddDestination),
|
||||
("testRemoveDestination", testRemoveDestination),
|
||||
("testLoggingWithoutDestination", testLoggingWithoutDestination),
|
||||
("testDestinationIntegration", testDestinationIntegration),
|
||||
("testColors", testColors),
|
||||
("testModifiedColors", testModifiedColors),
|
||||
("testDifferentMessageTypes", testDifferentMessageTypes),
|
||||
("testAutoClosure", testAutoClosure),
|
||||
("testLongRunningTaskIsNotExecutedWhenLoggingUnderMinLevel",
|
||||
testLongRunningTaskIsNotExecutedWhenLoggingUnderMinLevel),
|
||||
("testVersionAndBuild", testVersionAndBuild),
|
||||
("testStripParams", testStripParams),
|
||||
//("testGetCorrectThread", testGetCorrectThread) // incorrect on Linux
|
||||
]
|
||||
}
|
||||
|
||||
private class MockDestination: BaseDestination {
|
||||
var didSendToLevel: SwiftyBeaver.Level?
|
||||
var didSendMessage: String?
|
||||
var didSendToThread: String?
|
||||
var didSendFile: String?
|
||||
var didSendFunction: String?
|
||||
var didSendLine: Int?
|
||||
var didSendContext: (Any?)?
|
||||
|
||||
override func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, file: String, function: String, line: Int, context: Any?) -> String? {
|
||||
didSendToLevel = level
|
||||
didSendMessage = msg
|
||||
didSendToThread = thread
|
||||
didSendFile = file
|
||||
didSendFunction = function
|
||||
didSendLine = line
|
||||
didSendContext = context
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
var shouldLogToLevel: SwiftyBeaver.Level?
|
||||
var shouldLogPath: String?
|
||||
var shouldLogFunction: String?
|
||||
var shouldLogMessage: String?
|
||||
override func shouldLevelBeLogged(_ level: SwiftyBeaver.Level, path: String, function: String, message: String?) -> Bool {
|
||||
shouldLogToLevel = level
|
||||
shouldLogPath = path
|
||||
shouldLogFunction = function
|
||||
shouldLogMessage = message
|
||||
return true
|
||||
}
|
||||
|
||||
override func hasMessageFilters() -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# -e: exit when a command fails
|
||||
# -o pipefail: set exit status of shell script to last nonzero exit code, if any were nonzero.
|
||||
set -o pipefail
|
||||
|
||||
echo ""
|
||||
echo "Running Tests in Docker Container"
|
||||
echo "Swift 5"
|
||||
echo "================================="
|
||||
docker build -t swiftybeaver -f Dockerfile .
|
||||
|
||||
docker run -e SBPLATFORM_APP_ID=$SBPLATFORM_APP_ID \
|
||||
-e SBPLATFORM_APP_SECRET=$SBPLATFORM_APP_SECRET \
|
||||
-e SBPLATFORM_ENCRYPTION_KEY=$SBPLATFORM_ENCRYPTION_KEY \
|
||||
--name swiftybeaver --rm swiftybeaver swift test \
|
||||
|| (set +x; echo -e "\033[0;31mTests exited with non-zero exit code\033[0m"; tput bel; exit 1)
|
||||
echo "Finished tests, docker container were removed."
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
<?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>SwiftyMarkdown.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,19 @@
|
||||
.DS_Store
|
||||
.*.swp
|
||||
bin
|
||||
src
|
||||
lib
|
||||
frameworks
|
||||
include/openssl
|
||||
.build
|
||||
swiftpm
|
||||
*.gz
|
||||
*.gz.asc
|
||||
*.framework
|
||||
*.o
|
||||
*.pbxuser
|
||||
*.xcworkspace/xcuserdata
|
||||
*.xcworkspace/xcshareddata
|
||||
*.xcodeproj/project.xcworkspace
|
||||
*.xcodeproj/xcuserdata
|
||||
Pods
|
||||
@@ -0,0 +1,15 @@
|
||||
language: objective-c
|
||||
osx_image: xcode12.2
|
||||
xcode_workspace: Demo.xcworkspace
|
||||
xcode_scheme: Demo
|
||||
xcode_destination: platform=iOS Simulator,OS=14.2,name=iPhone 11 Pro Max
|
||||
cache:
|
||||
bundler: true
|
||||
cocoapods: true
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
install:
|
||||
- bundle install --jobs=3 --deployment --path=${BUNDLE_PATH:-vendor/bundle}
|
||||
- bundle exec pod repo update
|
||||
- travis_wait 40 bundle exec pod install --verbose
|
||||
@@ -0,0 +1 @@
|
||||
1.1.1m-0
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Demo.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// Demo
|
||||
//
|
||||
// Created by Davide De Rosa on 10/29/20.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@main
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: UISceneSession Lifecycle
|
||||
|
||||
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
||||
// Called when a new scene session is being created.
|
||||
// Use this method to select a configuration to create the new scene with.
|
||||
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
|
||||
// Called when the user discards a scene session.
|
||||
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
|
||||
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,66 @@
|
||||
<?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>$(DEVELOPMENT_LANGUAGE)</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>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>Default Configuration</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
||||
<key>UISceneStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<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>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// SceneDelegate.swift
|
||||
// Demo
|
||||
//
|
||||
// Created by Davide De Rosa on 10/29/20.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
|
||||
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
||||
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
||||
guard let _ = (scene as? UIWindowScene) else { return }
|
||||
}
|
||||
|
||||
func sceneDidDisconnect(_ scene: UIScene) {
|
||||
// Called as the scene is being released by the system.
|
||||
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
||||
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
||||
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
|
||||
}
|
||||
|
||||
func sceneDidBecomeActive(_ scene: UIScene) {
|
||||
// Called when the scene has moved from an inactive state to an active state.
|
||||
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
||||
}
|
||||
|
||||
func sceneWillResignActive(_ scene: UIScene) {
|
||||
// Called when the scene will move from an active state to an inactive state.
|
||||
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
||||
}
|
||||
|
||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||
// Called as the scene transitions from the background to the foreground.
|
||||
// Use this method to undo the changes made on entering the background.
|
||||
}
|
||||
|
||||
func sceneDidEnterBackground(_ scene: UIScene) {
|
||||
// Called as the scene transitions from the foreground to the background.
|
||||
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
||||
// to restore the scene back to its current state.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// ViewController.swift
|
||||
// Demo
|
||||
//
|
||||
// Created by Davide De Rosa on 10/29/20.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ViewController: UIViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// DemoTests.swift
|
||||
// DemoTests
|
||||
//
|
||||
// Created by Davide De Rosa on 10/29/20.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import Demo
|
||||
|
||||
class DemoTests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() throws {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testPerformanceExample() throws {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?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>$(DEVELOPMENT_LANGUAGE)</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>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,3 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "cocoapods"
|
||||
@@ -0,0 +1,97 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.4)
|
||||
rexml
|
||||
activesupport (6.1.4.1)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
zeitwerk (~> 2.3)
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
algoliasearch (1.27.5)
|
||||
httpclient (~> 2.8, >= 2.8.3)
|
||||
json (>= 1.5.1)
|
||||
atomos (0.1.3)
|
||||
claide (1.0.3)
|
||||
cocoapods (1.11.2)
|
||||
addressable (~> 2.8)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.11.2)
|
||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||
cocoapods-downloader (>= 1.4.0, < 2.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
cocoapods-search (>= 1.0.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.4.0, < 2.0)
|
||||
cocoapods-try (>= 1.1.0, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
escape (~> 0.0.4)
|
||||
fourflusher (>= 2.3.0, < 3.0)
|
||||
gh_inspector (~> 1.0)
|
||||
molinillo (~> 0.8.0)
|
||||
nap (~> 1.0)
|
||||
ruby-macho (>= 1.0, < 3.0)
|
||||
xcodeproj (>= 1.21.0, < 2.0)
|
||||
cocoapods-core (1.11.2)
|
||||
activesupport (>= 5.0, < 7)
|
||||
addressable (~> 2.8)
|
||||
algoliasearch (~> 1.0)
|
||||
concurrent-ruby (~> 1.1)
|
||||
fuzzy_match (~> 2.0.4)
|
||||
nap (~> 1.0)
|
||||
netrc (~> 0.11)
|
||||
public_suffix (~> 4.0)
|
||||
typhoeus (~> 1.0)
|
||||
cocoapods-deintegrate (1.0.5)
|
||||
cocoapods-downloader (1.5.1)
|
||||
cocoapods-plugins (1.0.0)
|
||||
nap
|
||||
cocoapods-search (1.0.1)
|
||||
cocoapods-trunk (1.6.0)
|
||||
nap (>= 0.8, < 2.0)
|
||||
netrc (~> 0.11)
|
||||
cocoapods-try (1.2.0)
|
||||
colored2 (3.1.2)
|
||||
concurrent-ruby (1.1.9)
|
||||
escape (0.0.4)
|
||||
ethon (0.14.0)
|
||||
ffi (>= 1.15.0)
|
||||
ffi (1.15.4)
|
||||
fourflusher (2.3.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.8.10)
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.5.1)
|
||||
minitest (5.14.4)
|
||||
molinillo (0.8.0)
|
||||
nanaimo (0.3.0)
|
||||
nap (1.1.0)
|
||||
netrc (0.11.0)
|
||||
public_suffix (4.0.6)
|
||||
rexml (3.2.5)
|
||||
ruby-macho (2.5.1)
|
||||
typhoeus (1.4.0)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (2.0.4)
|
||||
concurrent-ruby (~> 1.0)
|
||||
xcodeproj (1.21.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (~> 3.2.4)
|
||||
zeitwerk (2.4.2)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
cocoapods
|
||||
|
||||
BUNDLED WITH
|
||||
2.2.15
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,125 @@
|
||||
|
||||
LICENSE ISSUES
|
||||
==============
|
||||
|
||||
The OpenSSL toolkit stays under a double license, i.e. both the conditions of
|
||||
the OpenSSL License and the original SSLeay license apply to the toolkit.
|
||||
See below for the actual license texts.
|
||||
|
||||
OpenSSL License
|
||||
---------------
|
||||
|
||||
/* ====================================================================
|
||||
* Copyright (c) 1998-2018 The OpenSSL Project. All rights reserved.
|
||||
*
|
||||
* 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 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.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
|
||||
*
|
||||
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please contact
|
||||
* openssl-core@openssl.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "OpenSSL"
|
||||
* nor may "OpenSSL" appear in their names without prior written
|
||||
* permission of the OpenSSL Project.
|
||||
*
|
||||
* 6. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
|
||||
* EXPRESSED 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 OpenSSL PROJECT OR
|
||||
* ITS 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This product includes cryptographic software written by Eric Young
|
||||
* (eay@cryptsoft.com). This product includes software written by Tim
|
||||
* Hudson (tjh@cryptsoft.com).
|
||||
*
|
||||
*/
|
||||
|
||||
Original SSLeay License
|
||||
-----------------------
|
||||
|
||||
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* This package is an SSL implementation written
|
||||
* by Eric Young (eay@cryptsoft.com).
|
||||
* The implementation was written so as to conform with Netscapes SSL.
|
||||
*
|
||||
* This library is free for commercial and non-commercial use as long as
|
||||
* the following conditions are aheared to. The following conditions
|
||||
* apply to all code found in this distribution, be it the RC4, RSA,
|
||||
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
|
||||
* included with this distribution is covered by the same copyright terms
|
||||
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
|
||||
*
|
||||
* Copyright remains Eric Young's, and as such any Copyright notices in
|
||||
* the code are not to be removed.
|
||||
* If this package is used in a product, Eric Young should be given attribution
|
||||
* as the author of the parts of the library used.
|
||||
* This can be in the form of a textual message at program startup or
|
||||
* in documentation (online or textual) provided with the package.
|
||||
*
|
||||
* 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 copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* "This product includes cryptographic software written by
|
||||
* Eric Young (eay@cryptsoft.com)"
|
||||
* The word 'cryptographic' can be left out if the rouines from the library
|
||||
* being used are not cryptographic related :-).
|
||||
* 4. If you include any Windows specific code (or a derivative thereof) from
|
||||
* the apps directory (application code) you must include an acknowledgement:
|
||||
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
|
||||
*
|
||||
* The licence and distribution terms for any publically available version or
|
||||
* derivative of this code cannot be changed. i.e. this code cannot simply be
|
||||
* copied and put under another distribution licence
|
||||
* [including the GNU Public Licence.]
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
Pod::Spec.new do |s|
|
||||
openssl_version = "1.1.1l"
|
||||
openssl_targets = "ios-sim-cross-x86_64 ios-sim-cross-arm64 ios64-cross-arm64 ios64-cross-arm64e macos64-x86_64 macos64-arm64 mac-catalyst-x86_64 mac-catalyst-arm64"
|
||||
script_version = "11"
|
||||
|
||||
s.name = "OpenSSL-Apple"
|
||||
s.version = "#{openssl_version}.#{script_version}"
|
||||
s.summary = "A script for compiling OpenSSL for Apple Devices"
|
||||
s.authors = "Felix Schulze", "Davide De Rosa", "Ezat Hashim"
|
||||
|
||||
s.homepage = "https://github.com/keeshux/openssl-apple.git"
|
||||
s.source = { :git => s.homepage.to_s, :tag => "v#{script_version}" }
|
||||
s.license = { :type => 'Apache', :file => 'LICENSE' }
|
||||
|
||||
s.prepare_command = <<-CMD
|
||||
./build-libssl.sh --version="#{openssl_version}" --targets="#{openssl_targets}" --disable-bitcode
|
||||
./create-openssl-framework.sh dynamic
|
||||
CMD
|
||||
|
||||
s.ios.deployment_target = "12.0"
|
||||
s.osx.deployment_target = "10.15"
|
||||
s.vendored_frameworks = "frameworks/openssl.xcframework"
|
||||
s.requires_arc = false
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
// swift-tools-version:5.3
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "openssl-apple",
|
||||
products: [
|
||||
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||
.library(
|
||||
name: "openssl-apple",
|
||||
targets: ["openssl"]),
|
||||
],
|
||||
dependencies: [
|
||||
// Dependencies declare other packages that this package depends on.
|
||||
// .package(url: /* package url */, from: "1.0.0"),
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||
.binaryTarget(
|
||||
name: "openssl",
|
||||
path: "./results/openssl.xcframework.zip"
|
||||
)
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
use_frameworks!
|
||||
platform :ios, '12.0'
|
||||
|
||||
target 'Demo' do
|
||||
pod 'OpenSSL-Apple', :path => '.'
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
PODS:
|
||||
- OpenSSL-Apple (1.1.1h.8)
|
||||
|
||||
DEPENDENCIES:
|
||||
- OpenSSL-Apple (from `.`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
OpenSSL-Apple:
|
||||
:path: "."
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
OpenSSL-Apple: 70990157548ecf94885310231aff52db698e1077
|
||||
|
||||
PODFILE CHECKSUM: 46de088b71467a12879d73c36eda858ad56ed167
|
||||
|
||||
COCOAPODS: 1.10.0
|
||||
@@ -0,0 +1,55 @@
|
||||
# OpenSSL-Apple
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
[](LICENSE)
|
||||
|
||||
This is a fork of the popular work by [Felix Schulze](https://github.com/x2on), that is a set of scripts for using self-compiled builds of the OpenSSL library on the iPhone and the Apple TV.
|
||||
|
||||
However, this repository focuses more on framework-based setups and also adds macOS and watchOS support.
|
||||
|
||||
# Compile library
|
||||
|
||||
Compile OpenSSL 1.1.1l for all targets:
|
||||
|
||||
```
|
||||
./build-libssl.sh --version=1.1.1l
|
||||
```
|
||||
|
||||
Compile OpenSSL 1.1.1l for specific targets:
|
||||
|
||||
```
|
||||
./build-libssl.sh --version=1.1.1l --targets="ios64-cross-arm64 macos64-x86_64 macos64-arm64"
|
||||
```
|
||||
|
||||
For all options see:
|
||||
|
||||
```
|
||||
./build-libssl.sh --help
|
||||
```
|
||||
|
||||
# Generate frameworks
|
||||
|
||||
Statically linked:
|
||||
|
||||
```
|
||||
./create-openssl-framework.sh static
|
||||
```
|
||||
|
||||
Dynamically linked:
|
||||
|
||||
```
|
||||
./create-openssl-framework.sh dynamic
|
||||
```
|
||||
|
||||
# Original project
|
||||
|
||||
* <https://github.com/x2on/OpenSSL-for-iPhone>
|
||||
|
||||
# Acknowledgements
|
||||
|
||||
This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (<https://www.openssl.org/>)
|
||||
@@ -0,0 +1,24 @@
|
||||
<?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>openssl</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.openssl.OpenSSL</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>openssl</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(OPENSSL_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>$(MIN_SDK_VERSION)</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1 @@
|
||||
AppleTV
|
||||
@@ -0,0 +1 @@
|
||||
AppleTV
|
||||
@@ -0,0 +1,24 @@
|
||||
<?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>openssl</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.openssl.OpenSSL</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>openssl</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>10.15</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?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>openssl</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.openssl.OpenSSL</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>openssl</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(OPENSSL_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>$(MIN_SDK_VERSION)</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?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>openssl</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.openssl.OpenSSL</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>openssl</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(OPENSSL_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>$(MIN_SDK_VERSION)</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1 @@
|
||||
Watch
|
||||
@@ -0,0 +1 @@
|
||||
Watch
|
||||
@@ -0,0 +1,24 @@
|
||||
<?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>openssl</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.openssl.OpenSSL</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>openssl</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(OPENSSL_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>$(MIN_SDK_VERSION)</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1 @@
|
||||
iPhone
|
||||
@@ -0,0 +1 @@
|
||||
iPhone
|
||||
+647
@@ -0,0 +1,647 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Automatic build script for libssl and libcrypto
|
||||
# for Apple devices.
|
||||
#
|
||||
# Created by Felix Schulze on 16.12.10.
|
||||
# Copyright 2010-2017 Felix Schulze. All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
# -u Attempt to use undefined variable outputs error message, and forces an exit
|
||||
set -u
|
||||
|
||||
# SCRIPT DEFAULTS
|
||||
|
||||
# Default version in case no version is specified
|
||||
DEFAULTVERSION="1.1.1m"
|
||||
|
||||
# Default (=full) set of targets (OpenSSL >= 1.1.1) to build
|
||||
DEFAULTTARGETS=`cat <<TARGETS
|
||||
ios-sim-cross-x86_64 ios-sim-cross-arm64 ios64-cross-arm64 ios64-cross-arm64e
|
||||
macos64-x86_64 macos64-arm64
|
||||
mac-catalyst-x86_64 mac-catalyst-arm64
|
||||
watchos-cross-armv7k watchos-cross-arm64_32 watchos-sim-cross-x86_64 watchos-sim-cross-i386 watchos-sim-cross-arm64
|
||||
tvos-sim-cross-x86_64 tvos64-cross-arm64
|
||||
TARGETS`
|
||||
|
||||
# Minimum iOS/tvOS SDK version to build for
|
||||
IOS_MIN_SDK_VERSION="12.0"
|
||||
MACOS_MIN_SDK_VERSION="10.14"
|
||||
CATALYST_MIN_SDK_VERSION="10.14"
|
||||
WATCHOS_MIN_SDK_VERSION="4.0"
|
||||
TVOS_MIN_SDK_VERSION="12.0"
|
||||
|
||||
# Init optional env variables (use available variable or default to empty string)
|
||||
CURL_OPTIONS="${CURL_OPTIONS:-}"
|
||||
CONFIG_OPTIONS="${CONFIG_OPTIONS:-}"
|
||||
|
||||
echo_help()
|
||||
{
|
||||
echo "Usage: $0 [options...]"
|
||||
echo "Generic options"
|
||||
echo " --branch=BRANCH Select OpenSSL branch to build. The script will determine and download the latest release for that branch"
|
||||
echo " --cleanup Clean up build directories (bin, include/openssl, lib, src) before starting build"
|
||||
echo " --ec-nistp-64-gcc-128 Enable configure option enable-ec_nistp_64_gcc_128 for 64 bit builds"
|
||||
echo " -h, --help Print help (this message)"
|
||||
echo " --ios-sdk=SDKVERSION Override iOS SDK version"
|
||||
echo " --macos-sdk=SDKVERSION Override macOS SDK version"
|
||||
echo " --catalyst-sdk=SDKVERSION Override macOS SDK version for Catalyst"
|
||||
echo " --watchos-sdk=SDKVERSION Override watchOS SDK version"
|
||||
echo " --tvos-sdk=SDKVERSION Override tvOS SDK version"
|
||||
echo " --min-ios-sdk=SDKVERSION Set minimum iOS SDK version (default: $IOS_MIN_SDK_VERSION)"
|
||||
echo " --min-macos-sdk=SDKVERSION Set minimum macOS SDK version (default: $MACOS_MIN_SDK_VERSION)"
|
||||
echo " --min-watchos-sdk=SDKVERSION Set minimum watchOS SDK version (default: $WATCHOS_MIN_SDK_VERSION)"
|
||||
echo " --min-tvos-sdk=SDKVERSION Set minimum tvOS SDK version (default: $TVOS_MIN_SDK_VERSION)"
|
||||
echo " --noparallel Disable running make with parallel jobs (make -j)"
|
||||
echo " --disable-bitcode Disable embedding Bitcode"
|
||||
echo " -v, --verbose Enable verbose logging"
|
||||
echo " --verbose-on-error Dump last 500 lines from log file if an error occurs (for Travis builds)"
|
||||
echo " --version=VERSION OpenSSL version to build (defaults to ${DEFAULTVERSION})"
|
||||
echo " --deprecated Exclude no-deprecated configure option and build with deprecated methods"
|
||||
echo " --targets=\"TARGET TARGET ...\" Space-separated list of build targets"
|
||||
echo " Options: ${DEFAULTTARGETS}"
|
||||
echo
|
||||
echo "For custom configure options, set variable CONFIG_OPTIONS"
|
||||
echo "For custom cURL options, set variable CURL_OPTIONS"
|
||||
echo " Example: CURL_OPTIONS=\"--proxy 192.168.1.1:8080\" ./build-libssl.sh"
|
||||
}
|
||||
|
||||
spinner()
|
||||
{
|
||||
local pid=$!
|
||||
local delay=0.75
|
||||
local spinstr='|/-\'
|
||||
while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do
|
||||
local temp=${spinstr#?}
|
||||
printf " [%c]" "$spinstr"
|
||||
local spinstr=$temp${spinstr%"$temp"}
|
||||
sleep $delay
|
||||
printf "\b\b\b\b\b"
|
||||
done
|
||||
|
||||
wait $pid
|
||||
return $?
|
||||
}
|
||||
|
||||
# Prepare target and source dir in build loop
|
||||
prepare_target_source_dirs()
|
||||
{
|
||||
# Prepare target dir
|
||||
TARGETDIR="${CURRENTPATH}/bin/${PLATFORM}${SDKVERSION}-${ARCH}.sdk"
|
||||
mkdir -p "${TARGETDIR}"
|
||||
LOG="${TARGETDIR}/build-openssl-${VERSION}.log"
|
||||
|
||||
echo "Building openssl-${VERSION} for ${PLATFORM} ${SDKVERSION} ${ARCH}..."
|
||||
echo " Logfile: ${LOG}"
|
||||
|
||||
# Prepare source dir
|
||||
SOURCEDIR="${CURRENTPATH}/src/${PLATFORM}-${ARCH}"
|
||||
mkdir -p "${SOURCEDIR}"
|
||||
tar zxf "${CURRENTPATH}/${OPENSSL_ARCHIVE_FILE_NAME}" -C "${SOURCEDIR}"
|
||||
cd "${SOURCEDIR}/${OPENSSL_ARCHIVE_BASE_NAME}"
|
||||
chmod u+x ./Configure
|
||||
}
|
||||
|
||||
# Check for error status
|
||||
check_status()
|
||||
{
|
||||
local STATUS=$1
|
||||
local COMMAND=$2
|
||||
|
||||
if [ "${STATUS}" != 0 ]; then
|
||||
if [[ "${LOG_VERBOSE}" != "verbose"* ]]; then
|
||||
echo "Problem during ${COMMAND} - Please check ${LOG}"
|
||||
fi
|
||||
|
||||
# Dump last 500 lines from log file for verbose-on-error
|
||||
if [ "${LOG_VERBOSE}" == "verbose-on-error" ]; then
|
||||
echo "Problem during ${COMMAND} - Dumping last 500 lines from log file"
|
||||
echo
|
||||
tail -n 500 "${LOG}"
|
||||
fi
|
||||
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Run Configure in build loop
|
||||
run_configure()
|
||||
{
|
||||
echo " Configure..."
|
||||
set +e
|
||||
if [ "${LOG_VERBOSE}" == "verbose" ]; then
|
||||
./Configure ${LOCAL_CONFIG_OPTIONS} no-tests | tee "${LOG}"
|
||||
else
|
||||
(./Configure ${LOCAL_CONFIG_OPTIONS} no-tests > "${LOG}" 2>&1) & spinner
|
||||
fi
|
||||
|
||||
# Check for error status
|
||||
check_status $? "Configure"
|
||||
}
|
||||
|
||||
# Run make in build loop
|
||||
run_make()
|
||||
{
|
||||
echo " Make (using ${BUILD_THREADS} thread(s))..."
|
||||
if [ "${LOG_VERBOSE}" == "verbose" ]; then
|
||||
make -j "${BUILD_THREADS}" | tee -a "${LOG}"
|
||||
else
|
||||
(make -j "${BUILD_THREADS}" >> "${LOG}" 2>&1) & spinner
|
||||
fi
|
||||
|
||||
# Check for error status
|
||||
check_status $? "make"
|
||||
}
|
||||
|
||||
# Cleanup and bookkeeping at end of build loop
|
||||
finish_build_loop()
|
||||
{
|
||||
# Return to ${CURRENTPATH} and remove source dir
|
||||
cd "${CURRENTPATH}"
|
||||
rm -r "${SOURCEDIR}"
|
||||
|
||||
# Add references to library files to relevant arrays
|
||||
if [[ "${PLATFORM}" == iPhone* ]]; then
|
||||
LIBSSL_IOS+=("${TARGETDIR}/lib/libssl.a")
|
||||
LIBCRYPTO_IOS+=("${TARGETDIR}/lib/libcrypto.a")
|
||||
if [[ "${PLATFORM}" == iPhoneSimulator* ]]; then
|
||||
OPENSSLCONF_SUFFIX="ios_sim_${ARCH}"
|
||||
else
|
||||
OPENSSLCONF_SUFFIX="ios_${ARCH}"
|
||||
fi
|
||||
elif [[ "${PLATFORM}" == Watch* ]]; then
|
||||
LIBSSL_WATCHOS+=("${TARGETDIR}/lib/libssl.a")
|
||||
LIBCRYPTO_WATCHOS+=("${TARGETDIR}/lib/libcrypto.a")
|
||||
if [[ "${PLATFORM}" == WatchSimulator* ]]; then
|
||||
OPENSSLCONF_SUFFIX="watchos_sim_${ARCH}"
|
||||
else
|
||||
OPENSSLCONF_SUFFIX="watchos_${ARCH}"
|
||||
fi
|
||||
elif [[ "${PLATFORM}" == AppleTV* ]]; then
|
||||
LIBSSL_TVOS+=("${TARGETDIR}/lib/libssl.a")
|
||||
LIBCRYPTO_TVOS+=("${TARGETDIR}/lib/libcrypto.a")
|
||||
if [[ "${PLATFORM}" == AppleTVSimulator* ]]; then
|
||||
OPENSSLCONF_SUFFIX="tvos_sim_${ARCH}"
|
||||
else
|
||||
OPENSSLCONF_SUFFIX="tvos_${ARCH}"
|
||||
fi
|
||||
elif [[ "${PLATFORM}" == Catalyst* ]]; then
|
||||
LIBSSL_CATALYST+=("${TARGETDIR}/lib/libssl.a")
|
||||
LIBCRYPTO_CATALYST+=("${TARGETDIR}/lib/libcrypto.a")
|
||||
OPENSSLCONF_SUFFIX="catalyst_${ARCH}"
|
||||
else
|
||||
LIBSSL_MACOS+=("${TARGETDIR}/lib/libssl.a")
|
||||
LIBCRYPTO_MACOS+=("${TARGETDIR}/lib/libcrypto.a")
|
||||
OPENSSLCONF_SUFFIX="macos_${ARCH}"
|
||||
fi
|
||||
|
||||
# Copy opensslconf.h to bin directory and add to array
|
||||
OPENSSLCONF="opensslconf_${OPENSSLCONF_SUFFIX}.h"
|
||||
cp "${TARGETDIR}/include/openssl/opensslconf.h" "${CURRENTPATH}/bin/${OPENSSLCONF}"
|
||||
OPENSSLCONF_ALL+=("${OPENSSLCONF}")
|
||||
|
||||
# Keep reference to first build target for include file
|
||||
if [ -z "${INCLUDE_DIR}" ]; then
|
||||
INCLUDE_DIR="${TARGETDIR}/include/openssl"
|
||||
fi
|
||||
}
|
||||
|
||||
gpg_validate()
|
||||
{
|
||||
local TARGET=$1
|
||||
local SIG=${2:-${TARGET}.asc}
|
||||
|
||||
GPG_B=$(which gpg)
|
||||
if [ ! -x "${GPG_B}" ]; then
|
||||
echo "WARN: No gpg executable found in PATH. Please consider installing gpg so archive signature validation can proceed."
|
||||
return 1
|
||||
fi
|
||||
|
||||
$GPG_B --keyserver keys.openpgp.org --keyserver-options auto-key-retrieve,include-subkeys --verify-options show-photos --verify "${SIG}" "${TARGET}"
|
||||
}
|
||||
|
||||
# Init optional command line vars
|
||||
ARCHS=""
|
||||
BRANCH=""
|
||||
CLEANUP=""
|
||||
CONFIG_ENABLE_EC_NISTP_64_GCC_128=""
|
||||
CONFIG_DISABLE_BITCODE=""
|
||||
CONFIG_NO_DEPRECATED=""
|
||||
IOS_SDKVERSION=""
|
||||
MACOS_SDKVERSION=""
|
||||
CATALYST_SDKVERSION=""
|
||||
WATCHOS_SDKVERSION=""
|
||||
TVOS_SDKVERSION=""
|
||||
LOG_VERBOSE=""
|
||||
PARALLEL=""
|
||||
TARGETS=""
|
||||
VERSION=""
|
||||
|
||||
# Process command line arguments
|
||||
for i in "$@"
|
||||
do
|
||||
case $i in
|
||||
--branch=*)
|
||||
BRANCH="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--cleanup)
|
||||
CLEANUP="true"
|
||||
;;
|
||||
--deprecated)
|
||||
CONFIG_NO_DEPRECATED="false"
|
||||
;;
|
||||
--ec-nistp-64-gcc-128)
|
||||
CONFIG_ENABLE_EC_NISTP_64_GCC_128="true"
|
||||
;;
|
||||
--disable-bitcode)
|
||||
CONFIG_DISABLE_BITCODE="true"
|
||||
;;
|
||||
-h|--help)
|
||||
echo_help
|
||||
exit
|
||||
;;
|
||||
--ios-sdk=*)
|
||||
IOS_SDKVERSION="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--macos-sdk=*)
|
||||
MACOS_SDKVERSION="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--catalyst-sdk=*)
|
||||
CATALYST_SDKVERSION="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--watchos-sdk=*)
|
||||
WATCHOS_SDKVERSION="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--tvos-sdk=*)
|
||||
TVOS_SDKVERSION="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--min-ios-sdk=*)
|
||||
IOS_MIN_SDK_VERSION="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--min-macos-sdk=*)
|
||||
MACOS_MIN_SDK_VERSION="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--min-watchos-sdk=*)
|
||||
WATCHOS_MIN_SDK_VERSION="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--min-tvos-sdk=*)
|
||||
TVOS_MIN_SDK_VERSION="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--noparallel)
|
||||
PARALLEL="false"
|
||||
;;
|
||||
--targets=*)
|
||||
TARGETS="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-v|--verbose)
|
||||
LOG_VERBOSE="verbose"
|
||||
;;
|
||||
--verbose-on-error)
|
||||
LOG_VERBOSE="verbose-on-error"
|
||||
;;
|
||||
--version=*)
|
||||
VERSION="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: ${i}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Don't mix version and branch
|
||||
if [[ -n "${VERSION}" && -n "${BRANCH}" ]]; then
|
||||
echo "Either select a branch (the script will determine and build the latest version) or select a specific version, but not both."
|
||||
exit 1
|
||||
|
||||
# Specific version: Verify version number format. Expected: dot notation
|
||||
elif [[ -n "${VERSION}" && ! "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+[a-z]*$ ]]; then
|
||||
echo "Unknown version number format. Examples: 1.0.2, 1.0.2h"
|
||||
exit 1
|
||||
|
||||
# Specific branch
|
||||
elif [ -n "${BRANCH}" ]; then
|
||||
# Verify version number format. Expected: dot notation
|
||||
if [[ ! "${BRANCH}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "Unknown branch version number format. Examples: 1.0.2, 1.1.1"
|
||||
exit 1
|
||||
|
||||
# Valid version number, determine latest version
|
||||
else
|
||||
echo "Checking latest version of ${BRANCH} branch on openssl.org..."
|
||||
# Get directory content listing of /source/ (only contains latest version per branch), limit list to archives (so one archive per branch),
|
||||
# filter for the requested branch, sort the list and get the last item (last two steps to ensure there is always 1 result)
|
||||
VERSION=$(curl ${CURL_OPTIONS} -s https://ftp.openssl.org/source/ | grep -Eo '>openssl-[0-9]\.[0-9]\.[0-9][a-z]*\.tar\.gz<' | grep -Eo "${BRANCH//./\.}[a-z]*" | sort | tail -1)
|
||||
|
||||
# Verify result
|
||||
if [ -z "${VERSION}" ]; then
|
||||
echo "Could not determine latest version, please check https://www.openssl.org/source/ and use --version option"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Script default
|
||||
elif [ -z "${VERSION}" ]; then
|
||||
VERSION="${DEFAULTVERSION}"
|
||||
fi
|
||||
|
||||
BUILD_TYPE="targets"
|
||||
|
||||
# Set default for TARGETS if not specified
|
||||
if [ ! -n "${TARGETS}" ]; then
|
||||
TARGETS="${DEFAULTTARGETS}"
|
||||
fi
|
||||
|
||||
# Add no-deprecated config option (if not overwritten)
|
||||
if [ "${CONFIG_NO_DEPRECATED}" != "false" ]; then
|
||||
CONFIG_OPTIONS="${CONFIG_OPTIONS} no-deprecated"
|
||||
fi
|
||||
|
||||
# Determine SDK versions
|
||||
if [ ! -n "${IOS_SDKVERSION}" ]; then
|
||||
IOS_SDKVERSION=$(xcrun -sdk iphoneos --show-sdk-version)
|
||||
fi
|
||||
if [ ! -n "${MACOS_SDKVERSION}" ]; then
|
||||
MACOS_SDKVERSION=$(xcrun -sdk macosx --show-sdk-version)
|
||||
fi
|
||||
if [ ! -n "${CATALYST_SDKVERSION}" ]; then
|
||||
CATALYST_SDKVERSION=$(xcrun -sdk macosx --show-sdk-version)
|
||||
fi
|
||||
if [ ! -n "${WATCHOS_SDKVERSION}" ]; then
|
||||
WATCHOS_SDKVERSION=$(xcrun -sdk watchos --show-sdk-version)
|
||||
fi
|
||||
if [ ! -n "${TVOS_SDKVERSION}" ]; then
|
||||
TVOS_SDKVERSION=$(xcrun -sdk appletvos --show-sdk-version)
|
||||
fi
|
||||
|
||||
# Truncate to minor version
|
||||
MINOR_VERSION=(${MACOS_SDKVERSION//./ })
|
||||
MACOS_SDKVERSION="${MINOR_VERSION[0]}.${MINOR_VERSION[1]}"
|
||||
MINOR_VERSION=(${CATALYST_SDKVERSION//./ })
|
||||
CATALYST_SDKVERSION="${MINOR_VERSION[0]}.${MINOR_VERSION[1]}"
|
||||
|
||||
# Determine number of cores for (parallel) build
|
||||
BUILD_THREADS=1
|
||||
if [ "${PARALLEL}" != "false" ]; then
|
||||
BUILD_THREADS=$(sysctl hw.ncpu | awk '{print $2}')
|
||||
fi
|
||||
|
||||
# Determine script directory
|
||||
SCRIPTDIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
|
||||
|
||||
# Write files relative to current location and validate directory
|
||||
CURRENTPATH=$(pwd)
|
||||
case "${CURRENTPATH}" in
|
||||
*\ * )
|
||||
echo "Your path contains whitespaces, which is not supported by 'make install'."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
cd "${CURRENTPATH}"
|
||||
|
||||
# Validate Xcode Developer path
|
||||
DEVELOPER=$(xcode-select -print-path)
|
||||
if [ ! -d "${DEVELOPER}" ]; then
|
||||
echo "Xcode path is not set correctly ${DEVELOPER} does not exist"
|
||||
echo "run"
|
||||
echo "sudo xcode-select -switch <Xcode path>"
|
||||
echo "for default installation:"
|
||||
echo "sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "${DEVELOPER}" in
|
||||
*\ * )
|
||||
echo "Your Xcode path contains whitespaces, which is not supported."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Show build options
|
||||
echo
|
||||
echo "Build options"
|
||||
echo " OpenSSL version: ${VERSION}"
|
||||
echo " Targets: ${TARGETS}"
|
||||
echo " iOS SDK: ${IOS_SDKVERSION} (min ${IOS_MIN_SDK_VERSION})"
|
||||
echo " macOS SDK: ${MACOS_SDKVERSION} (min ${MACOS_MIN_SDK_VERSION})"
|
||||
echo " macOS SDK (Catalyst): ${CATALYST_SDKVERSION} (min ${CATALYST_MIN_SDK_VERSION})"
|
||||
echo " watchOS SDK: ${WATCHOS_SDKVERSION} (min ${WATCHOS_MIN_SDK_VERSION})"
|
||||
echo " tvOS SDK: ${TVOS_SDKVERSION} (min ${TVOS_MIN_SDK_VERSION})"
|
||||
if [ "${CONFIG_DISABLE_BITCODE}" == "true" ]; then
|
||||
echo " Bitcode embedding disabled"
|
||||
fi
|
||||
echo " Number of make threads: ${BUILD_THREADS}"
|
||||
if [ -n "${CONFIG_OPTIONS}" ]; then
|
||||
echo " Configure options: ${CONFIG_OPTIONS}"
|
||||
fi
|
||||
echo " Build location: ${CURRENTPATH}"
|
||||
echo
|
||||
|
||||
# Download OpenSSL when not present
|
||||
OPENSSL_ARCHIVE_BASE_NAME="openssl-${VERSION}"
|
||||
OPENSSL_ARCHIVE_FILE_NAME="${OPENSSL_ARCHIVE_BASE_NAME}.tar.gz"
|
||||
OPENSSL_ARCHIVE_SIGNATURE_FILE_EXT=".asc"
|
||||
OPENSSL_ARCHIVE_SIGNATURE_FILE_NAME="${OPENSSL_ARCHIVE_FILE_NAME}${OPENSSL_ARCHIVE_SIGNATURE_FILE_EXT}"
|
||||
if [ ! -e ${OPENSSL_ARCHIVE_FILE_NAME} ]; then
|
||||
echo "Downloading ${OPENSSL_ARCHIVE_FILE_NAME}..."
|
||||
OPENSSL_ARCHIVE_BASE_URL="https://www.openssl.org/source"
|
||||
OPENSSL_ARCHIVE_URL="${OPENSSL_ARCHIVE_BASE_URL}/${OPENSSL_ARCHIVE_FILE_NAME}"
|
||||
|
||||
# Check whether file exists here (this is the location of the latest version for each branch)
|
||||
# -s be silent, -f return non-zero exit status on failure, -I get header (do not download)
|
||||
curl ${CURL_OPTIONS} -sfI "${OPENSSL_ARCHIVE_URL}" > /dev/null
|
||||
|
||||
# If unsuccessful, update the URL for older versions and try again.
|
||||
if [ $? -ne 0 ]; then
|
||||
BRANCH=$(echo "${VERSION}" | grep -Eo '^[0-9]\.[0-9]\.[0-9]')
|
||||
OPENSSL_ARCHIVE_URL="https://www.openssl.org/source/old/${BRANCH}/${OPENSSL_ARCHIVE_FILE_NAME}"
|
||||
|
||||
curl ${CURL_OPTIONS} -sfI "${OPENSSL_ARCHIVE_URL}" > /dev/null
|
||||
fi
|
||||
|
||||
# Both attempts failed, so report the error
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "An error occurred trying to find OpenSSL ${VERSION} on ${OPENSSL_ARCHIVE_URL}"
|
||||
echo "Please verify that the version you are trying to build exists, check cURL's error message and/or your network connection."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Archive was found, so proceed with download.
|
||||
# -O Use server-specified filename for download
|
||||
curl ${CURL_OPTIONS} -O "${OPENSSL_ARCHIVE_URL}"
|
||||
# also download the gpg signature from the same location
|
||||
curl ${CURL_OPTIONS} -O "${OPENSSL_ARCHIVE_URL}${OPENSSL_ARCHIVE_SIGNATURE_FILE_EXT}"
|
||||
|
||||
else
|
||||
echo "Using ${OPENSSL_ARCHIVE_FILE_NAME}"
|
||||
fi
|
||||
|
||||
# Validate archive signature
|
||||
if [ -e ${OPENSSL_ARCHIVE_SIGNATURE_FILE_NAME} ]; then
|
||||
gpg_validate "${OPENSSL_ARCHIVE_FILE_NAME}" "${OPENSSL_ARCHIVE_SIGNATURE_FILE_NAME}"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "WARN: GPG signature validation was unsuccessful."
|
||||
fi
|
||||
else
|
||||
echo "WARN: No GPG signature validation performed. (missing ${OPENSSL_ARCHIVE_SIGNATURE_FILE_NAME})"
|
||||
fi
|
||||
|
||||
# Set reference to custom configuration (OpenSSL 1.1.1)
|
||||
# See: https://github.com/openssl/openssl/commit/afce395cba521e395e6eecdaf9589105f61e4411
|
||||
export OPENSSL_LOCAL_CONFIG_DIR="${SCRIPTDIR}/config"
|
||||
|
||||
# -e Abort script at first error, when a command exits with non-zero status (except in until or while loops, if-tests, list constructs)
|
||||
# -o pipefail Causes a pipeline to return the exit status of the last command in the pipe that returned a non-zero return value
|
||||
set -eo pipefail
|
||||
|
||||
# Clean up target directories if requested and present
|
||||
if [ "${CLEANUP}" == "true" ]; then
|
||||
if [ -d "${CURRENTPATH}/bin" ]; then
|
||||
rm -r "${CURRENTPATH}/bin"
|
||||
fi
|
||||
if [ -d "${CURRENTPATH}/include/openssl" ]; then
|
||||
rm -r "${CURRENTPATH}/include/openssl"
|
||||
fi
|
||||
if [ -d "${CURRENTPATH}/lib" ]; then
|
||||
rm -r "${CURRENTPATH}/lib"
|
||||
fi
|
||||
if [ -d "${CURRENTPATH}/src" ]; then
|
||||
rm -r "${CURRENTPATH}/src"
|
||||
fi
|
||||
fi
|
||||
|
||||
# (Re-)create target directories
|
||||
mkdir -p "${CURRENTPATH}/bin"
|
||||
mkdir -p "${CURRENTPATH}/lib"
|
||||
mkdir -p "${CURRENTPATH}/src"
|
||||
|
||||
# Init vars for library references
|
||||
INCLUDE_DIR=""
|
||||
OPENSSLCONF_ALL=()
|
||||
LIBSSL_IOS=()
|
||||
LIBCRYPTO_IOS=()
|
||||
LIBSSL_MACOS=()
|
||||
LIBCRYPTO_MACOS=()
|
||||
LIBSSL_CATALYST=()
|
||||
LIBCRYPTO_CATALYST=()
|
||||
LIBSSL_WATCHOS=()
|
||||
LIBCRYPTO_WATCHOS=()
|
||||
LIBSSL_TVOS=()
|
||||
LIBCRYPTO_TVOS=()
|
||||
|
||||
source "${SCRIPTDIR}/scripts/build-loop-targets.sh"
|
||||
|
||||
# Copy include directory
|
||||
cp -R "${INCLUDE_DIR}" "${CURRENTPATH}/include/"
|
||||
|
||||
# Only create intermediate file when building for multiple targets
|
||||
# For a single target, opensslconf.h is still present in $INCLUDE_DIR (and has just been copied to the target include dir)
|
||||
if [ ${#OPENSSLCONF_ALL[@]} -gt 1 ]; then
|
||||
|
||||
# Prepare intermediate header file
|
||||
# This overwrites opensslconf.h that was copied from $INCLUDE_DIR
|
||||
OPENSSLCONF_INTERMEDIATE="${CURRENTPATH}/include/openssl/opensslconf.h"
|
||||
cp "${CURRENTPATH}/include/opensslconf-template.h" "${OPENSSLCONF_INTERMEDIATE}"
|
||||
|
||||
# Loop all header files
|
||||
LOOPCOUNT=0
|
||||
for OPENSSLCONF_CURRENT in "${OPENSSLCONF_ALL[@]}" ; do
|
||||
|
||||
# Copy specific opensslconf file to include dir
|
||||
cp "${CURRENTPATH}/bin/${OPENSSLCONF_CURRENT}" "${CURRENTPATH}/include/openssl"
|
||||
|
||||
# Determine define condition
|
||||
case "${OPENSSLCONF_CURRENT}" in
|
||||
*_ios_arm64.h)
|
||||
DEFINE_CONDITION="TARGET_OS_IOS && TARGET_OS_EMBEDDED && TARGET_CPU_ARM64"
|
||||
;;
|
||||
*_ios_arm64e.h)
|
||||
DEFINE_CONDITION="TARGET_OS_IOS && TARGET_OS_EMBEDDED && TARGET_CPU_ARM64E"
|
||||
;;
|
||||
*_ios_sim_x86_64.h)
|
||||
DEFINE_CONDITION="TARGET_OS_IOS && TARGET_OS_SIMULATOR && TARGET_CPU_X86_64"
|
||||
;;
|
||||
*_ios_sim_arm64.h)
|
||||
DEFINE_CONDITION="TARGET_OS_IOS && TARGET_OS_SIMULATOR && TARGET_CPU_ARM64"
|
||||
;;
|
||||
*_macos_x86_64.h)
|
||||
DEFINE_CONDITION="TARGET_OS_OSX && TARGET_CPU_X86_64"
|
||||
;;
|
||||
*_macos_arm64.h)
|
||||
DEFINE_CONDITION="TARGET_OS_OSX && TARGET_CPU_ARM64"
|
||||
;;
|
||||
*_catalyst_x86_64.h)
|
||||
DEFINE_CONDITION="(TARGET_OS_MACCATALYST || (TARGET_OS_IOS && TARGET_OS_SIMULATOR)) && TARGET_CPU_X86_64"
|
||||
;;
|
||||
*_catalyst_arm64.h)
|
||||
DEFINE_CONDITION="(TARGET_OS_MACCATALYST || (TARGET_OS_IOS && TARGET_OS_SIMULATOR)) && TARGET_CPU_ARM64"
|
||||
;;
|
||||
*_watchos_armv7k.h)
|
||||
DEFINE_CONDITION="TARGET_OS_WATCH && TARGET_OS_EMBEDDED && TARGET_CPU_ARM"
|
||||
;;
|
||||
*_watchos_arm64_32.h)
|
||||
DEFINE_CONDITION="TARGET_OS_WATCH && TARGET_OS_EMBEDDED && TARGET_CPU_ARM64"
|
||||
;;
|
||||
*_watchos_sim_x86_64.h)
|
||||
DEFINE_CONDITION="TARGET_OS_WATCH && TARGET_OS_SIMULATOR && TARGET_CPU_X86_64"
|
||||
;;
|
||||
*_watchos_sim_arm64.h)
|
||||
DEFINE_CONDITION="TARGET_OS_WATCH && TARGET_OS_SIMULATOR && TARGET_CPU_ARM64"
|
||||
;;
|
||||
*_watchos_sim_i386.h)
|
||||
DEFINE_CONDITION="TARGET_OS_WATCH && TARGET_OS_SIMULATOR && TARGET_CPU_X86"
|
||||
;;
|
||||
*_tvos_arm64.h)
|
||||
DEFINE_CONDITION="TARGET_OS_TV && TARGET_OS_EMBEDDED && TARGET_CPU_ARM64"
|
||||
;;
|
||||
*_tvos_sim_x86_64.h)
|
||||
DEFINE_CONDITION="TARGET_OS_TV && TARGET_OS_SIMULATOR && TARGET_CPU_X86_64"
|
||||
;;
|
||||
*)
|
||||
# Don't run into unexpected cases by setting the default condition to false
|
||||
DEFINE_CONDITION="0"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Determine loopcount; start with if and continue with elif
|
||||
LOOPCOUNT=$((LOOPCOUNT + 1))
|
||||
if [ ${LOOPCOUNT} -eq 1 ]; then
|
||||
echo "#if ${DEFINE_CONDITION}" >> "${OPENSSLCONF_INTERMEDIATE}"
|
||||
else
|
||||
echo "#elif ${DEFINE_CONDITION}" >> "${OPENSSLCONF_INTERMEDIATE}"
|
||||
fi
|
||||
|
||||
# Add include
|
||||
echo "# include <openssl/${OPENSSLCONF_CURRENT}>" >> "${OPENSSLCONF_INTERMEDIATE}"
|
||||
done
|
||||
|
||||
# Finish
|
||||
echo "#else" >> "${OPENSSLCONF_INTERMEDIATE}"
|
||||
echo '# error Unable to determine target or target not included in OpenSSL build' >> "${OPENSSLCONF_INTERMEDIATE}"
|
||||
echo "#endif" >> "${OPENSSLCONF_INTERMEDIATE}"
|
||||
fi
|
||||
|
||||
echo "Done."
|
||||
@@ -0,0 +1,153 @@
|
||||
## -*- mode: perl; -*-
|
||||
|
||||
my %targets = ();
|
||||
|
||||
%targets = (
|
||||
## Base settings for cross-compile
|
||||
# Based on 10-main.conf: iphoneos-cross
|
||||
# Add generic compiler flags
|
||||
# Add embed-bitcode option if SDK version is 9 or higher
|
||||
"all-base" => {
|
||||
template => 1,
|
||||
cflags => combine('-isysroot $(CROSS_TOP)/SDKs/$(CROSS_SDK) -fno-common',
|
||||
sub { ((!defined($ENV{'CONFIG_DISABLE_BITCODE'}) || $ENV{'CONFIG_DISABLE_BITCODE'} ne 'true') && defined($ENV{'SDKVERSION'}) && $ENV{'SDKVERSION'} =~ /^(9|[1-9][0-9]+)\./ && $disabled{shared})
|
||||
? '-fembed-bitcode' : (); },
|
||||
),
|
||||
},
|
||||
|
||||
## Base settings
|
||||
"ios-cross-base" => {
|
||||
inherit_from => [ "all-base" ],
|
||||
template => 1,
|
||||
cflags => add(sub { defined($ENV{'IOS_MIN_SDK_VERSION'}) ? '-mios-version-min=$(IOS_MIN_SDK_VERSION)' : '-mios-version-min=12.0'; }),
|
||||
},
|
||||
"macos-base" => {
|
||||
inherit_from => [ "all-base" ],
|
||||
template => 1,
|
||||
cflags => add(sub { defined($ENV{'MACOS_MIN_SDK_VERSION'}) ? '-mmacosx-version-min=$(MACOS_MIN_SDK_VERSION)' : '-mmacosx-version-min=10.15'; }),
|
||||
},
|
||||
"mac-catalyst-base" => {
|
||||
inherit_from => [ "all-base" ],
|
||||
template => 1,
|
||||
# cflags => add(sub { defined($ENV{'CATALYST_MIN_SDK_VERSION'}) ? '-mmacosx-version-min=$(CATALYST_MIN_SDK_VERSION)' : '-mmacosx-version-min=10.15'; }),
|
||||
},
|
||||
"watchos-cross-base" => {
|
||||
inherit_from => [ "all-base" ],
|
||||
template => 1,
|
||||
cflags => add(sub { defined($ENV{'WATCH_MIN_SDK_VERSION'}) ? '-mwatchos-version-min=$(WATCH_MIN_SDK_VERSION)' : '-mwatchos-version-min=4.0'; }),
|
||||
},
|
||||
"tvos-cross-base" => {
|
||||
inherit_from => [ "all-base" ],
|
||||
template => 1,
|
||||
cflags => add(sub { defined($ENV{'TVOS_MIN_SDK_VERSION'}) ? '-mtvos-version-min=$(TVOS_MIN_SDK_VERSION)' : '-mtvos-version-min=12.0'; }),
|
||||
defines => [ "HAVE_FORK=0" ],
|
||||
},
|
||||
|
||||
## Apple iOS
|
||||
|
||||
# Device
|
||||
"ios64-cross-arm64" => {
|
||||
inherit_from => [ "darwin-common", "ios-cross-base", asm("aarch64_asm") ],
|
||||
cflags => add("-arch arm64"),
|
||||
bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR",
|
||||
perlasm_scheme => "ios64",
|
||||
sys_id => "iOS",
|
||||
},
|
||||
"ios64-cross-arm64e" => {
|
||||
inherit_from => [ "darwin-common", "ios-cross-base", asm("aarch64_asm") ],
|
||||
cflags => add("-arch arm64e"),
|
||||
bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR",
|
||||
perlasm_scheme => "ios64",
|
||||
sys_id => "iOS",
|
||||
},
|
||||
# Simulator
|
||||
"ios-sim-cross-x86_64" => {
|
||||
inherit_from => [ "darwin64-x86_64-cc", "ios-cross-base" ],
|
||||
sys_id => "iOS",
|
||||
},
|
||||
"ios-sim-cross-arm64" => {
|
||||
inherit_from => [ "darwin64-arm64-cc", "ios-cross-base" ],
|
||||
cflags => add("-target arm64-apple-ios13.0-simulator -mios-version-min=13.0"),
|
||||
sys_id => "iOS",
|
||||
},
|
||||
|
||||
# Device
|
||||
"macos64-x86_64" => {
|
||||
inherit_from => [ "darwin64-x86_64-cc", "macos-base" ],
|
||||
sys_id => "MacOSX",
|
||||
},
|
||||
"macos64-arm64" => {
|
||||
inherit_from => [ "darwin64-arm64-cc", "macos-base" ],
|
||||
sys_id => "MacOSX",
|
||||
},
|
||||
# Catalyst
|
||||
"mac-catalyst-x86_64" => {
|
||||
inherit_from => [ "darwin64-x86_64-cc", "mac-catalyst-base" ],
|
||||
cflags => add("-target x86_64-apple-ios13.0-macabi -mios-version-min=13.0"),
|
||||
sys_id => "MacOSX",
|
||||
},
|
||||
"mac-catalyst-arm64" => {
|
||||
inherit_from => [ "darwin64-arm64-cc", "mac-catalyst-base" ],
|
||||
cflags => add("-target arm64-apple-ios13.0-macabi -mios-version-min=13.0"),
|
||||
sys_id => "MacOSX",
|
||||
},
|
||||
|
||||
## Apple WatchOS
|
||||
|
||||
# Device
|
||||
"watchos-cross-armv7k" => {
|
||||
inherit_from => [ "darwin-common", "watchos-cross-base", asm("armv7_asm") ],
|
||||
cflags => add("-arch armv7k -fembed-bitcode"),
|
||||
defines => [ "HAVE_FORK=0" ],
|
||||
sys_id => "WatchOS",
|
||||
},
|
||||
"watchos-cross-arm64_32" => {
|
||||
inherit_from => [ "darwin-common", "watchos-cross-base"],
|
||||
cflags => add("-arch arm64_32 -fembed-bitcode"),
|
||||
defines => [ "HAVE_FORK=0" ],
|
||||
sys_id => "WatchOS",
|
||||
},
|
||||
"watchos-cross-arm64" => {
|
||||
inherit_from => [ "darwin-common", "watchos-cross-base", asm("aarch64_asm") ],
|
||||
cflags => add("-arch arm64 -fembed-bitcode"),
|
||||
bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR",
|
||||
perlasm_scheme => "ios64",
|
||||
defines => [ "HAVE_FORK=0" ],
|
||||
sys_id => "WatchOS",
|
||||
},
|
||||
# Simulator
|
||||
"watchos-sim-cross-x86_64" => {
|
||||
inherit_from => [ "darwin64-x86_64-cc", "watchos-cross-base"],
|
||||
cflags => add("-fembed-bitcode"),
|
||||
defines => [ "HAVE_FORK=0" ],
|
||||
sys_id => "WatchOS",
|
||||
},
|
||||
"watchos-sim-cross-arm64" => {
|
||||
inherit_from => [ "darwin64-arm64-cc", "watchos-cross-base"],
|
||||
cflags => add("-target arm64-apple-watchos7.2-simulator -mwatchos-version-min=7.2 -fembed-bitcode"),
|
||||
defines => [ "HAVE_FORK=0" ],
|
||||
sys_id => "WatchOS",
|
||||
},
|
||||
"watchos-sim-cross-i386" => {
|
||||
inherit_from => [ "darwin-common", "watchos-cross-base"],
|
||||
cflags => add("-arch i386 -fembed-bitcode"),
|
||||
defines => [ "HAVE_FORK=0" ],
|
||||
sys_id => "WatchOS",
|
||||
},
|
||||
|
||||
## Apple TV
|
||||
|
||||
# Device
|
||||
"tvos64-cross-arm64" => {
|
||||
inherit_from => [ "darwin-common", "tvos-cross-base", asm("aarch64_asm") ],
|
||||
cflags => add("-arch arm64"),
|
||||
bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR",
|
||||
perlasm_scheme => "ios64",
|
||||
sys_id => "tvOS",
|
||||
},
|
||||
# Simulator
|
||||
"tvos-sim-cross-x86_64" => {
|
||||
inherit_from => [ "darwin64-x86_64-cc", "tvos-cross-base" ],
|
||||
sys_id => "tvOS",
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,291 @@
|
||||
#!/bin/bash
|
||||
source scripts/get-openssl-version.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ $# == 0 ]; then
|
||||
echo "Usage: `basename $0` static|dynamic"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d lib ]; then
|
||||
echo "Please run build-libssl.sh first!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FWTYPE=$1
|
||||
FWNAME=openssl
|
||||
FWROOT=frameworks
|
||||
|
||||
if [ -d $FWROOT ]; then
|
||||
echo "Removing previous $FWNAME.framework copies"
|
||||
rm -rf $FWROOT
|
||||
fi
|
||||
|
||||
#ALL_SYSTEMS=("iPhone" "AppleTV" "MacOSX" "Catalyst" "Watch")
|
||||
ALL_SYSTEMS=("iPhoneOS" "iPhoneSimulator" "AppleTVOS" "AppleTVSimulator" "MacOSX" "Catalyst" "WatchOS" "WatchSimulator")
|
||||
|
||||
function check_bitcode() {
|
||||
local BITCODE_PATH=$1
|
||||
local IS_VERBOSE=$2
|
||||
|
||||
if [[ $FWTYPE == "dynamic" ]]; then
|
||||
BITCODE_PATTERN="__LLVM"
|
||||
else
|
||||
BITCODE_PATTERN="__bitcode"
|
||||
fi
|
||||
|
||||
if otool -l "$BITCODE_PATH" | grep "${BITCODE_PATTERN}" >/dev/null; then
|
||||
if $IS_VERBOSE; then
|
||||
echo "INFO: $BITCODE_PATH contains Bitcode"
|
||||
fi
|
||||
BITCODE_ENABLED=1
|
||||
else
|
||||
if $IS_VERBOSE; then
|
||||
echo "INFO: $BITCODE_PATH doesn't contain Bitcode"
|
||||
fi
|
||||
BITCODE_ENABLED=0
|
||||
fi
|
||||
}
|
||||
|
||||
# Inspect Mach-O load commands to get minimum SDK version.
|
||||
#
|
||||
# Depending on the actual minimum SDK version it may look like this
|
||||
# (for modern SDKs):
|
||||
#
|
||||
# Load command 1
|
||||
# cmd LC_BUILD_VERSION
|
||||
# cmdsize 24
|
||||
# platform 8
|
||||
# sdk 13.2 <-- target SDK
|
||||
# minos 12.0 <-- minimum SDK
|
||||
# ntools 0
|
||||
#
|
||||
# Or like this for older versions, with a platform-dependent tag:
|
||||
#
|
||||
# Load command 1
|
||||
# cmd LC_VERSION_MIN_WATCHOS
|
||||
# cmdsize 16
|
||||
# version 4.0 <-- minimum SDK
|
||||
# sdk 6.1 <-- target SDK
|
||||
function get_min_sdk() {
|
||||
local file=$1
|
||||
set +o pipefail
|
||||
otool -l "$file" | awk "
|
||||
/^Load command/ {
|
||||
last_command = \"\"
|
||||
}
|
||||
\$1 == \"cmd\" {
|
||||
last_command = \$2
|
||||
}
|
||||
(last_command ~ /LC_BUILD_VERSION/ && \$1 == \"minos\") ||
|
||||
(last_command ~ /^LC_VERSION_MIN_/ && \$1 == \"version\") {
|
||||
print \$2
|
||||
exit
|
||||
}
|
||||
"
|
||||
set -o pipefail
|
||||
}
|
||||
|
||||
# Read OpenSSL version from opensslv.h file.
|
||||
#
|
||||
# In modern OpenSSL releases the version line looks like this:
|
||||
#
|
||||
# # define OPENSSL_VERSION_TEXT "OpenSSL 1.1.1g 21 Apr 2020"
|
||||
#
|
||||
# But for older versions with FIPS module it may look like this:
|
||||
#
|
||||
# # ifdef OPENSSL_FIPS
|
||||
# # define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2u-fips 20 Dec 2019"
|
||||
# # else
|
||||
# # define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2u 20 Dec 2019"
|
||||
# # endif
|
||||
#
|
||||
# For App Store validation purposes, replace trailing letter with
|
||||
# 2-digit offset from 'a' (ASCII 97), plus 1 for 1-based
|
||||
#
|
||||
# 1.0.2u
|
||||
# 'u' = 117 -> 20 + 1 = 21
|
||||
# 1.0.221
|
||||
#
|
||||
# 1.1.1g
|
||||
# 'g' = 103 -> 6 + 1 = 07 (zero-padded)
|
||||
# 1.1.107
|
||||
#
|
||||
function get_openssl_version_from_file() {
|
||||
local opensslv=$1
|
||||
local std_version=$(awk '/define OPENSSL_VERSION_TEXT/ && !/-fips/ {print $5}' "$opensslv")
|
||||
echo $(get_openssl_version $std_version)
|
||||
}
|
||||
|
||||
if [ $FWTYPE == "dynamic" ]; then
|
||||
DEVELOPER=`xcode-select -print-path`
|
||||
FW_EXEC_NAME="${FWNAME}.framework/${FWNAME}"
|
||||
INSTALL_NAME="@rpath/${FW_EXEC_NAME}"
|
||||
COMPAT_VERSION="1.0.0"
|
||||
CURRENT_VERSION="1.0.0"
|
||||
|
||||
RX='([A-z]+)([0-9]+(\.[0-9]+)*)-([A-z0-9_]+)\.sdk'
|
||||
|
||||
cd bin
|
||||
for TARGETDIR in `ls -d *.sdk`; do
|
||||
if [[ $TARGETDIR =~ $RX ]]; then
|
||||
PLATFORM="${BASH_REMATCH[1]}"
|
||||
SDKVERSION="${BASH_REMATCH[2]}"
|
||||
ARCH="${BASH_REMATCH[4]}"
|
||||
fi
|
||||
|
||||
echo "Assembling .dylib for $PLATFORM $SDKVERSION ($ARCH)"
|
||||
|
||||
MIN_SDK_VERSION=$(get_min_sdk "${TARGETDIR}/lib/libcrypto.a")
|
||||
if [[ $PLATFORM == AppleTVSimulator* ]]; then
|
||||
MIN_SDK="-tvos_simulator_version_min $MIN_SDK_VERSION"
|
||||
elif [[ $PLATFORM == AppleTV* ]]; then
|
||||
MIN_SDK="-tvos_version_min $MIN_SDK_VERSION"
|
||||
elif [[ $PLATFORM == MacOSX* ]]; then
|
||||
MIN_SDK="-macosx_version_min $MIN_SDK_VERSION"
|
||||
elif [[ $PLATFORM == Catalyst* ]]; then
|
||||
MIN_SDK="-platform_version mac-catalyst $MIN_SDK_VERSION $MIN_SDK_VERSION"
|
||||
PLATFORM="MacOSX"
|
||||
elif [[ $PLATFORM == iPhoneSimulator* ]]; then
|
||||
MIN_SDK="-ios_simulator_version_min $MIN_SDK_VERSION"
|
||||
elif [[ $PLATFORM == WatchOS* ]]; then
|
||||
MIN_SDK="-watchos_version_min $MIN_SDK_VERSION"
|
||||
elif [[ $PLATFORM == WatchSimulator* ]]; then
|
||||
MIN_SDK="-watchos_simulator_version_min $MIN_SDK_VERSION"
|
||||
else
|
||||
MIN_SDK="-ios_version_min $MIN_SDK_VERSION"
|
||||
fi
|
||||
|
||||
CROSS_TOP="${DEVELOPER}/Platforms/${PLATFORM}.platform/Developer"
|
||||
CROSS_SDK="${PLATFORM}${SDKVERSION}.sdk"
|
||||
SDK="${CROSS_TOP}/SDKs/${CROSS_SDK}"
|
||||
|
||||
#cd $TARGETDIR
|
||||
#libtool -dynamic -lSystem $MIN_SDK -syslibroot $SDK -install_name $INSTALL_NAME -compatibility_version $COMPAT_VERSION -current_version $CURRENT_VERSION lib/*.a -o $FWNAME.dylib
|
||||
|
||||
TARGETOBJ="${TARGETDIR}/obj"
|
||||
rm -rf $TARGETOBJ
|
||||
mkdir $TARGETOBJ
|
||||
cd $TARGETOBJ
|
||||
ar -x ../lib/libcrypto.a
|
||||
ar -x ../lib/libssl.a
|
||||
cd ..
|
||||
|
||||
BUNDLE_BITCODE=""
|
||||
check_bitcode "lib/libssl.a" false
|
||||
if [[ $BITCODE_ENABLED == 1 ]]; then
|
||||
BUNDLE_BITCODE="-bitcode_bundle"
|
||||
fi
|
||||
|
||||
ld obj/*.o \
|
||||
-dylib \
|
||||
$BUNDLE_BITCODE \
|
||||
-lSystem \
|
||||
-arch $ARCH \
|
||||
$MIN_SDK \
|
||||
-syslibroot $SDK \
|
||||
-compatibility_version $COMPAT_VERSION \
|
||||
-current_version $CURRENT_VERSION \
|
||||
-application_extension \
|
||||
-o $FWNAME.dylib
|
||||
install_name_tool -id $INSTALL_NAME $FWNAME.dylib
|
||||
|
||||
cd ..
|
||||
done
|
||||
cd ..
|
||||
|
||||
for SYS in ${ALL_SYSTEMS[@]}; do
|
||||
SYSDIR="$FWROOT/$SYS"
|
||||
FWDIR="$SYSDIR/$FWNAME.framework"
|
||||
DYLIBS=(bin/${SYS}*/$FWNAME.dylib)
|
||||
|
||||
if [[ ${#DYLIBS[@]} -gt 0 && -e ${DYLIBS[0]} ]]; then
|
||||
echo "Creating framework for $SYS"
|
||||
mkdir -p $FWDIR/Headers
|
||||
lipo -create ${DYLIBS[@]} -output $FWDIR/$FWNAME
|
||||
cp -r include/$FWNAME/* $FWDIR/Headers/
|
||||
cp -L assets/$SYS/Info.plist $FWDIR/Info.plist
|
||||
MIN_SDK_VERSION=$(get_min_sdk "$FWDIR/$FWNAME")
|
||||
OPENSSL_VERSION=$(get_openssl_version_from_file "$FWDIR/Headers/opensslv.h")
|
||||
sed -e "s/\\\$(MIN_SDK_VERSION)/$MIN_SDK_VERSION/g" \
|
||||
-e "s/\\\$(OPENSSL_VERSION)/$OPENSSL_VERSION/g" \
|
||||
-i '' "$FWDIR/Info.plist"
|
||||
echo "Created $FWDIR"
|
||||
check_bitcode "$FWDIR/$FWNAME" true
|
||||
else
|
||||
echo "Skipped framework for $SYS"
|
||||
fi
|
||||
done
|
||||
|
||||
rm bin/*/$FWNAME.dylib
|
||||
else
|
||||
for SYS in ${ALL_SYSTEMS[@]}; do
|
||||
SYSDIR="$FWROOT/$SYS"
|
||||
FWDIR="$SYSDIR/$FWNAME.framework"
|
||||
LIBS_CRYPTO=(bin/${SYS}*/lib/libcrypto.a)
|
||||
LIBS_SSL=(bin/${SYS}*/lib/libssl.a)
|
||||
|
||||
if [[ ${#LIBS_CRYPTO[@]} -gt 0 && -e ${LIBS_CRYPTO[0]} && ${#LIBS_SSL[@]} -gt 0 && -e ${LIBS_SSL[0]} ]]; then
|
||||
echo "Creating framework for $SYS"
|
||||
mkdir -p $FWDIR/lib
|
||||
lipo -create ${LIBS_CRYPTO[@]} -output $FWDIR/lib/libcrypto.a
|
||||
lipo -create ${LIBS_SSL[@]} -output $FWDIR/lib/libssl.a
|
||||
libtool -static -o $FWDIR/$FWNAME $FWDIR/lib/*.a
|
||||
rm -rf $FWDIR/lib
|
||||
mkdir -p $FWDIR/Headers
|
||||
cp -r include/$FWNAME/* $FWDIR/Headers/
|
||||
cp -L assets/$SYS/Info.plist $FWDIR/Info.plist
|
||||
MIN_SDK_VERSION=$(get_min_sdk "$FWDIR/$FWNAME")
|
||||
OPENSSL_VERSION=$(get_openssl_version_from_file "$FWDIR/Headers/opensslv.h")
|
||||
sed -e "s/\\\$(MIN_SDK_VERSION)/$MIN_SDK_VERSION/g" \
|
||||
-e "s/\\\$(OPENSSL_VERSION)/$OPENSSL_VERSION/g" \
|
||||
-i '' "$FWDIR/Info.plist"
|
||||
echo "Created $FWDIR"
|
||||
check_bitcode "$FWDIR/$FWNAME" true
|
||||
else
|
||||
echo "Skipped framework for $SYS"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# macOS and Catalyst symlinks
|
||||
for SYS in ${ALL_SYSTEMS[@]}; do
|
||||
if [[ $SYS == "MacOSX" || $SYS == "Catalyst" ]]; then
|
||||
SYSDIR="$FWROOT/$SYS"
|
||||
FWDIR="$SYSDIR/$FWNAME.framework"
|
||||
if [[ ! -e "$FWDIR" ]]; then
|
||||
continue
|
||||
fi
|
||||
cd $FWDIR
|
||||
|
||||
mkdir "Versions"
|
||||
mkdir "Versions/A"
|
||||
mkdir "Versions/A/Resources"
|
||||
mv "openssl" "Headers" "Versions/A"
|
||||
mv "Info.plist" "Versions/A/Resources"
|
||||
|
||||
(cd "Versions" && ln -s "A" "Current")
|
||||
ln -s "Versions/Current/openssl"
|
||||
ln -s "Versions/Current/Headers"
|
||||
ln -s "Versions/Current/Resources"
|
||||
|
||||
cd ../../..
|
||||
fi
|
||||
done
|
||||
|
||||
build_xcframework() {
|
||||
local FRAMEWORKS=($FWROOT/*/$FWNAME.framework)
|
||||
local ARGS=
|
||||
for ARG in ${FRAMEWORKS[@]}; do
|
||||
ARGS+="-framework ${ARG} "
|
||||
done
|
||||
|
||||
echo
|
||||
xcodebuild -create-xcframework $ARGS -output "$FWROOT/$FWNAME.xcframework"
|
||||
|
||||
# These intermediate frameworks are silly, and not needed any more.
|
||||
#find ${FWROOT} -mindepth 1 -maxdepth 1 -type d -not -name "$FWNAME.xcframework" -exec rm -rf '{}' \;
|
||||
}
|
||||
|
||||
build_xcframework
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
|
||||
LICENSE ISSUES
|
||||
==============
|
||||
|
||||
The OpenSSL toolkit stays under a dual license, i.e. both the conditions of
|
||||
the OpenSSL License and the original SSLeay license apply to the toolkit.
|
||||
See below for the actual license texts. Actually both licenses are BSD-style
|
||||
Open Source licenses. In case of any license issues related to OpenSSL
|
||||
please contact openssl-core@openssl.org.
|
||||
|
||||
OpenSSL License
|
||||
---------------
|
||||
|
||||
/* ====================================================================
|
||||
* Copyright (c) 1998-2008 The OpenSSL Project. All rights reserved.
|
||||
*
|
||||
* 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 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.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
|
||||
*
|
||||
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please contact
|
||||
* openssl-core@openssl.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "OpenSSL"
|
||||
* nor may "OpenSSL" appear in their names without prior written
|
||||
* permission of the OpenSSL Project.
|
||||
*
|
||||
* 6. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
|
||||
* EXPRESSED 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 OpenSSL PROJECT OR
|
||||
* ITS 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.
|
||||
* ====================================================================
|
||||
*
|
||||
* This product includes cryptographic software written by Eric Young
|
||||
* (eay@cryptsoft.com). This product includes software written by Tim
|
||||
* Hudson (tjh@cryptsoft.com).
|
||||
*
|
||||
*/
|
||||
|
||||
Original SSLeay License
|
||||
-----------------------
|
||||
|
||||
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* This package is an SSL implementation written
|
||||
* by Eric Young (eay@cryptsoft.com).
|
||||
* The implementation was written so as to conform with Netscapes SSL.
|
||||
*
|
||||
* This library is free for commercial and non-commercial use as long as
|
||||
* the following conditions are aheared to. The following conditions
|
||||
* apply to all code found in this distribution, be it the RC4, RSA,
|
||||
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
|
||||
* included with this distribution is covered by the same copyright terms
|
||||
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
|
||||
*
|
||||
* Copyright remains Eric Young's, and as such any Copyright notices in
|
||||
* the code are not to be removed.
|
||||
* If this package is used in a product, Eric Young should be given attribution
|
||||
* as the author of the parts of the library used.
|
||||
* This can be in the form of a textual message at program startup or
|
||||
* in documentation (online or textual) provided with the package.
|
||||
*
|
||||
* 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 copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* "This product includes cryptographic software written by
|
||||
* Eric Young (eay@cryptsoft.com)"
|
||||
* The word 'cryptographic' can be left out if the rouines from the library
|
||||
* being used are not cryptographic related :-).
|
||||
* 4. If you include any Windows specific code (or a derivative thereof) from
|
||||
* the apps directory (application code) you must include an acknowledgement:
|
||||
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
|
||||
*
|
||||
* The licence and distribution terms for any publically available version or
|
||||
* derivative of this code cannot be changed. i.e. this code cannot simply be
|
||||
* copied and put under another distribution licence
|
||||
* [including the GNU Public Licence.]
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Building OpenSSL for the different architectures of all iOS and tvOS devices requires different settings.
|
||||
* In order to be able to use assembly code on all devices, the choice was made to keep optimal settings for all
|
||||
* devices and use this intermediate header file to use the proper opensslconf.h file for each architecture.
|
||||
|
||||
* See also https://github.com/x2on/OpenSSL-for-iPhone/issues/126 and referenced pull requests
|
||||
*/
|
||||
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,117 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Automatic build script for libssl and libcrypto
|
||||
# for iPhoneOS and iPhoneSimulator
|
||||
#
|
||||
# Created by Felix Schulze on 16.12.10.
|
||||
# Copyright 2010-2016 Felix Schulze. All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
for TARGET in ${TARGETS}
|
||||
do
|
||||
# Determine relevant SDK version
|
||||
if [[ "${TARGET}" == macos* ]]; then
|
||||
SDKVERSION="${MACOS_SDKVERSION}"
|
||||
elif [[ "${TARGET}" == mac-catalyst-* ]]; then
|
||||
SDKVERSION="${CATALYST_SDKVERSION}"
|
||||
elif [[ "${TARGET}" == watchos* ]]; then
|
||||
SDKVERSION="${WATCHOS_SDKVERSION}"
|
||||
elif [[ "${TARGET}" == tvos* ]]; then
|
||||
SDKVERSION="${TVOS_SDKVERSION}"
|
||||
else
|
||||
SDKVERSION="${IOS_SDKVERSION}"
|
||||
fi
|
||||
|
||||
# These variables are used in the configuration file
|
||||
export SDKVERSION
|
||||
export IOS_MIN_SDK_VERSION
|
||||
export MACOS_MIN_SDK_VERSION
|
||||
export CATALYST_MIN_SDK_VERSION
|
||||
export WATCHOS_MIN_SDK_VERSION
|
||||
export TVOS_MIN_SDK_VERSION
|
||||
export CONFIG_DISABLE_BITCODE
|
||||
|
||||
# Determine platform
|
||||
if [[ "${TARGET}" == "macos"* ]]; then
|
||||
PLATFORM="MacOSX"
|
||||
if [[ "${TARGET}" == "macos64-arm64" ]]; then
|
||||
MACOS_MIN_SDK_VERSION="11.0"
|
||||
fi
|
||||
elif [[ "${TARGET}" == "mac-catalyst-"* ]]; then
|
||||
PLATFORM="MacOSX"
|
||||
elif [[ "${TARGET}" == "watchos-sim-cross"* ]]; then
|
||||
PLATFORM="WatchSimulator"
|
||||
elif [[ "${TARGET}" == "watchos"* ]]; then
|
||||
PLATFORM="WatchOS"
|
||||
elif [[ "${TARGET}" == "tvos-sim-cross-"* ]]; then
|
||||
PLATFORM="AppleTVSimulator"
|
||||
elif [[ "${TARGET}" == "tvos64-cross-"* ]]; then
|
||||
PLATFORM="AppleTVOS"
|
||||
elif [[ "${TARGET}" == "ios-sim-cross-"* ]]; then
|
||||
PLATFORM="iPhoneSimulator"
|
||||
else
|
||||
PLATFORM="iPhoneOS"
|
||||
fi
|
||||
|
||||
# Extract ARCH from TARGET (part after last dash)
|
||||
ARCH=$(echo "${TARGET}" | sed -E 's|^.*\-([^\-]+)$|\1|g')
|
||||
|
||||
# Cross compile references, see Configurations/10-main.conf
|
||||
export CROSS_COMPILE="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain/usr/bin/"
|
||||
export CROSS_TOP="${DEVELOPER}/Platforms/${PLATFORM}.platform/Developer"
|
||||
export CROSS_SDK="${PLATFORM}${SDKVERSION}.sdk"
|
||||
|
||||
# Prepare TARGETDIR and SOURCEDIR
|
||||
PLATFORM="${PLATFORM}"
|
||||
if [[ "${TARGET}" == "mac-catalyst-"* ]]; then
|
||||
PLATFORM="Catalyst"
|
||||
fi
|
||||
prepare_target_source_dirs
|
||||
|
||||
## Determine config options
|
||||
# Add build target, --prefix and prevent async (references to getcontext(),
|
||||
# setcontext() and makecontext() result in App Store rejections) and creation
|
||||
# of shared libraries (default since 1.1.0)
|
||||
LOCAL_CONFIG_OPTIONS="${TARGET} --prefix=${TARGETDIR} ${CONFIG_OPTIONS} no-async no-shared"
|
||||
|
||||
# Only relevant for 64 bit builds
|
||||
if [[ "${CONFIG_ENABLE_EC_NISTP_64_GCC_128}" == "true" && "${ARCH}" == *64 ]]; then
|
||||
LOCAL_CONFIG_OPTIONS="${LOCAL_CONFIG_OPTIONS} enable-ec_nistp_64_gcc_128"
|
||||
fi
|
||||
|
||||
# openssl-1.1.1 tries to use an unguarded fork(), affecting AppleTVOS and WatchOS.
|
||||
# Luckily this is only present in the testing suite and can be built without it.
|
||||
if [[ $PLATFORM == "AppleTV"* || $PLATFORM == "Watch"* ]]; then
|
||||
LOCAL_CONFIG_OPTIONS="${LOCAL_CONFIG_OPTIONS} no-tests"
|
||||
fi
|
||||
|
||||
# Run Configure
|
||||
run_configure
|
||||
|
||||
# Run make
|
||||
run_make
|
||||
|
||||
# Run make install
|
||||
set -e
|
||||
if [ "${LOG_VERBOSE}" == "verbose" ]; then
|
||||
make install_dev | tee -a "${LOG}"
|
||||
else
|
||||
make install_dev >> "${LOG}" 2>&1
|
||||
fi
|
||||
|
||||
# Remove source dir, add references to library files to relevant arrays
|
||||
# Keep reference to first build target for include file
|
||||
finish_build_loop
|
||||
done
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
function get_openssl_version() {
|
||||
local std_version=$1
|
||||
local script_version=${2:-}
|
||||
local generic_version=${std_version%?}
|
||||
local subpatch=${std_version: -1}
|
||||
local subpatch_number=$(($(printf '%d' \'$subpatch) - 97 + 1))
|
||||
subpatch_number="$(printf '%02d' $subpatch_number)"
|
||||
script_version="$(printf '%02d' $script_version)"
|
||||
local normalized_version="${generic_version}${subpatch_number}${script_version}"
|
||||
echo $normalized_version
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.pbxuser
|
||||
**/*.xcworkspace/xcuserdata
|
||||
**/*.xcodeproj/project.xcworkspace
|
||||
**/*.xcodeproj/xcuserdata
|
||||
Pods
|
||||
docs
|
||||
build
|
||||
.swiftpm
|
||||
.build
|
||||
@@ -0,0 +1 @@
|
||||
2.7
|
||||
@@ -0,0 +1,531 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- OpenVPN: Parse authentication requirement from `--auth-user-pass`.
|
||||
- OpenVPN: Handle multiple `--remote` options correctly.
|
||||
|
||||
### Changed
|
||||
|
||||
- Manager package completely rewritten.
|
||||
- WireGuard: Use entities from WireGuardKit directly.
|
||||
- Only enable on-demand if at least one rule is provided.
|
||||
- Dropped incomplete support for IPSec/IKEv2.
|
||||
|
||||
## 4.1.0 (2022-02-09)
|
||||
|
||||
### Added
|
||||
|
||||
- WireGuard support. [#236](https://github.com/passepartoutvpn/tunnelkit/pull/236)
|
||||
- Handle `--keepalive` option.
|
||||
|
||||
### Changed
|
||||
|
||||
- Relax deployment target for macOS down to 10.14
|
||||
- Upgrade OpenSSL to 1.1.1m.
|
||||
|
||||
## 4.0.3 (2021-11-27)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Verify CA from on-disk file. [#237](https://github.com/passepartoutvpn/tunnelkit/pull/237)
|
||||
|
||||
## 4.0.2 (2021-11-25)
|
||||
|
||||
### Changed
|
||||
|
||||
- Revert to OpenSSL. [#233](https://github.com/passepartoutvpn/tunnelkit/pull/233)
|
||||
|
||||
### Fixed
|
||||
|
||||
- TLS fails on CA verification on some servers. [#232](https://github.com/passepartoutvpn/tunnelkit/issues/232)
|
||||
- TLS negotiation times out with ProtonVPN. [#230](https://github.com/passepartoutvpn/tunnelkit/issues/230)
|
||||
|
||||
## 4.0.1 (2021-11-18)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Regression in TLS handshake (temporarily revert [#213](https://github.com/passepartoutvpn/tunnelkit/pull/213)).
|
||||
|
||||
## 4.0.0 (2021-11-16)
|
||||
|
||||
### Changed
|
||||
|
||||
- Migrate to SwiftPM. [#210](https://github.com/passepartoutvpn/tunnelkit/issues/210)
|
||||
- Replace OpenSSL with BoringSSL from SwiftNIO SSL.
|
||||
- Drop support for TLS security level (not present in BoringSSL).
|
||||
|
||||
## 3.5.0 (2021-10-18)
|
||||
|
||||
### Added
|
||||
|
||||
- Support for IPSec/IKEv2 providers.
|
||||
|
||||
### Changed
|
||||
|
||||
- Avoid caching PEMs on disk (roop). [#213](https://github.com/passepartoutvpn/tunnelkit/pull/213)
|
||||
- Upgrade OpenSSL to 1.1.1l.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Avoid caching PEMs on disk. [#213](https://github.com/passepartoutvpn/tunnelkit/pull/213)
|
||||
|
||||
## 3.4.0 (2021-08-07)
|
||||
|
||||
### Added
|
||||
|
||||
- Support for XOR patch (Sam Foxman). [#170](https://github.com/passepartoutvpn/tunnelkit/pull/170)
|
||||
|
||||
## 3.3.3 (2021-07-19)
|
||||
|
||||
### Added
|
||||
|
||||
- Support for `--compress stub-v2`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Return error in install completion handler. [#206](https://github.com/passepartoutvpn/tunnelkit/issues/206)
|
||||
- Relax handling of whitespaces in configuration file.
|
||||
|
||||
## 3.3.2 (2021-06-26)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Clean up cached PEMs at the end of a Session. [#203](https://github.com/passepartoutvpn/tunnelkit/pull/203)
|
||||
|
||||
## 3.3.1 (2021-02-12)
|
||||
|
||||
### Changed
|
||||
|
||||
- Skip keychain password prompt on macOS. [#200](https://github.com/passepartoutvpn/tunnelkit/pull/200)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Restore app group in keychain queries about password references. [#201](https://github.com/passepartoutvpn/tunnelkit/pull/201)
|
||||
|
||||
## 3.3.0 (2021-01-28)
|
||||
|
||||
### Added
|
||||
|
||||
- Handle `--data-ciphers` and `data-ciphers-fallback` from OpenVPN 2.5
|
||||
- Support DNS over HTTPS (DoH) and TLS (DoT).
|
||||
|
||||
### Changed
|
||||
|
||||
- Pick tunnel password reference from an existing keychain item context.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Do not override network DNS settings when not provided by VPN. [#197](https://github.com/passepartoutvpn/tunnelkit/issues/197)
|
||||
|
||||
## 3.2.0 (2021-01-07)
|
||||
|
||||
### Changed
|
||||
|
||||
- Encoding of internal provider configuration.
|
||||
|
||||
## 3.1.0 (2020-12-28)
|
||||
|
||||
### Added
|
||||
|
||||
- Parse `--tun-mtu` option.
|
||||
|
||||
### Changed
|
||||
|
||||
- Update API to access current Wi-Fi SSID.
|
||||
- Refactor access to keychain.
|
||||
|
||||
## 3.0.0 (2020-11-15)
|
||||
|
||||
### Added
|
||||
|
||||
- Support for Apple Silicon (macOS arm64).
|
||||
- Customize IV_UI_VER (pahnev). [#178](https://github.com/passepartoutvpn/tunnelkit/pull/178)
|
||||
|
||||
### Changed
|
||||
|
||||
- Deployment targets raised to iOS 12.0 and macOS 10.15
|
||||
- Use active profile name in VPN configuration (device settings).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Incorrect tunnel bundle identifiers in Demo. [#176](https://github.com/passepartoutvpn/tunnelkit/issues/176)
|
||||
- IV_PLAT in peer info was hardcoded to "mac" (pahnev). [#177](https://github.com/passepartoutvpn/tunnelkit/pull/177)
|
||||
|
||||
# 2.2.7 (2020-06-11)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Code cleanup.
|
||||
|
||||
## 2.2.6 (2020-05-12)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Address concerns from Guido Vranken fuzzers. [#141](https://github.com/passepartoutvpn/tunnelkit/pull/141)
|
||||
|
||||
## 2.2.5 (2020-05-12)
|
||||
|
||||
### Changed
|
||||
|
||||
- Improve IP Header parsing (roop). [#171](https://github.com/passepartoutvpn/tunnelkit/pull/171)
|
||||
|
||||
## 2.2.4 (2020-05-10)
|
||||
|
||||
### Added
|
||||
|
||||
- Support for SAN hostname in certificates (jaroslavas). [#168](https://github.com/passepartoutvpn/tunnelkit/pull/168)
|
||||
|
||||
### Fixed
|
||||
|
||||
- IPv6 traffic broken on Mojave. [#146](https://github.com/passepartoutvpn/tunnelkit/issues/146), [#169](https://github.com/passepartoutvpn/tunnelkit/pull/169)
|
||||
- Restore tunnel MTU setting (ueshiba). [#148](https://github.com/passepartoutvpn/tunnelkit/pull/148)
|
||||
- Transient connected state upon connection failure (rob-patchett). [#128](https://github.com/passepartoutvpn/tunnelkit/pull/128)
|
||||
|
||||
## 2.2.3 (2020-04-21)
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade OpenSSL to 1.1.1g. [#166](https://github.com/passepartoutvpn/tunnelkit/issues/166)
|
||||
|
||||
## 2.2.2 (2020-04-20)
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade OpenSSL to 1.1.1f. [#165](https://github.com/passepartoutvpn/tunnelkit/issues/165)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Index out of range during negotiation (Grivus). [#143](https://github.com/passepartoutvpn/tunnelkit/pull/143)
|
||||
- Handle server shutdown/restart (remote `--explicit-exit-notify`). [#131](https://github.com/passepartoutvpn/tunnelkit/issues/131)
|
||||
- Abrupt disconnection upon unknown packet key id (johankool). [#161](https://github.com/passepartoutvpn/tunnelkit/pull/161)
|
||||
- Handle explicit IPv4/IPv6 protocols (`4` or `6` suffix in `--proto`). [#153](https://github.com/passepartoutvpn/tunnelkit/issues/153)
|
||||
- Mitigate IP traffic breaking on Mojave. [#146](https://github.com/passepartoutvpn/tunnelkit/issues/146)
|
||||
- Pointer warnings from Xcode 11.4 upgrade.
|
||||
|
||||
## 2.2.1 (2019-12-14)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Keep-alive pings coalescing over time.
|
||||
- Ping timeout not checked for if keep-alive is disabled.
|
||||
|
||||
## 2.2.0 (2019-12-11)
|
||||
|
||||
### Changed
|
||||
|
||||
- Require explicit `--ca` and `--cipher` in .ovpn configuration file.
|
||||
|
||||
## 2.1.0 (2019-11-03)
|
||||
|
||||
### Added
|
||||
|
||||
- Allow keep-alive timeout to be configured by the server or client (Robert Patchett). [#122](https://github.com/passepartoutvpn/tunnelkit/pull/122)
|
||||
- Support for proxy autoconfiguration URL (ThinkChaos). [#125](https://github.com/passepartoutvpn/tunnelkit/pull/125)
|
||||
- Support multiple DNS search domains. [#127](https://github.com/passepartoutvpn/tunnelkit/issues/127)
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade OpenSSL to 1.1.1d. [#123](https://github.com/passepartoutvpn/tunnelkit/issues/123)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Session negotiation succeeds too early (Robert Patchett). [#124](https://github.com/passepartoutvpn/tunnelkit/pull/124)
|
||||
- Handle `vpn_gateway` literal in `--route`.
|
||||
|
||||
## 2.0.5 (2019-09-26)
|
||||
|
||||
### Fixed
|
||||
|
||||
- OpenSSL framework structure on macOS makes binary invalid when uploaded to App Store Connect.
|
||||
- Potential OOB in memcmp() (Guido Vranken).
|
||||
|
||||
## 2.0.3 (2019-09-06)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Deadlock on shutdown (further fixes). [#106](https://github.com/passepartoutvpn/tunnelkit/issues/106)
|
||||
- Regression with negotiation failing due to .staleSession error. [#120](https://github.com/passepartoutvpn/tunnelkit/issues/120)
|
||||
|
||||
## 2.0.2 (2019-07-27)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Deadlock on shutdown. [#106](https://github.com/passepartoutvpn/tunnelkit/issues/106)
|
||||
- Stuck on SOFT_RESET. [#105](https://github.com/passepartoutvpn/tunnelkit/issues/105)
|
||||
- Tunnel dies unexpectedly on macOS. [#111](https://github.com/passepartoutvpn/tunnelkit/issues/111)
|
||||
- Recover from ENOBUFS. [#112](https://github.com/passepartoutvpn/tunnelkit/issues/112)
|
||||
|
||||
## 2.0.1 (2019-05-28)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Regression in LZO subspec.
|
||||
|
||||
## 2.0.0 (2019-05-28)
|
||||
|
||||
### Changed
|
||||
|
||||
- Major refactoring.
|
||||
|
||||
## 1.7.1 (2019-05-14)
|
||||
|
||||
### Added
|
||||
|
||||
- Partially support `--redirect-gateway block-local`. [#81](https://github.com/passepartoutvpn/tunnelkit/issues/81)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Authentication failure due to local options. [#95](https://github.com/passepartoutvpn/tunnelkit/issues/95)
|
||||
- Customize security level (to tolerate weak certificates). [#97](https://github.com/passepartoutvpn/tunnelkit/issues/97)
|
||||
- Connection stalls on server-initiated SOFT_RESET.
|
||||
- Wrong configuration mutability.
|
||||
|
||||
## 1.7.0 (2019-04-28)
|
||||
|
||||
### Changed
|
||||
|
||||
- Do not redirect all traffic to VPN unless `--redirect-gateway` specified. [#90](https://github.com/passepartoutvpn/tunnelkit/issues/90)
|
||||
- Upgrade OpenSSL to 1.1.0j.
|
||||
|
||||
### Fixed
|
||||
|
||||
- SoftEther sends an incomplete PUSH_REPLY. [#86](https://github.com/passepartoutvpn/tunnelkit/issues/86)
|
||||
- Authentication/Decrypt errors with TLS wrapping. [#88](https://github.com/passepartoutvpn/tunnelkit/issues/88), [#61](https://github.com/passepartoutvpn/tunnelkit/issues/61)
|
||||
- Broken DNS when no servers provided. [#84](https://github.com/passepartoutvpn/tunnelkit/issues/84)
|
||||
- UDP may disconnect on high-speed upload link. [#87](https://github.com/passepartoutvpn/tunnelkit/issues/87)
|
||||
- Client certificate may fail when private key in .ovpn is encrypted. [#91](https://github.com/passepartoutvpn/tunnelkit/issues/91)
|
||||
- DNS is unreachable when VPN is not default gateway. [#94](https://github.com/passepartoutvpn/tunnelkit/issues/94)
|
||||
|
||||
## 1.6.2 (2019-04-17)
|
||||
|
||||
### Added
|
||||
|
||||
- Basic support for proxy settings (no PAC). [#74](https://github.com/passepartoutvpn/tunnelkit/issues/74)
|
||||
|
||||
### Changed
|
||||
|
||||
- Make `hostname` optional and pick `resolvedAddresses` if nil.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Negotiation times out with SoftEther. [#67](https://github.com/passepartoutvpn/tunnelkit/issues/67)
|
||||
- Unable to handle continuated PUSH_REPLY. [#71](https://github.com/passepartoutvpn/tunnelkit/issues/71)
|
||||
- TCP requiring multiple PUSH_REQUEST. [#73](https://github.com/passepartoutvpn/tunnelkit/issues/73)
|
||||
- DNS inconsistencies. [#85](https://github.com/passepartoutvpn/tunnelkit/pull/85)
|
||||
|
||||
## 1.6.1 (2019-04-07)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Cipher/digest erroneously required by AppExtension.
|
||||
|
||||
## 1.6.0 (2019-04-06)
|
||||
|
||||
### Added
|
||||
|
||||
- Handle `dhcp-option DOMAIN`. [#77](https://github.com/passepartoutvpn/tunnelkit/issues/77)
|
||||
|
||||
### Changed
|
||||
|
||||
- Refactor configuration parser for reuse.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Unrecognized PKCS#8 encrypted private keys. [#80](https://github.com/passepartoutvpn/tunnelkit/issues/80)
|
||||
- Handle PEM with preamble. [#78](https://github.com/passepartoutvpn/tunnelkit/issues/78)
|
||||
|
||||
## 1.5.2 (2019-04-01)
|
||||
|
||||
### Added
|
||||
|
||||
- Optional data count report via `TunnelKitProvider.Configuration.dataCount(in:)`.
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgraded to Swift 5.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `checksEKU` not propagated to TunnelKitProvider.
|
||||
|
||||
## 1.5.1 (2019-03-25)
|
||||
|
||||
### Added
|
||||
|
||||
- Scramble endpoints via `--remote-random`. [#76](https://github.com/passepartoutvpn/tunnelkit/issues/76)
|
||||
- Support for encrypted certificate private keys. [#72](https://github.com/passepartoutvpn/tunnelkit/issues/72)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Send explicit exit notification if UDP. [#29](https://github.com/passepartoutvpn/tunnelkit/issues/29)
|
||||
- Broken reconnection on network change (mitigated). [#75](https://github.com/passepartoutvpn/tunnelkit/issues/75)
|
||||
|
||||
## 1.5.0 (2019-03-20)
|
||||
|
||||
### Added
|
||||
|
||||
- Support for legacy `--comp-lzo` compression. [#69](https://github.com/passepartoutvpn/tunnelkit/pull/69)
|
||||
- Support for newer `--compress lzo` option. [#70](https://github.com/passepartoutvpn/tunnelkit/pull/70)
|
||||
|
||||
## 1.4.3 (2019-03-18)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Several reconnection issues.
|
||||
- Missing EKU flag evaluation.
|
||||
|
||||
## 1.4.2 (2019-03-05)
|
||||
|
||||
### Added
|
||||
|
||||
- Shut down if server pushes a compressed data packet.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Custom DNS servers were not applied.
|
||||
- Reject `<connection>` blocks as unsupported.
|
||||
|
||||
## 1.4.1 (2019-02-25)
|
||||
|
||||
### Added
|
||||
|
||||
- Override DNS servers client side. [#56](https://github.com/passepartoutvpn/tunnelkit/pull/56)
|
||||
- Shut down if server pushes a compression directive. [#65](https://github.com/passepartoutvpn/tunnelkit/pull/65)
|
||||
|
||||
### Changed
|
||||
|
||||
- Enable or disable EKU according to `remote-cert-tls server` in .ovpn file. [#64](https://github.com/passepartoutvpn/tunnelkit/pull/64)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Compiling errors in demo target.
|
||||
- Linking errors with OpenSSL.
|
||||
- A few potential vulnerabilities.
|
||||
|
||||
## 1.4.0 (2018-11-12)
|
||||
|
||||
### Added
|
||||
|
||||
- Parser for .ovpn configuration files. [#47](https://github.com/passepartoutvpn/tunnelkit/pull/47)
|
||||
|
||||
### Changed
|
||||
|
||||
- Due to [#47](https://github.com/passepartoutvpn/tunnelkit/pull/47), `SocketType` and `EndpointProtocol` were moved to Core subspec.
|
||||
|
||||
## 1.3.1 (2018-11-07)
|
||||
|
||||
### Fixed
|
||||
|
||||
- IPv4/UInt32 conversions are not endianness-agnostic. [#46](https://github.com/passepartoutvpn/tunnelkit/pull/46)
|
||||
|
||||
## 1.3.0 (2018-10-28)
|
||||
|
||||
### Changed
|
||||
|
||||
- Refactored tunnel configuration API for increased code reuse. [#44](https://github.com/passepartoutvpn/tunnelkit/pull/44)
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Use high-level accessories instead of `debugLogKey` and `lastErrorKey`. [#45](https://github.com/passepartoutvpn/tunnelkit/pull/45)
|
||||
|
||||
### Fixed
|
||||
|
||||
- IPv4/UInt32 calculations were wrong.
|
||||
|
||||
## 1.2.2 (2018-10-25)
|
||||
|
||||
### Changed
|
||||
|
||||
- Debug log is saved to group container rather than `UserDefaults`. [#43](https://github.com/passepartoutvpn/tunnelkit/pull/43)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Handle server-initiated renegotiation. [#41](https://github.com/passepartoutvpn/tunnelkit/pull/41)
|
||||
- Potentially private data (e.g. Internet addresses) is now masked in debug log. [#42](https://github.com/passepartoutvpn/tunnelkit/pull/42)
|
||||
|
||||
## 1.2.1 (2018-10-24)
|
||||
|
||||
### Added
|
||||
|
||||
- Configuration key `lastErrorKey` for reporting errors to host app. [#40](https://github.com/passepartoutvpn/tunnelkit/pull/40)
|
||||
- Server extended key usage validation (EKU). [#27](https://github.com/passepartoutvpn/tunnelkit/issues/27)
|
||||
|
||||
### Fixed
|
||||
|
||||
- CA file was not closed after MD5 calculation when using PIA patches.
|
||||
- Mitigated an issue with MTU in TCP mode during negotiation. [#39](https://github.com/passepartoutvpn/tunnelkit/issues/39)
|
||||
|
||||
## 1.2.0 (2018-10-20)
|
||||
|
||||
### Added
|
||||
|
||||
- Support for `--tls-auth` wrapping. [#34](https://github.com/passepartoutvpn/tunnelkit/pull/34)
|
||||
- Support for `--tls-crypt` wrapping. [#35](https://github.com/passepartoutvpn/tunnelkit/pull/35)
|
||||
- Parser for static OpenVPN keys from file. [#36](https://github.com/passepartoutvpn/tunnelkit/pull/36)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Handling of mixed DATA_V1/DATA_V2 packets. [#30](https://github.com/passepartoutvpn/tunnelkit/issues/30)
|
||||
|
||||
## 1.1.2 (2018-10-18)
|
||||
|
||||
### Added
|
||||
|
||||
- Restored support for PIA patches. [#32](https://github.com/passepartoutvpn/tunnelkit/pull/32)
|
||||
|
||||
## 1.1.1 (2018-10-10)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Make CA non-optional. [#28](https://github.com/passepartoutvpn/tunnelkit/pull/28)
|
||||
|
||||
## 1.1.0 (2018-09-26)
|
||||
|
||||
### Added
|
||||
|
||||
- Client certificate verification. [#3](https://github.com/passepartoutvpn/tunnelkit/pull/3)
|
||||
- Support for both `--comp-lzo` and `--compress` compression framing. [#2](https://github.com/passepartoutvpn/tunnelkit/pull/2), [#5](https://github.com/passepartoutvpn/tunnelkit/pull/5), [#10](https://github.com/passepartoutvpn/tunnelkit/pull/10)
|
||||
- Routes setup from PUSH_REPLY. [#7](https://github.com/passepartoutvpn/tunnelkit/pull/7)
|
||||
- Support for IPv6. [#8](https://github.com/passepartoutvpn/tunnelkit/pull/8)
|
||||
- Support for server-side NCP. [#11](https://github.com/passepartoutvpn/tunnelkit/pull/11)
|
||||
- Property to mark ciphers not requiring digest auth (e.g. GCM). [#13](https://github.com/passepartoutvpn/tunnelkit/pull/13)
|
||||
- `Codable` implementations for native Swift serialization. [#15](https://github.com/passepartoutvpn/tunnelkit/pull/15)
|
||||
- More cipher and digest algorithms. [#16](https://github.com/passepartoutvpn/tunnelkit/pull/16)
|
||||
- Negotiated compression framing from PUSH_REPLY. [#19](https://github.com/passepartoutvpn/tunnelkit/pull/19)
|
||||
- Customizable keep-alive. [#20](https://github.com/passepartoutvpn/tunnelkit/pull/20)
|
||||
- Negotiated keep-alive from PUSH_REPLY. [#22](https://github.com/passepartoutvpn/tunnelkit/pull/22)
|
||||
- Peer-info metadata.
|
||||
|
||||
### Changed
|
||||
|
||||
- Raised iOS target to 11 (drops 32-bit support).
|
||||
- Upgraded OpenSSL from 1.1.0h to 1.1.0i.
|
||||
- Minor adjustments for Xcode 10 / Swift 4.2.
|
||||
- Deep refactoring of control channel for future extensibility.
|
||||
- App group moved out of tunnel configuration, to make it more platform-agnostic and coherent to serialize.
|
||||
- Keep-alive is disabled by default.
|
||||
- Several internal renamings.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Sensitive data logged in PUSH_REPLY. [#12](https://github.com/passepartoutvpn/tunnelkit/pull/12)
|
||||
- Bad interpretation of 0 seconds between renegotiations. [#18](https://github.com/passepartoutvpn/tunnelkit/pull/18)
|
||||
- Incorrect behavior on data-related failures. [#21](https://github.com/passepartoutvpn/tunnelkit/pull/21)
|
||||
|
||||
## 1.0.0 (2018-08-23)
|
||||
|
||||
### Added
|
||||
|
||||
- Initial fork from https://github.com/pia-foss/tunnel-apple
|
||||
|
||||
### Removed
|
||||
|
||||
- Non-standard PIA patches.
|
||||
@@ -0,0 +1,9 @@
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I have the right to submit it under the MIT license; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the MIT license; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved.
|
||||
@@ -0,0 +1,8 @@
|
||||
## Contributing
|
||||
|
||||
- Use imperative commit messages.
|
||||
- GOOD: "Add new feature"
|
||||
- BAD: "Added new feature"
|
||||
- Rebase your branch to `master` and possibly squash unrelevant commits.
|
||||
- Make sure to pass the unit tests, as long as CI doesn't automate them yet.
|
||||
- Submit the PR.
|
||||
@@ -0,0 +1,216 @@
|
||||
//
|
||||
// Configuration.swift
|
||||
// Demo
|
||||
//
|
||||
// Created by Davide De Rosa on 6/13/20.
|
||||
// Copyright (c) 2022 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/keeshux
|
||||
//
|
||||
// This file is part of TunnelKit.
|
||||
//
|
||||
// TunnelKit is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// TunnelKit is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TunnelKitCore
|
||||
import TunnelKitOpenVPN
|
||||
import TunnelKitWireGuard
|
||||
|
||||
extension OpenVPN {
|
||||
struct DemoConfiguration {
|
||||
static let ca = OpenVPN.CryptoContainer(pem: """
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIG6zCCBNOgAwIBAgIJAJhm2PWFkE8NMA0GCSqGSIb3DQEBCwUAMIGpMQswCQYD
|
||||
VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxEzAR
|
||||
BgNVBAoTCkdvb2dsZSBJbmMxEzARBgNVBAsTCkRldmVsb3BlcnMxFjAUBgNVBAMT
|
||||
DUdvb2dsZSBJbmMgQ0ExEDAOBgNVBCkTB0Vhc3lSU0ExITAfBgkqhkiG9w0BCQEW
|
||||
EnN1cHBvcnRAZ29vZ2xlLmNvbTAeFw0xNTAyMDIwNTMwMDlaFw0yNTAxMzAwNTMw
|
||||
MDlaMIGpMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50
|
||||
YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBJbmMxEzARBgNVBAsTCkRldmVsb3Bl
|
||||
cnMxFjAUBgNVBAMTDUdvb2dsZSBJbmMgQ0ExEDAOBgNVBCkTB0Vhc3lSU0ExITAf
|
||||
BgkqhkiG9w0BCQEWEnN1cHBvcnRAZ29vZ2xlLmNvbTCCAiIwDQYJKoZIhvcNAQEB
|
||||
BQADggIPADCCAgoCggIBAN8T5cgRQ8+zsE2FWRpArqTlBh7MvoQU9Z4659eJ3Mhq
|
||||
+pvR960HG9Bg6MkH0gwdcU65l0TLTwweOLBIZoxhLB+OVvl/x0FD4EnK9Pmp5SIU
|
||||
P7cEqcqqRfRAI+9k0jwiGcPOl7KKqfz70c6QsQYn2VvrTMqgDt4IS/zpaToZsftq
|
||||
ibCtKh0bPv4UMLg6Y31cItYlVIrrbGrM4Kvdb8yN8ho3ms5KV421G9s9w/6KYBZt
|
||||
zr3mHoI9o+njE0ScTIRDnygbTevMZuCStIMjFRYaSvw0mHJu/07AQb+jwRBlZixw
|
||||
B79tuZzd0pZvDPpvjqWNfvE8iIoqVAv+eMe+/XG0n5ptUfhz27yDHOoZmaPjVThg
|
||||
4/DR8dBm6vKH4lsbCXdcZqSyBHhHNNVcGF024RItvULC/wu4xmjJOTzWV5YqjHWY
|
||||
1P+7APCTYWOfvl/xZ0W42yYB2oBcsl3wpyrbFoqXVqfkOkUArp8h0zNose7+G6jW
|
||||
xsFGqp566xD72GmULEn1TaIstdvbkvLhtgJzHkP3zSsaspSxgJNc46ZwQs5acDOB
|
||||
6NpUMeyT9dYzgiLGL8F/aBcYYs03qV9Ae6puuNlH60wZyDe7xCfrrbLHfal6wKXD
|
||||
ULdv6HJ6tmcgzHx+qt5vdlqDeocSOmOgK0Xpv+GUTCMpTB8uSztb3puyLQ5A1xgT
|
||||
AgMBAAGjggESMIIBDjAdBgNVHQ4EFgQUrnDngftZs+1zGhU3iSaU0yJg4oAwgd4G
|
||||
A1UdIwSB1jCB04AUrnDngftZs+1zGhU3iSaU0yJg4oChga+kgawwgakxCzAJBgNV
|
||||
BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEG
|
||||
A1UEChMKR29vZ2xlIEluYzETMBEGA1UECxMKRGV2ZWxvcGVyczEWMBQGA1UEAxMN
|
||||
R29vZ2xlIEluYyBDQTEQMA4GA1UEKRMHRWFzeVJTQTEhMB8GCSqGSIb3DQEJARYS
|
||||
c3VwcG9ydEBnb29nbGUuY29tggkAmGbY9YWQTw0wDAYDVR0TBAUwAwEB/zANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEARGOf8IUhXm0rLSmhydWwHKdcTH0LKkw/muknDkBm6j+q
|
||||
VQHYyJIrPOe3jZZ+Vzk5mnEj8RCJ/H5DiYnxPSlpr7slNtI/AqG4d5ODwU3uGsrs
|
||||
LaoUK5OWc81R0l5EBfzo+rfYI5O/0uG7M9BsGQZVz0ZpiqHuUb9BXlZ6gRVCWepm
|
||||
l7cqF8038o6ZraHpeNAI6FejBEMrO45Wc5eutpbcg18FTkotiRWS3I6K4xg75lZp
|
||||
tjF1aYGTAhC/8yoAYmBKzbKJXyNW2Vq93/9y+43OUJridoijB7cqbUpZFOVdtnZ5
|
||||
LHb3h7hLV/3C2WgehM73f/UMc65fIk+9CpwD7Cgpu9duBknf0c0s0Sw3HA/s6SL6
|
||||
V4FhARi7flTF9TGR6+e0i2oreXEwJXP3GoXpazOqzrGekSXRMqwLY83fJ/RzP0Ap
|
||||
PMc5TfiQVcL/h92CUAwwH1vRJkAhrTvNXh1Ynd7zdFT/wYWrK0twm4qlTjKYpbVL
|
||||
RIoeppgOUG+1t82/HW2geWLYSNRfZiTbpAvm00HJavD12qOD0NUIErlQnOZvW2UC
|
||||
/RzA/yu9ZguEIlV+8qmkiUCKyajyLFydWqqScMYAeJMh6aJzfQ4UHu2bzr9Qo2MV
|
||||
HiT8esMeX+/orMetzuTPgZInMhznvVdNdfwAfibwlXOKvm154UgDVgnKV405oNM=
|
||||
-----END CERTIFICATE-----
|
||||
""")
|
||||
|
||||
static let clientCertificate = OpenVPN.CryptoContainer(pem: """
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIHPTCCBSWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADCBqTELMAkGA1UEBhMCVVMx
|
||||
CzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpH
|
||||
b29nbGUgSW5jMRMwEQYDVQQLEwpEZXZlbG9wZXJzMRYwFAYDVQQDEw1Hb29nbGUg
|
||||
SW5jIENBMRAwDgYDVQQpEwdFYXN5UlNBMSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0
|
||||
QGdvb2dsZS5jb20wHhcNMTUwMjAyMDUzODUxWhcNMjUwMTMwMDUzODUxWjCBojEL
|
||||
MAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3
|
||||
MRMwEQYDVQQKEwpHb29nbGUgSW5jMRMwEQYDVQQLEwpEZXZlbG9wZXJzMQ8wDQYD
|
||||
VQQDEwZjbGllbnQxEDAOBgNVBCkTB0Vhc3lSU0ExITAfBgkqhkiG9w0BCQEWEnN1
|
||||
cHBvcnRAZ29vZ2xlLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
|
||||
AO+ClQmiqC8eZsXbLtS+3UF+CUBdabPOFpKOvhmpgsxCdylzALWK5WAOx4an+uXg
|
||||
L8LrhF5sjHSEtTXiRzh6e+vqzn228t6ZKJIA5jDCZ44CTCTZKdxu1X+wSJNIEOzz
|
||||
u5OVzVM5gQPWOewBOq81NMbLHxWXHVB3gybE5KU859XBLJush8vCBK5No3VOMlmI
|
||||
qUbwVCfX8kh322N4PIe8dvsGyAFjqn05y0bD83IuXAY0HtijUwquiWEeZO8dluIt
|
||||
NqpYkeMpMGaU208/7P6/btT9EXtuHV6fMEeeO/SXIrE9EGmrWsieXg+TEilXuGMc
|
||||
hHDfkRw6xeXTFD5P0Jxrb5EhKZMV9GRIg+62VyP6s3de/3xOY7/2BKoWilmxdWcm
|
||||
VLz0i5Zxl7wokHf8egEInECZmyYCwGgu/KS/kChm8JLYiQ5oJJ+1+JZyQciko+xk
|
||||
qvngbx9pTHtcJYE1mW6jEw4V5f7ID3LdOqLmiitKQ34ke/2OPY1NSBspAL/P2Mi0
|
||||
W33GRHOfAIRy5PEqAk7GjEEPPpyEyAUXS0TpFdvgQEOKqw4oxJuZ1GPWGDxNfp1g
|
||||
JKg2HBM+Nc7QepMXLh5LHTNSOSWvJf3LsrUQ6goKp2PA0ucpktXxh08uNBJ5nUrJ
|
||||
ZyituebSAv51C5r45VNCDk542vvNZVGx+mXOjRXQfVL3AgMBAAGjggFzMIIBbzAJ
|
||||
BgNVHRMEAjAAMC0GCWCGSAGG+EIBDQQgFh5FYXN5LVJTQSBHZW5lcmF0ZWQgQ2Vy
|
||||
dGlmaWNhdGUwHQYDVR0OBBYEFC6k0HKIbIzDih6+khKzUr3uIULVMIHeBgNVHSME
|
||||
gdYwgdOAFK5w54H7WbPtcxoVN4kmlNMiYOKAoYGvpIGsMIGpMQswCQYDVQQGEwJV
|
||||
UzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxEzARBgNVBAoT
|
||||
Ckdvb2dsZSBJbmMxEzARBgNVBAsTCkRldmVsb3BlcnMxFjAUBgNVBAMTDUdvb2ds
|
||||
ZSBJbmMgQ0ExEDAOBgNVBCkTB0Vhc3lSU0ExITAfBgkqhkiG9w0BCQEWEnN1cHBv
|
||||
cnRAZ29vZ2xlLmNvbYIJAJhm2PWFkE8NMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAsG
|
||||
A1UdDwQEAwIHgDARBgNVHREECjAIggZjbGllbnQwDQYJKoZIhvcNAQELBQADggIB
|
||||
ACEBDTW4moXsrkIOJVC66vlbcHqphCLkTsvSt3e7FU8+UGR7eKnvg61kG16HmBcZ
|
||||
AQ/ChFyNafCdHXOmHFp9s7hRHFJ1LZ5xidBxQhBOTf66aoDzILj67MvLoCFnuxEq
|
||||
f3Ok5ayGKWVppfMUs7RgTPL+XSMLM1lsHpFMcy983MNZ+w8sSVgHiWrso2q6nTSG
|
||||
aZYn7nSTpxlDHSVDB757wsIcDKT8FF/4nA0649meuEVMtNYR3hCmqiAkK9QwK8MR
|
||||
BCt3emHq5jVg51NNrhGKoaXwgab+p/YehHx1XFcDTUXIImkN0s1hZy4DlrUYkOBT
|
||||
3izKnWFziq2Zkpx9N6ZEdknQvFXeQg+EAMnVcvpf78WBvq8BIa+PlIMlSojj3tjP
|
||||
krsyjTwWk4/f3IL4Y9B8SpoGHW3hzsEA1Z1QdYy1LnRi0MQ6XIM06vMrM/JW6H/r
|
||||
fHGa7wDILYCwgzmgqX8ek8R5v9fOdtzpJxL54o3mgkNsPuDglylNy87sR4xTd5Cr
|
||||
NOQ9Q/PuNi0u2pEMsbmj3OrPjy2TFsW6BiDKr5y48lHin7OqmuiQZMnDX/o75Ylc
|
||||
bcdJrlfMT2PJrSvH6ap61NqQK9xnIqKOhuI9xwVCvizI67GuGxiwCgiF+YSR5nOA
|
||||
kiJ6Ts2iqIvR7T7Eme2vBYH/UJ1DXrdCJx6IDGxxgoXk
|
||||
-----END CERTIFICATE-----
|
||||
""")
|
||||
|
||||
static let clientKey = OpenVPN.CryptoContainer(pem: """
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDvgpUJoqgvHmbF
|
||||
2y7Uvt1BfglAXWmzzhaSjr4ZqYLMQncpcwC1iuVgDseGp/rl4C/C64RebIx0hLU1
|
||||
4kc4envr6s59tvLemSiSAOYwwmeOAkwk2SncbtV/sEiTSBDs87uTlc1TOYED1jns
|
||||
ATqvNTTGyx8Vlx1Qd4MmxOSlPOfVwSybrIfLwgSuTaN1TjJZiKlG8FQn1/JId9tj
|
||||
eDyHvHb7BsgBY6p9OctGw/NyLlwGNB7Yo1MKrolhHmTvHZbiLTaqWJHjKTBmlNtP
|
||||
P+z+v27U/RF7bh1enzBHnjv0lyKxPRBpq1rInl4PkxIpV7hjHIRw35EcOsXl0xQ+
|
||||
T9Cca2+RISmTFfRkSIPutlcj+rN3Xv98TmO/9gSqFopZsXVnJlS89IuWcZe8KJB3
|
||||
/HoBCJxAmZsmAsBoLvykv5AoZvCS2IkOaCSftfiWckHIpKPsZKr54G8faUx7XCWB
|
||||
NZluoxMOFeX+yA9y3Tqi5oorSkN+JHv9jj2NTUgbKQC/z9jItFt9xkRznwCEcuTx
|
||||
KgJOxoxBDz6chMgFF0tE6RXb4EBDiqsOKMSbmdRj1hg8TX6dYCSoNhwTPjXO0HqT
|
||||
Fy4eSx0zUjklryX9y7K1EOoKCqdjwNLnKZLV8YdPLjQSeZ1KyWcorbnm0gL+dQua
|
||||
+OVTQg5OeNr7zWVRsfplzo0V0H1S9wIDAQABAoICAGL0e6kod/5HvESA419ooDd/
|
||||
4Eikj5iHTFIvAaHOpEjKKTuJ1UAsa8p9MLiUzJePQYxyDBWLGZjGf6wMmkpeaLa3
|
||||
I6tTHBMWCmoQTwrUNz63+ke7JY16iWEhL0sSmlOb++LlIJkDCCfSqcm1VE6xV+XO
|
||||
ZEBiV+04A4rQDHusp0hscIa9CLoJpi9xylgb/7d4PCAgCVUQ5nxEcPMu6StXlXzv
|
||||
d1EDoZvtdev956ZEOycg/6GYESY3qHDkwuT8P6ug7JYC0/ubt/CaDeY3Ti6OXzdG
|
||||
e6OYgi/m62abnL/Yda/uv8o4zuBWdhxPMlC8emUQkjOkWurj6YGj7Rg1l8YYqVXr
|
||||
VVzRVq5bwL2FaDlQA//K2RGhRqScG3/M9qYJYRYNNPsVR3dBkewiqFnQcyyBOvIM
|
||||
c4xFoCxFbhf+TXRH/74W1wIVQH/w4A55PsYdZfm4g1DRFbbsGmo/tsfDq73tz2Pi
|
||||
sUXR2JzNW9Sj7q6F2RPiMrIV3E7apdCeylgGS9Uhf7ZNorBjhgKVkm0UxQeRkedk
|
||||
BvH/r3AqVqWc3IhJ/KadcBm+mPyStTcL452odXrpLqPyENTGsNy4MZ12QQbXa6uT
|
||||
RaRDhO7G6ocTR6UlUstsjiFe3LKSrXRlJdZ+4xJsquBBcTS6PYzeOr2ZnS+/QGpE
|
||||
R+iJHidYRJcCe2kP7KwRAoIBAQD4pUUUZfBaSFzEq17sWwpL8enDIJJAybIQ808L
|
||||
v7CuJeemZqiDh+La0htvd+/qZhZfEZKJPCiV1ml/o3ArwK5CnFK/ZLTjRC9ocm5c
|
||||
POwJhKo52Y0FsazOLmVD6SqS5jKvl4Gvn+KkkGLZrvefAFWpthyLWHRYaDXeIUkd
|
||||
y6piGh99v3/9KZSN7gpZjdl1AdCQAR7tdOC1rQx7Nzl6gxpmJ1/SmRQ5XYYfJU5a
|
||||
6q1w2x+nt6fGE3BLJ/rxx5kKAJmwFeYlsuFAFkXypRjXtF66jewP/3j/lckArlXA
|
||||
3X3K17BJ8R/x5DGaybwk17Vv6UFMlFJSTYOyGbsUIWJVvWVZAoIBAQD2mCOMdSCH
|
||||
Nx+2kFEEuisv9PBboMKs+bvIYJCNJ7/FGscGxr916/GAc/p2Sfp2Dweybxi5msUj
|
||||
Oqidpw2hLDlGEioJyQxrvrk5Pa75ipZKZ8VnKIhlupIZ5FGJmVU/DDak+Drw6W0N
|
||||
Ae5w6Q7Pbf1YcCle9ZRUN5MITdGMIWnLKUVF1ZbL153mOMizJRWa0XsnJjacLiXi
|
||||
/tYsSrBKaA4N+j0rOutN9FIF7PyjoZ+3YKEttmRYV5W3OtkLC82zORFWahX5K/3n
|
||||
mcSZLkG7n9dWQkcOvXgpPh+7f6u3MX0H0EWze0RhRp8h8fZiuVELyZL3evdWquwN
|
||||
K9i7s9pTL2DPAoIBAEObzLjLLxudaXwgjOL/rkEQOlvQU3RCY6SwQ+IR8Vyo+eAJ
|
||||
MfDx1gFh+AvLNPUrZRHcmVevf+meL3mBW1LKRZffIbDhFT5mn+1qkA+MkTHVXOP1
|
||||
/554vWAixW49zFG9PjL4o065zsqoZ/iA1tvpH2HSHtjU6G3RiDQqINN1OZMLP1zV
|
||||
4VtZHweoni/TnjlukONXKq2uhhtgPnCSh5KEa30zX57H+PPQNlPptPCLtzVkn6rf
|
||||
CUOWrYYCDP4JI9fQafmzOq0tgooGhGaB9ctRRCC9zl5bPO9iLxF8VdznXPj2xPyW
|
||||
D/WZ8tL/36S08qTHa/YCro+qfBDFZlUG7tIZeaECggEAeTrERzoR2se73InIetV3
|
||||
g+UcAT/gVR+VNOZcSjjfa2xFqkwtNjDfknHyERM/gajT9OHvOtge0Ln2yUKmTbUr
|
||||
Fwq5BgSECbhC4SQ1EFMUndG0V4myvKhjST1Y5JewNAWyG5o5h9SKGxn2+iVpdYqy
|
||||
QTcq75c1681CiJORUB3hH9LTToi50M7YvqTt7jxuCaWwsMd1k4SQda8o5a92Sa4s
|
||||
MqzyQ318zt8tL+KZNWyw03s64flIDbJJVUImD+smnlSQ9HXFBbGd6q1K3K/D+xSS
|
||||
zcJZoqJ9H3F+MjSK284FlMDMc3dHX7dTZmHI6jIG6Q+ZI/ec/0uaLsN+kpDR5ZFm
|
||||
OwKCAQEA11nK0Orlb85QRRNWIp2TiclXPBK/x6fhtDDEtyIfNtw0cVLr8EjABepP
|
||||
12H57Hs1f9qLeWFa20dbTh2OeEvOnqzdXR1/27sjSc8UqreEwzrv/bkmBEF92xxy
|
||||
66LIr0o2S72ZT6E6IImJ2N563GrOWla7LpQN5V64RAc3C2vb5DiL70oCE0Qpb9Vn
|
||||
M69t86apMrAxkUxVJAWLRBd9fbYyzJgTW61tFqXWTZpiz6bhuWApSEzaHcL3/f5l
|
||||
3qibvYTFj6CIqcdHA6Sy+UTEyb7zWnFwWVNEwAadsMmq45mhdoFjlm/5onPrpj+l
|
||||
1LXZrtjAB4U+/F7um6YyAavpHYq9hg==
|
||||
-----END PRIVATE KEY-----
|
||||
""")
|
||||
|
||||
static func make(_ title: String, appGroup: String, hostname: String, port: UInt16, socketType: SocketType) -> OpenVPN.ProviderConfiguration {
|
||||
var builder = OpenVPN.ConfigurationBuilder()
|
||||
builder.ca = ca
|
||||
builder.cipher = .aes128cbc
|
||||
builder.digest = .sha1
|
||||
builder.compressionFraming = .compLZO
|
||||
builder.renegotiatesAfter = nil
|
||||
builder.remotes = [Endpoint(hostname, EndpointProtocol(socketType, port))]
|
||||
builder.clientCertificate = clientCertificate
|
||||
builder.clientKey = clientKey
|
||||
builder.mtu = 1350
|
||||
let cfg = builder.build()
|
||||
|
||||
var providerConfiguration = OpenVPN.ProviderConfiguration(title, appGroup: appGroup, configuration: cfg)
|
||||
providerConfiguration.shouldDebug = true
|
||||
providerConfiguration.masksPrivateData = false
|
||||
return providerConfiguration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension WireGuard {
|
||||
struct DemoConfiguration {
|
||||
static func make(
|
||||
_ title: String,
|
||||
appGroup: String,
|
||||
clientPrivateKey: String,
|
||||
clientAddress: String,
|
||||
serverPublicKey: String,
|
||||
serverAddress: String,
|
||||
serverPort: String
|
||||
) -> WireGuard.ProviderConfiguration? {
|
||||
var builder = try! WireGuard.ConfigurationBuilder(clientPrivateKey)
|
||||
builder.addresses = [clientAddress]
|
||||
builder.dnsServers = ["1.1.1.1", "1.0.0.1"]
|
||||
try! builder.addPeer(serverPublicKey, endpoint: "\(serverAddress):\(serverPort)")
|
||||
builder.addDefaultGatewayIPv4(toPeer: 0)
|
||||
let cfg = builder.build()
|
||||
|
||||
return WireGuard.ProviderConfiguration(title, appGroup: appGroup, configuration: cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?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>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>TunnelKitDemoTunnel</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>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.networkextension.packet-tunnel</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
||||
</dict>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright (c) 2022 Davide De Rosa. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user