WireGuard

This commit is contained in:
Yuriy.Shikin
2022-04-25 07:28:13 +00:00
committed by Liz Malinouskaya
parent 660bc9ea03
commit 6ab7fa45a1
557 changed files with 74488 additions and 165 deletions
+1
View File
@@ -16,3 +16,4 @@
xcuserdata/
/BuildTools/Creator/Vendors/packages
/BuildTools/Creator/Creator
/macOS/SystemExtensions/OpenVPN/Info.plist
+1
View File
@@ -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.
+11 -14
View File
@@ -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
+16
View File
@@ -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
+2
View File
@@ -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"
+1 -1
View File
@@ -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";
+75
View File
@@ -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)
+735
View File
@@ -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 filters `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 apps 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)
+11
View File
@@ -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
+26
View File
@@ -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>
+21
View File
@@ -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]
)
+270
View File
@@ -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
![swiftybeaver-demo1](https://cloud.githubusercontent.com/assets/564725/14846071/218c0646-0c62-11e6-92cb-e6e963b68724.gif)
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 lets 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 wont 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 **Apples 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. SwiftyBeavers 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 apps 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
}
}
@@ -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)
]
}
@@ -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 wont 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 wont 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 wont 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
View File
@@ -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."
@@ -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
@@ -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.
}
}
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -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
+201
View File
@@ -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
![iOS support](https://img.shields.io/badge/iOS-12+-blue.svg)
![macOS support](https://img.shields.io/badge/macOS-10.15+-blue.svg)
![macOS Catalyst support](https://img.shields.io/badge/macOS%20Catalyst-10.15+-blue.svg)
![watchOS support](https://img.shields.io/badge/watchOS-4.0+-blue.svg)
![tvOS support](https://img.shields.io/badge/tvOS-12+-blue.svg)
![OpenSSL version](https://img.shields.io/badge/OpenSSL-1.1.1l-green.svg)
[![license](https://img.shields.io/badge/license-Apache%202.0-lightgrey.svg)](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>
+1
View File
@@ -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>
+1
View File
@@ -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>
+1
View File
@@ -0,0 +1 @@
iPhone
@@ -0,0 +1 @@
iPhone
+647
View File
@@ -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
View File
@@ -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>
@@ -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
}
+11
View File
@@ -0,0 +1,11 @@
.DS_Store
*.swp
*.pbxuser
**/*.xcworkspace/xcuserdata
**/*.xcodeproj/project.xcworkspace
**/*.xcodeproj/xcuserdata
Pods
docs
build
.swiftpm
.build
+1
View File
@@ -0,0 +1 @@
2.7
+531
View File
@@ -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.
+9
View File
@@ -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.
+8
View File
@@ -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