2020-05-02 22:06:57 -07:00
//
// V e r i f y A p p O p e r a t i o n . s w i f t
// A l t S t o r e
//
// C r e a t e d b y R i l e y T e s t u t o n 5 / 2 / 2 0 .
// C o p y r i g h t © 2 0 2 0 R i l e y T e s t u t . A l l r i g h t s r e s e r v e d .
//
import Foundation
import AltSign
import AltKit
import Roxas
enum VerificationError : ALTLocalizedError
{
2020-05-07 13:13:05 -07:00
case privateEntitlements ( ALTApplication , entitlements : [ String : Any ] )
case mismatchedBundleIdentifiers ( ALTApplication , sourceBundleID : String )
2020-05-02 22:06:57 -07:00
var app : ALTApplication {
2020-05-07 13:13:05 -07:00
switch self
{
2020-05-02 22:06:57 -07:00
case . privateEntitlements ( let app , _ ) : return app
2020-05-07 13:13:05 -07:00
case . mismatchedBundleIdentifiers ( let app , _ ) : return app
2020-05-02 22:06:57 -07:00
}
}
var errorFailure : String ? {
return String ( format : NSLocalizedString ( " “%@” could not be installed. " , comment : " " ) , app . name )
}
var failureReason : String ? {
switch self
{
case . privateEntitlements ( let app , _ ) :
return String ( format : NSLocalizedString ( " “%@” requires private permissions. " , comment : " " ) , app . name )
2020-05-07 13:13:05 -07:00
case . mismatchedBundleIdentifiers ( let app , let sourceBundleID ) :
return String ( format : NSLocalizedString ( " The bundle ID “%@” does not match the one specified by the source (“%@”). " , comment : " " ) , app . bundleIdentifier , sourceBundleID )
2020-05-02 22:06:57 -07:00
}
}
}
@objc ( VerifyAppOperation )
class VerifyAppOperation : ResultOperation < Void >
{
let context : AppOperationContext
var verificationHandler : ( ( VerificationError ) -> Bool ) ?
init ( context : AppOperationContext )
{
self . context = context
super . init ( )
}
override func main ( )
{
super . main ( )
do
{
if let error = self . context . error
{
throw error
}
guard let app = self . context . app else { throw OperationError . invalidParameters }
2020-05-07 13:13:05 -07:00
guard app . bundleIdentifier = = self . context . bundleIdentifier else {
throw VerificationError . mismatchedBundleIdentifiers ( app , sourceBundleID : self . context . bundleIdentifier )
}
2020-05-02 22:06:57 -07:00
2020-05-07 13:13:05 -07:00
// M a k e s u r e t h i s g o e s l a s t , s i n c e o n c e u s e r r e s p o n d s t o a l e r t w e d o n ' t d o a n y m o r e a p p v e r i f i c a t i o n .
2020-05-02 22:06:57 -07:00
if let commentStart = app . entitlementsString . range ( of : " <!---><!--> " ) , let commentEnd = app . entitlementsString . range ( of : " <!-- --> " )
{
// P s y c h i c P a p e r p r i v a t e e n t i t l e m e n t s .
let entitlementsStart = app . entitlementsString . index ( after : commentStart . upperBound )
let rawEntitlements = String ( app . entitlementsString [ entitlementsStart . . < commentEnd . lowerBound ] )
let plistTemplate = " " "
<? xml version = " 1.0 " encoding = " UTF-8 " ? >
<! DOCTYPE plist PUBLIC " -//Apple Computer//DTD PLIST 1.0//EN " " http://www.apple.com/DTDs/PropertyList-1.0.dtd " >
< plist version = " 1.0 " >
< dict >
% @
</ dict >
</ plist >
" " "
let entitlementsPlist = String ( format : plistTemplate , rawEntitlements )
let entitlements = try PropertyListSerialization . propertyList ( from : entitlementsPlist . data ( using : . utf8 ) ! , options : [ ] , format : nil ) as ! [ String : Any ]
2020-05-07 13:13:05 -07:00
let error = VerificationError . privateEntitlements ( app , entitlements : entitlements )
2020-05-02 22:06:57 -07:00
self . process ( error ) { ( result ) in
self . finish ( result . mapError { $0 as Error } )
}
return
}
self . finish ( . success ( ( ) ) )
}
catch
{
self . finish ( . failure ( error ) )
}
}
}
private extension VerifyAppOperation
{
func process ( _ error : VerificationError , completion : @ escaping ( Result < Void , VerificationError > ) -> Void )
{
guard let presentingViewController = self . context . presentingViewController else { return completion ( . failure ( error ) ) }
DispatchQueue . main . async {
switch error
{
case . privateEntitlements ( _ , let entitlements ) :
let permissions = entitlements . keys . sorted ( ) . joined ( separator : " \n " )
let message = String ( format : NSLocalizedString ( " " "
You must allow access to these private permissions before continuing :
% @
Private permissions allow apps to do more than normally allowed by iOS , including potentially accessing sensitive private data . Make sure to only install apps from sources you trust .
" " " , comment: " " ), permissions)
let alertController = UIAlertController ( title : error . failureReason ? ? error . localizedDescription , message : message , preferredStyle : . alert )
alertController . addAction ( UIAlertAction ( title : NSLocalizedString ( " Allow Access " , comment : " " ) , style : . destructive ) { ( action ) in
completion ( . success ( ( ) ) )
} )
alertController . addAction ( UIAlertAction ( title : NSLocalizedString ( " Deny Access " , comment : " " ) , style : . default , handler : { ( action ) in
completion ( . failure ( error ) )
} ) )
presentingViewController . present ( alertController , animated : true , completion : nil )
2020-05-07 13:13:05 -07:00
case . mismatchedBundleIdentifiers : return completion ( . failure ( error ) )
2020-05-02 22:06:57 -07:00
}
}
}
}