rahmenlose Ansicht.m DruckansichtC {C[140] Corba[140] CS[140]}Mathematik
#import "MDMSetup.h"
#import "LicenseStore.h"
#import "UserSettings.h"
#import "MyIdentityStore.h"
#import "ThreemaFramework/ThreemaFramework-Swift.h"
#ifdef DEBUG
static const DDLogLevel ddLogLevel = DDLogLevelAll;
#else
static const DDLogLevel ddLogLevel = DDLogLevelNotice;
#endif
NSString * const MDM_CONFIGURATION_KEY = @"com.apple.configuration.managed"; // Company MDM persists in user defaults
NSString * const MDM_FEEDBACK_KEY = @"com.apple.feedback.managed";
NSString * const MDM_THREEMA_CONFIGURATION_KEY = @"threema_mdm_configuration"; // Threema MDM persists in user defaults
NSString * const MDM_KEY_LICENSE_USERNAME = @"th_license_username"; // String
NSString * const MDM_KEY_LICENSE_PASSWORD = @"th_license_password"; // String
NSString * const MDM_KEY_NICKNAME = @"th_nickname"; // String max 32
NSString * const MDM_KEY_LINKED_EMAIL = @"th_linked_email"; // String
NSString * const MDM_KEY_LINKED_PHONE = @"th_linked_phone"; // String
NSString * const MDM_KEY_FIRST_NAME = @"th_firstname"; // String
NSString * const MDM_KEY_LAST_NAME = @"th_lastname"; // String
NSString * const MDM_KEY_CSI = @"th_csi"; // String
NSString * const MDM_KEY_JOB_TITLE = @"th_job_title"; // String
NSString * const MDM_KEY_DEPARTMENT = @"th_department"; // String
NSString * const MDM_KEY_CATEGORY = @"th_category"; // String
NSString * const MDM_KEY_CONTACT_SYNC = @"th_contact_sync"; // Bool
NSString * const MDM_KEY_READONLY_PROFILE = @"th_readonly_profile"; // Bool
NSString * const MDM_KEY_ID_BACKUP = @"th_id_backup"; // String
NSString * const MDM_KEY_ID_BACKUP_PASSWORD = @"th_id_backup_password"; // String
NSString * const MDM_KEY_BLOCK_UNKNOWN = @"th_block_unknown"; // Bool
NSString * const MDM_KEY_HIDE_INACTIVE_IDS = @"th_hide_inactive_ids"; // Bool
NSString * const MDM_KEY_DISABLE_SAVE_TO_GALLERY = @"th_disable_save_to_gallery"; // Bool
NSString * const MDM_KEY_DISABLE_SCREENSHOTS = @"th_disable_screenshots"; // Bool
NSString * const MDM_KEY_DISABLE_ADD_CONTACT = @"th_disable_add_contact"; // Bool
NSString * const MDM_KEY_DISABLE_EXPORT = @"th_disable_export"; // Bool
NSString * const MDM_KEY_DISABLE_BACKUPS = @"th_disable_backups"; // Bool
NSString * const MDM_KEY_DISABLE_ID_EXPORT = @"th_disable_id_export"; // Bool
NSString * const MDM_KEY_DISABLE_SYSTEM_BACKUPS = @"th_disable_system_backups"; // Bool
NSString * const MDM_KEY_DISABLE_IOS_SYSTEM_BACKUPS_ID_KEY_INCLUSION = @"th_disable_ios_system_backups_id_key_inclusion"; // Bool
NSString * const MDM_KEY_DISABLE_MESSAGE_PREVIEW = @"th_disable_message_preview"; // Bool
NSString * const MDM_KEY_DISABLE_SEND_PROFILE_PICTURE = @"th_disable_send_profile_picture"; // Bool
NSString * const MDM_KEY_DISABLE_CALLS = @"th_disable_calls"; // Bool
NSString * const MDM_KEY_DISABLE_VIDEO_CALLS = @"th_disable_video_calls"; // String
NSString * const MDM_KEY_DISABLE_GROUP_CALLS = @"th_disable_group_calls"; // Bool
NSString * const MDM_KEY_DISABLE_CREATE_GROUP = @"th_disable_create_group"; // Bool
NSString * const MDM_KEY_SKIP_WIZARD = @"th_skip_wizard"; // Bool
NSString * const MDM_KEY_DISABLE_WEB = @"th_disable_web"; // Bool
NSString * const MDM_KEY_DISABLE_MULTIDEVICE = @"th_disable_multidevice"; // Bool
NSString * const MDM_KEY_WEB_HOSTS = @"th_web_hosts"; // String
NSString * const MDM_KEY_DISABLE_SHARE_MEDIA = @"th_disable_share_media"; // Bool
NSString * const MDM_KEY_DISABLE_WORK_DIRECTORY = @"th_disable_work_directory"; // Bool
NSString * const MDM_KEY_SAFE_ENABLE = @"th_safe_enable"; // Bool
NSString * const MDM_KEY_SAFE_PASSWORD = @"th_safe_password"; // String min. 8, max. 4096
NSString * const MDM_KEY_SAFE_SERVER_URL = @"th_safe_server_url"; // String
NSString * const MDM_KEY_SAFE_SERVER_USERNAME = @"th_safe_server_username"; // String
NSString * const MDM_KEY_SAFE_SERVER_PASSWORD = @"th_safe_server_password"; // String
NSString * const MDM_KEY_SAFE_RESTORE_ENABLE = @"th_safe_restore_enable"; // Bool
NSString * const MDM_KEY_SAFE_RESTORE_ID = @"th_safe_restore_id"; // String 8
NSString * const MDM_KEY_KEEP_MESSAGE_DAYS = @"th_keep_messages_days"; // Int
NSString * const MDM_KEY_SAFE_PASSWORD_PATTERN = @"th_safe_password_pattern"; // String
NSString * const MDM_KEY_SAFE_PASSWORD_MESSAGE = @"th_safe_password_message"; // String
NSString * const MDM_KEY_ONPREM_SERVER = @"th_onprem_server"; // String
NSString * const MDM_KEY_ENABLE_REMOTE_SECRET = @"th_enable_remote_secret"; // Bool
NSString * const MDM_KEY_THREEMA_CONFIGURATION = @"mdm";
NSString * const MDM_KEY_THREEMA_OVERRIDE = @"override";
NSString * const MDM_KEY_THREEMA_PARAMS = @"params";
static NSDictionary *_mdmCache;
static NSDictionary *_mdmCacheSetup;
@interface MDMSetup ()
@property (nonatomic, strong) NSSet *personalInfoKeys;
/// Collaborators, resolving to the shared instances unless they were injected
@property (nonatomic, readonly, nonnull) id<UserSettingsProtocol> userSettings;
@property (nonatomic, readonly, nonnull) id<LicenseStoreProtocol> licenseStore;
@property (nonatomic, readonly, nonnull) id<MyIdentityStoreProtocol> myIdentityStore;
// Test-only initializer. Declared here (framework-internal) only so this file compiles; it is NOT part
// of any public header, so the app can't call it. Test targets declare a matching `MDMSetup (Testing)`
// category in their bridging header, which dispatches to this implementation.
- (nonnull instancetype)initForTestWithAppSetupStateRawValue:(NSInteger)appSetupStateRawValue
isBusinessApp:(BOOL)businessApp
isOnPrem:(BOOL)onPrem
userSettings:(nonnull id<UserSettingsProtocol>)userSettings
licenseStore:(nonnull id<LicenseStoreProtocol>)licenseStore
myIdentityStore:(nonnull id<MyIdentityStoreProtocol>)myIdentityStore;
@end
@implementation MDMSetup {
/// Check if app can handle MDM
BOOL isBusinessApp;
/// Check is OnPrem App to handle Remote Secret
BOOL isOnPrem;
BOOL isSetup;
dispatch_queue_t queue;
/// Injected collaborators, `nil` means use the shared instance
id<UserSettingsProtocol> _injectedUserSettings;
id<LicenseStoreProtocol> _injectedLicenseStore;
id<MyIdentityStoreProtocol> _injectedMyIdentityStore;
}
// We use injected instances during testing
- (id<UserSettingsProtocol>)userSettings {
return _injectedUserSettings ?: [UserSettings sharedUserSettings];
}
- (id<LicenseStoreProtocol>)licenseStore {
return _injectedLicenseStore ?: [LicenseStore sharedLicenseStore];
}
- (id<MyIdentityStoreProtocol>)myIdentityStore {
return _injectedMyIdentityStore ?: [MyIdentityStore sharedMyIdentityStore];
}
- (MDMSetup*)init {
return [[MDMSetup alloc] initWithAppSetupStateRawValue:AppSetup.state];
}
- (MDMSetup*)initWithAppSetupStateRawValue:(NSInteger)appSetupStateRawValue {
self = [super init];
if (self) {
AppSetupState state = [self appSetupStateFromRawValue:appSetupStateRawValue];
isSetup = state != AppSetupStateComplete;
isBusinessApp = TargetManagerObjC.isBusinessApp;
isOnPrem = TargetManagerObjC.isOnPrem;
// Resolve eagerly: an MDMSetup created once app setup is complete needs its BusinessInjector for the
// multi-device sync of MDM parameters. Creating one before the Remote Secret is initialized fails here
// by design, surfacing any too-early MDM access instead of hiding it behind a lazy retry.
if (!isSetup) {
NSError *error;
_businessInjector = [[AppLaunchManager shared] businessForBackgroundProcess:YES error:&error];
if (error) {
DDLogError(@"Error while getting BusinessInjector: %@", error.description);
}
}
queue = dispatch_queue_create("ch.threema.MdmConfiguration", NULL);
_personalInfoKeys = [MDMSetup defaultPersonalInfoKeys];
}
return self;
}
- (nonnull instancetype)initForTestWithAppSetupStateRawValue:(NSInteger)appSetupStateRawValue
isBusinessApp:(BOOL)businessApp
isOnPrem:(BOOL)onPrem
userSettings:(nonnull id<UserSettingsProtocol>)userSettings
licenseStore:(nonnull id<LicenseStoreProtocol>)licenseStore
myIdentityStore:(nonnull id<MyIdentityStoreProtocol>)myIdentityStore {
self = [super init];
if (self) {
AppSetupState state = [self appSetupStateFromRawValue:appSetupStateRawValue];
isSetup = state != AppSetupStateComplete;
isBusinessApp = businessApp;
isOnPrem = onPrem;
_injectedUserSettings = userSettings;
_injectedLicenseStore = licenseStore;
_injectedMyIdentityStore = myIdentityStore;
queue = dispatch_queue_create("ch.threema.MdmConfiguration", NULL);
_personalInfoKeys = [MDMSetup defaultPersonalInfoKeys];
}
return self;
}
- (AppSetupState)appSetupStateFromRawValue:(NSInteger)rawValue {
switch (rawValue) {
case 10:
return AppSetupStateNotSetup;
case 20:
return AppSetupStateIdentityAdded;
case 30:
return AppSetupStateIdentitySetupComplete;
case 40:
return AppSetupStateComplete;
default:
[NSException raise:@"Invalid raw value for AppSetupState" format:@"raw value %ld is invalid", (long)rawValue];
return AppSetupStateNotSetup;
}
}
+ (void)clearMdmCache {
_mdmCache = nil;
_mdmCacheSetup = nil;
}
// MARK: MDM values
- (BOOL)disableBackups {
NSNumber *disableBackups = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_BACKUPS];
return [disableBackups isKindOfClass:[NSNumber class]] ? disableBackups.boolValue : NO;
}
- (BOOL)disableIdExport {
NSNumber *disableIdExport = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_ID_EXPORT];
return [disableIdExport isKindOfClass:[NSNumber class]] ? disableIdExport.boolValue : NO;
}
- (BOOL)disableSystemBackups {
NSNumber *disableSystemBackups = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_SYSTEM_BACKUPS];
return [disableSystemBackups isKindOfClass:[NSNumber class]] ? disableSystemBackups.boolValue : NO;
}
- (BOOL)disableIOSSystemBackupsIDKeyInclusion {
NSNumber *disableIOSSystemBackupsIDKeyInclusion = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_IOS_SYSTEM_BACKUPS_ID_KEY_INCLUSION];
return [disableIOSSystemBackupsIDKeyInclusion isKindOfClass:[NSNumber class]] ? disableIOSSystemBackupsIDKeyInclusion.boolValue : NO;
}
- (BOOL)readonlyProfile {
NSNumber *readonlyProfile = [self getMdmConfigurationBoolForKey:MDM_KEY_READONLY_PROFILE];
return [readonlyProfile isKindOfClass:[NSNumber class]] ? readonlyProfile.boolValue : NO;
}
- (BOOL)disableAddContact {
NSNumber *disableAddContact = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_ADD_CONTACT];
return [disableAddContact isKindOfClass:[NSNumber class]] ? disableAddContact.boolValue : NO;
}
- (BOOL)disableScreenshots {
NSNumber *disableScreenshots = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_SCREENSHOTS];
return [disableScreenshots isKindOfClass:[NSNumber class]] ? disableScreenshots.boolValue : NO;
}
- (BOOL)disableSaveToGallery {
NSNumber *disableSaveToGallery = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_SAVE_TO_GALLERY];
return [disableSaveToGallery isKindOfClass:[NSNumber class]] ? disableSaveToGallery.boolValue : NO;
}
- (BOOL)disableExport {
NSNumber *disableExport = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_EXPORT];
return [disableExport isKindOfClass:[NSNumber class]] ? disableExport.boolValue : NO;
}
- (BOOL)disableMessagePreview {
NSNumber *disableMessagePreview = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_MESSAGE_PREVIEW];
return [disableMessagePreview isKindOfClass:[NSNumber class]] ? disableMessagePreview.boolValue : NO;
}
- (BOOL)disableCalls {
NSNumber *disableCalls = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_CALLS];
return [disableCalls isKindOfClass:[NSNumber class]] ? disableCalls.boolValue : NO;
}
- (BOOL)disableVideoCalls {
NSNumber *disableVideoCalls = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_VIDEO_CALLS];
return [disableVideoCalls isKindOfClass:[NSNumber class]] ? disableVideoCalls.boolValue : NO;
}
- (BOOL)disableGroupCalls {
NSNumber *disableGroupCalls = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_GROUP_CALLS];
return [disableGroupCalls isKindOfClass:[NSNumber class]] ? disableGroupCalls.boolValue : NO;
}
- (BOOL)disableWeb {
NSNumber *disableWeb = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_WEB];
return [disableWeb isKindOfClass:[NSNumber class]] ? disableWeb.boolValue : NO;
}
/// If the MDM key for multi-device is set, use the value from the disableMultidevice key.
/// Otherwise, use the disableWeb key for multi-device.
- (BOOL)disableMultiDevice {
if ([self existsMdmKey:MDM_KEY_DISABLE_MULTIDEVICE]) {
NSNumber *disableMultiDevice = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_MULTIDEVICE];
return [disableMultiDevice isKindOfClass:[NSNumber class]] ? disableMultiDevice.boolValue : NO;
}
return [self disableWeb];
}
- (nullable NSString *)webHosts {
NSString *webHosts = [self getMdmConfigurationValueForKey:MDM_KEY_WEB_HOSTS];
return [webHosts isKindOfClass:[NSString class]] && webHosts.length > 0 ? webHosts : nil;
}
- (BOOL)disableCreateGroup {
NSNumber *disableCreateGroup = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_CREATE_GROUP];
return [disableCreateGroup isKindOfClass:[NSNumber class]] ? disableCreateGroup.boolValue : NO;
}
- (BOOL)disableSendProfilePicture {
NSNumber *disableSendProfilePicture = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_SEND_PROFILE_PICTURE];
return [disableSendProfilePicture isKindOfClass:[NSNumber class]] ? disableSendProfilePicture.boolValue : NO;
}
- (BOOL)skipWizard {
NSNumber *skipWizard = [self getMdmConfigurationBoolForKey:MDM_KEY_SKIP_WIZARD];
return [skipWizard isKindOfClass:[NSNumber class]] ? skipWizard.boolValue : NO;
}
- (BOOL)disableShareMedia {
NSNumber *disableShareMedia = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_SHARE_MEDIA];
return [disableShareMedia isKindOfClass:[NSNumber class]] ? disableShareMedia.boolValue : NO;
}
- (BOOL)disableWorkDirectory {
NSNumber *disableWorkDirectory = [self getMdmConfigurationBoolForKey:MDM_KEY_DISABLE_WORK_DIRECTORY];
return [disableWorkDirectory isKindOfClass:[NSNumber class]] ? disableWorkDirectory.boolValue : NO;
}
- (BOOL)disableHideStaleContacts {
NSNumber *disableHideInactiveIds = [self getMdmConfigurationBoolForKey:MDM_KEY_HIDE_INACTIVE_IDS];
return [disableHideInactiveIds isKindOfClass:[NSNumber class]] ? YES : NO;
}
- (nullable NSNumber*)safeEnable {
NSNumber *safeEnable = [self getMdmConfigurationBoolForKey:MDM_KEY_SAFE_ENABLE];
return [safeEnable isKindOfClass:[NSNumber class]] ? safeEnable : nil;
}
- (nullable NSString *)safePassword {
NSString *safePassword = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_PASSWORD];
return [safePassword isKindOfClass:[NSString class]] && safePassword.length > 0 ? safePassword : nil;
}
- (nullable NSString *)safeServerUrl {
NSString *safeServerUrl = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_SERVER_URL];
return [safeServerUrl isKindOfClass:[NSString class]] && safeServerUrl.length > 0 ? safeServerUrl : nil;
}
- (nullable NSString *)safeServerUsername {
NSString *safeServerUsername = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_SERVER_USERNAME];
return [safeServerUsername isKindOfClass:[NSString class]] && safeServerUsername.length > 0 ? safeServerUsername : nil;
}
- (nullable NSString *)safeServerPassword {
NSString *safeServerPassword = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_SERVER_PASSWORD];
return [safeServerPassword isKindOfClass:[NSString class]] && safeServerPassword.length > 0 ? safeServerPassword : nil;
}
- (BOOL)safeRestoreEnable {
NSNumber *safeRestoreEnable = [self getMdmConfigurationBoolForKey:MDM_KEY_SAFE_RESTORE_ENABLE];
return [safeRestoreEnable isKindOfClass:[NSNumber class]] ? safeRestoreEnable.boolValue : YES;
}
- (nullable NSString *)safeRestoreId {
NSString *safeRestoreId = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_RESTORE_ID];
return [safeRestoreId isKindOfClass:[NSString class]] && safeRestoreId.length > 0 ? safeRestoreId : nil;
}
- (nullable NSString *)safePasswordPattern {
NSString *safePasswordPattern = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_PASSWORD_PATTERN];
return [safePasswordPattern isKindOfClass:[NSString class]] && safePasswordPattern.length > 0 ? safePasswordPattern : nil;
}
- (nullable NSString *)safePasswordMessage {
NSString *safePasswordMessage = [self getMdmConfigurationValueForKey:MDM_KEY_SAFE_PASSWORD_MESSAGE];
return [safePasswordMessage isKindOfClass:[NSString class]] && safePasswordMessage.length > 0 ? safePasswordMessage : nil;
}
- (nullable NSString *)nickname {
NSString *nickname = [self getMdmConfigurationValueForKey:MDM_KEY_NICKNAME];
return [nickname isKindOfClass:[NSString class]] && nickname.length > 0 ? nickname : nil;
}
- (nullable NSString *)linkEmail {
NSString *linkEmail = [self getMdmConfigurationValueForKey:MDM_KEY_LINKED_EMAIL];
return [linkEmail isKindOfClass:[NSString class]] && linkEmail.length > 0 ? linkEmail : nil;
}
- (nullable NSString *)linkPhoneNumber {
NSString *linkPhoneNumber = [self getMdmConfigurationValueForKey:MDM_KEY_LINKED_PHONE];
return [linkPhoneNumber isKindOfClass:[NSString class]] && linkPhoneNumber.length > 0 ? linkPhoneNumber : nil;
}
- (BOOL)contactSync {
NSNumber *contactSync = [self getMdmConfigurationBoolForKey:MDM_KEY_CONTACT_SYNC];
return [contactSync isKindOfClass:[NSNumber class]] ? contactSync.boolValue : NO;
}
- (nullable NSNumber *)keepMessagesDays {
NSNumber *keepMessagesDays = [self getMdmConfigurationValueForKey:MDM_KEY_KEEP_MESSAGE_DAYS];
return [keepMessagesDays isKindOfClass:[NSNumber class]] ? keepMessagesDays : nil;
}
- (nullable NSString *)onPremConfigUrl {
NSString *onPremConfigUrl = [self getMdmConfigurationValueForKey:MDM_KEY_ONPREM_SERVER];
return [onPremConfigUrl isKindOfClass:[NSString class]] && onPremConfigUrl.length > 0 ? onPremConfigUrl : nil;
}
/// This function determines whether the remote secret is enabled. This feature is currently only available on OnPrem environments; all other targets return false.
- (BOOL)enableRemoteSecret {
NSNumber *enableRemoteSecret = [self getMdmConfigurationBoolForKey:MDM_KEY_ENABLE_REMOTE_SECRET];
if (isOnPrem) {
return [enableRemoteSecret isKindOfClass:[NSNumber class]] ? enableRemoteSecret.boolValue : NO;
}
else {
return NO;
}
}
// MARK: Threema Safe status
- (BOOL)isSafeBackupDisable {
return [[self safeSetupWork] isSafeBackupStatusSetWithSafeState:SafeSetupWork.backupDisable];
}
- (BOOL)isSafeBackupForce {
return [[self safeSetupWork] isSafeBackupStatusSetWithSafeState:SafeSetupWork.backupForce];
}
- (BOOL)isSafeBackupPasswordPreset {
return [[self safeSetupWork] isSafeBackupStatusSetWithSafeState:SafeSetupWork.passwordPreset];
}
- (BOOL)isSafeBackupServerPreset {
return [[self safeSetupWork] isSafeBackupStatusSetWithSafeState:SafeSetupWork.serverPreset];
}
- (BOOL)isSafeRestoreDisable {
return [[self safeSetupWork] isSafeRestoreStatusSetWithSafeState:SafeSetupWork.restoreDisable];
}
- (BOOL)isSafeRestoreForce {
return [[self safeSetupWork] isSafeRestoreStatusSetWithSafeState:SafeSetupWork.restoreForce];
}
- (BOOL)isSafeRestorePasswordPreset {
return [[self safeSetupWork] isSafeRestoreStatusSetWithSafeState:SafeSetupWork.passwordPreset];
}
- (BOOL)isSafeRestoreServerPreset {
return [[self safeSetupWork] isSafeRestoreStatusSetWithSafeState:SafeSetupWork.serverPreset];
}
- (SafeSetupWork*)safeSetupWork {
return [[SafeSetupWork alloc] initWithMdmSetup:self];
}
// MARK: apply MDM to user settings, identity
- (void)loadRenewableValues {
if (!isBusinessApp) {
return;
}
[self loadLicenseInfo];
id<UserSettingsProtocol> userSettings = self.userSettings;
NSNumber *blockUnknown = [self getMdmConfigurationBoolForKey:MDM_KEY_BLOCK_UNKNOWN];
if ([blockUnknown isKindOfClass:[NSNumber class]]) {
userSettings.blockUnknown = blockUnknown.boolValue;
}
NSNumber *hideInactiveIds = [self getMdmConfigurationBoolForKey:MDM_KEY_HIDE_INACTIVE_IDS];
if ([hideInactiveIds isKindOfClass:[NSNumber class]]) {
userSettings.hideStaleContacts = hideInactiveIds.boolValue;
}
NSNumber *contactSync = [self getMdmConfigurationBoolForKey:MDM_KEY_CONTACT_SYNC];
if ([contactSync isKindOfClass:[NSNumber class]]) {
userSettings.syncContacts = contactSync.boolValue;
}
if ([self existsMdmKey:MDM_KEY_DISABLE_SAVE_TO_GALLERY]) {
userSettings.autoSaveMedia = ![self disableSaveToGallery];
}
if ([self existsMdmKey:MDM_KEY_DISABLE_MESSAGE_PREVIEW]) {
userSettings.pushDecrypt = ![self disableMessagePreview];
}
if ([self existsMdmKey:MDM_KEY_DISABLE_CALLS]) {
if ([ThreemaEnvironment supportsCallKit]) {
userSettings.enableThreemaCall = ![self disableCalls];
if ([self disableCalls]) {
userSettings.enableThreemaGroupCalls = false;
}
}
else {
userSettings.enableThreemaCall = false;
}
}
if ([self existsMdmKey:MDM_KEY_DISABLE_GROUP_CALLS]) {
// TODO: IOS-3878 OS Integration
if ([ThreemaEnvironment supportsCallKit]) {
userSettings.enableThreemaGroupCalls = ![self disableGroupCalls];
}
else {
userSettings.enableThreemaGroupCalls = false;
}
}
if ([self existsMdmKey:MDM_KEY_DISABLE_VIDEO_CALLS]) {
userSettings.enableVideoCall= ![self disableVideoCalls];
}
if ([self existsMdmKey:MDM_KEY_DISABLE_SCREENSHOTS]) {
userSettings.allowScreenshot = ![self disableScreenshots];
}
if ([self existsMdmKey:MDM_KEY_DISABLE_SEND_PROFILE_PICTURE]) {
if ([self disableSendProfilePicture]) {
[userSettings setSendProfilePicture:SendProfilePictureNone];
}
}
if ([self existsMdmKey:MDM_KEY_DISABLE_WORK_DIRECTORY]) {
userSettings.companyDirectory = ![self disableWorkDirectory];
}
if ([self existsMdmKey:MDM_KEY_DISABLE_MULTIDEVICE]) {
if ([self disableMultiDevice]) {
[self runDisableMultiDeviceWithCompletionHandler:^{
// do nothing
}];
}
}
else if ([self existsMdmKey:MDM_KEY_DISABLE_WEB] && [self disableWeb]) {
[self runDisableMultiDeviceWithCompletionHandler:^{
// do nothing
}];
}
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationSettingStoreSynchronization object:nil];
}
- (void)loadLicenseInfo {
if (!isBusinessApp) {
return;
}
id<LicenseStoreProtocol> licenseStore = self.licenseStore;
NSString *licenseUsername = [self getMdmConfigurationValueForKey:MDM_KEY_LICENSE_USERNAME];
if ([licenseUsername isKindOfClass:[NSString class]] && licenseUsername.length > 0) {
licenseStore.licenseUsername = licenseUsername;
}
NSString *licensePassword = [self getMdmConfigurationValueForKey:MDM_KEY_LICENSE_PASSWORD];
if ([licensePassword isKindOfClass:[NSString class]] && licensePassword.length > 0) {
licenseStore.licensePassword = licensePassword;
}
NSString *onPremConfigUrl = [self onPremConfigUrl];
if (onPremConfigUrl != nil && [licenseStore validCustomOnPremConfigUrlWithPredefinedUrl:onPremConfigUrl]) {
licenseStore.onPremConfigUrl = onPremConfigUrl;
}
}
- (void)loadIDCreationValues {
// We should set firstname, lastname, csi and category when mdm is empty
// Do nothing for consumer
if (!isBusinessApp) {
return;
}
id<MyIdentityStoreProtocol> identityStore = self.myIdentityStore;
NSString *nickname = [self getMdmConfigurationValueForKey:MDM_KEY_NICKNAME];
if ([nickname isKindOfClass:[NSString class]] && nickname.length > 0 && nickname.length < 32) {
identityStore.pushFromName = nickname;
}
NSNumber *contactSync = [self getMdmConfigurationBoolForKey:MDM_KEY_CONTACT_SYNC];
if ([contactSync isKindOfClass:[NSNumber class]]) {
self.userSettings.syncContacts = contactSync.boolValue;
}
NSString *email = [self getMdmConfigurationValueForKey:MDM_KEY_LINKED_EMAIL];
if ([email isKindOfClass:[NSString class]] && email.length > 0) {
identityStore.createIDEmail = email;
}
NSString *phone = [self getMdmConfigurationValueForKey:MDM_KEY_LINKED_PHONE];
if ([phone isKindOfClass:[NSString class]] && phone.length > 0) {
identityStore.createIDPhone = phone;
}
// Set the firstname to nil if the key is not set or the value is empty
// because the user can't edit this value
if ([self existsMdmKey:MDM_KEY_FIRST_NAME]) {
NSString *firstName = [self getMdmConfigurationValueForKey:MDM_KEY_FIRST_NAME];
if ([firstName isKindOfClass:[NSString class]]) {
identityStore.firstName = firstName;
} else {
DDLogError(@"[MDMSetup] loadIDCreationValues firstName is not a string");
identityStore.firstName = nil;
}
} else {
identityStore.firstName = nil;
}
// Set the lastname to nil if the key is not set or the value is empty
// because the user can't edit this value
if ([self existsMdmKey:MDM_KEY_LAST_NAME]) {
NSString *lastName = [self getMdmConfigurationValueForKey:MDM_KEY_LAST_NAME];
if ([lastName isKindOfClass:[NSString class]]) {
identityStore.lastName = lastName;
} else {
DDLogError(@"[MDMSetup] loadIDCreationValues lastName is not a string");
identityStore.lastName = nil;
}
} else {
identityStore.lastName = nil;
}
// Set the csi to nil if the key is not set or the value is empty
// because the user can't edit this value
if ([self existsMdmKey:MDM_KEY_CSI]) {
NSString *csi = [self getMdmConfigurationValueForKey:MDM_KEY_CSI];
if ([csi isKindOfClass:[NSString class]]) {
identityStore.csi = csi;
} else {
DDLogError(@"[MDMSetup] loadIDCreationValues csi is not a string");
identityStore.csi = nil;
}
} else {
identityStore.csi = nil;
}
// Set the job title to nil if the key is not set or the value is empty
// because the user can't edit this value
if ([self existsMdmKey:MDM_KEY_JOB_TITLE]) {
NSString *jobTitle = [self getMdmConfigurationValueForKey:MDM_KEY_JOB_TITLE];
if ([jobTitle isKindOfClass:[NSString class]]) {
identityStore.jobTitle = jobTitle;
} else {
DDLogError(@"[MDMSetup] loadIDCreationValues jobTitle is not a string");
identityStore.jobTitle = nil;
}
} else {
identityStore.jobTitle = nil;
}
// Set the department to nil if the key is not set or the value is empty
// because the user can't edit this value
if ([self existsMdmKey:MDM_KEY_DEPARTMENT]) {
NSString *department = [self getMdmConfigurationValueForKey:MDM_KEY_DEPARTMENT];
if ([department isKindOfClass:[NSString class]]) {
identityStore.department = department;
} else {
DDLogError(@"[MDMSetup] loadIDCreationValues department is not a string");
identityStore.department = nil;
}
} else {
identityStore.department = nil;
}
// Set the category to nil if the key is not set or the value is empty
// because the user can't edit this value
if ([self existsMdmKey:MDM_KEY_CATEGORY]) {
NSString *category = [self getMdmConfigurationValueForKey:MDM_KEY_CATEGORY];
if ([category isKindOfClass:[NSString class]]) {
identityStore.category = category;
} else {
DDLogError(@"[MDMSetup] loadIDCreationValues category is not a string");
identityStore.category = nil;
}
} else {
identityStore.category = nil;
}
}
- (BOOL)hasIDBackup {
if (!isBusinessApp) {
return NO;
}
_idBackup = [self getMdmConfigurationValueForKey:MDM_KEY_ID_BACKUP];
if ([_idBackup isKindOfClass:[NSString class]] == NO || _idBackup.length < 1) {
return NO;
}
_idBackupPassword = [self getMdmConfigurationValueForKey:MDM_KEY_ID_BACKUP_PASSWORD];
return YES;
}
- (void)restoreIDBackupOnCompletion:(void(^_Nonnull)(void))onCompletion onError:(void(^_Nonnull)(NSError *error))onError {
if ([self hasIDBackup] == NO) {
return;
}
// Not routed through `self.myIdentityStore`: `restoreFromBackup:withPassword:` is not part of
// `MyIdentityStoreProtocol`
MyIdentityStore *identityStore = [MyIdentityStore sharedMyIdentityStore];
id<LicenseStoreProtocol> licenseStore = self.licenseStore;
[identityStore restoreFromBackup:_idBackup withPassword:_idBackupPassword onCompletion:^{
ServerAPIConnector *apiConnector = [[ServerAPIConnector alloc] init];
/* Obtain server group from server */
[apiConnector updateMyIdentityStore:identityStore onCompletion:^{
[licenseStore performUpdateWorkInfo];
onCompletion();
} onError:^(NSError *error) {
onError(error);
}];
} onError:^(NSError *error) {
onError(error);
}];
}
- (nullable NSString *)supportDescriptionString {
NSString *supportDescriptionString;
if ([[self getThreemaMDM] valueForKey:MDM_KEY_THREEMA_PARAMS] != nil && [[[self getThreemaMDM] valueForKey:MDM_KEY_THREEMA_PARAMS] allKeys].count > 0) {
supportDescriptionString = [@"" stringByAppendingString:@"m"];
}
if ([self getCompanyMDM] != nil && [[self getCompanyMDM] allKeys].count > 0) {
if (supportDescriptionString != nil) {
supportDescriptionString = [supportDescriptionString stringByAppendingString:@"e"];
} else {
supportDescriptionString = [@"" stringByAppendingString:@"e"];
}
}
return supportDescriptionString;
}
- (BOOL)existsMdmKey:(nonnull NSString*)mdmKey {
NSDictionary *mdm = [self getMdmConfiguration];
return [[mdm allKeys] containsObject:mdmKey];
}
- (void)applyCompanyMDMWithCachedThreemaMDMSendForce:(BOOL)sendForce {
if ([AppGroup getCurrentType] != AppGroupTypeApp) {
return;
}
NSMutableDictionary *workData = [NSMutableDictionary new];
NSDictionary *threemaMdm = [self getThreemaMDM];
if (threemaMdm != nil) {
[workData setObject:threemaMdm forKey:MDM_KEY_THREEMA_CONFIGURATION];
}
[self applyThreemaMdm:workData sendForce:sendForce];
}
- (void)applyThreemaMdm:(NSDictionary *)workData sendForce:(BOOL)sendForce {
if (!isBusinessApp) {
return;
}
dispatch_sync(queue, ^{
NSUserDefaults *defaults = AppGroup.userDefaults;
NSDictionary *threemaMdm = nil;
// Only update MDM settings if key exists in work data
// This means MDM settings are only reset if the key is returned with no settings in it.
if (workData != nil && [[workData allKeys] containsObject:MDM_KEY_THREEMA_CONFIGURATION]) {
threemaMdm = [defaults dictionaryForKey:MDM_THREEMA_CONFIGURATION_KEY];
NSDictionary *newThreemaMdm = workData[MDM_KEY_THREEMA_CONFIGURATION];
NSMutableDictionary *currentThreemaMdm = [[NSMutableDictionary alloc] initWithDictionary:threemaMdm];
if([currentThreemaMdm isEqualToDictionary:newThreemaMdm] == NO) {
// remove missing Threema MDM parameters
NSMutableArray *missingMdmKeys = [[NSMutableArray alloc] init];
NSMutableDictionary *currentThreemaMdmParameters = [[NSMutableDictionary alloc] initWithDictionary:currentThreemaMdm[MDM_KEY_THREEMA_PARAMS]];
[currentThreemaMdmParameters enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if ([[newThreemaMdm[MDM_KEY_THREEMA_PARAMS] allKeys] containsObject:key] == NO) {
[missingMdmKeys addObject:key];
}
}];
[currentThreemaMdmParameters removeObjectsForKeys:missingMdmKeys];
[currentThreemaMdm setObject:currentThreemaMdmParameters forKey:MDM_KEY_THREEMA_PARAMS];
// apply new Threema MDM parameters
BOOL override = ((NSNumber *)newThreemaMdm[MDM_KEY_THREEMA_OVERRIDE]).boolValue;
NSDictionary *newThreemaMdmParameters = [self applyMdmParameters:currentThreemaMdm[MDM_KEY_THREEMA_PARAMS] source:newThreemaMdm[MDM_KEY_THREEMA_PARAMS] override:override];
NSMutableDictionary *newThreemaMdmConfiguration = [[NSMutableDictionary alloc] initWithDictionary:threemaMdm];
[newThreemaMdmConfiguration setObject:newThreemaMdmParameters forKey:MDM_KEY_THREEMA_PARAMS];
[newThreemaMdmConfiguration setObject:[NSNumber numberWithBool:override] forKey:MDM_KEY_THREEMA_OVERRIDE];
threemaMdm = newThreemaMdmConfiguration;
}
// store Threema MDM
[defaults setObject:threemaMdm forKey:MDM_THREEMA_CONFIGURATION_KEY];
[defaults synchronize];
}
[MDMSetup clearMdmCache];
});
[self loadIDCreationValues];
[self loadRenewableValues];
[self.licenseStore performUpdateWorkInfoForce:sendForce];
[self sync];
[self syncSettingCalls];
}
/// Obtain the company’s MDM
///
/// If you intend to simulate the company directory, you can directly return a custom dictionary.
///
/// *Example to fake the company MDM*
///
/// return @{MDM_KEY_SAFE_PASSWORD: @"pw123456", MDM_KEY_SAFE_SERVER_USERNAME: @"user"};
///
- (nullable NSDictionary*)getCompanyMDM {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
return [defaults dictionaryForKey:MDM_CONFIGURATION_KEY];
}
- (nullable NSDictionary*)getThreemaMDM {
NSUserDefaults *defaults = AppGroup.userDefaults;
return [defaults dictionaryForKey:MDM_THREEMA_CONFIGURATION_KEY];
}
- (NSDictionary*)getMdmConfiguration {
__block NSDictionary *mdm;
dispatch_sync(queue, ^{
if (isBusinessApp) {
if (!isSetup) {
if (_mdmCache == nil) {
_mdmCache = [self getMDMParameters:[self getCompanyMDM] threemaMDM:[self getThreemaMDM]];
if ([_mdmCache valueForKey:MDM_KEY_SAFE_PASSWORD] != nil) {
[[NSNotificationCenter defaultCenter] postNotificationName:kSafeBackupPasswordCheck object:nil userInfo:nil];
}
}
mdm = _mdmCache;
} else {
if (_mdmCacheSetup == nil) {
_mdmCacheSetup = [self getMDMParameters:[self getCompanyMDM] threemaMDM:[self getThreemaMDM]];
if ([_mdmCacheSetup valueForKey:MDM_KEY_SAFE_PASSWORD] != nil) {
[[NSNotificationCenter defaultCenter] postNotificationName:kSafeBackupPasswordCheck object:nil userInfo:nil];
}
}
mdm = _mdmCacheSetup;
}
} else {
mdm = [[NSDictionary alloc] init];
}
});
return mdm;
}
- (NSDictionary*)getMDMParameters:(NSDictionary*)companyMdm threemaMDM:(NSDictionary*)threemaMdm {
if (companyMdm != nil) {
if (threemaMdm != nil) {
// Merge company and Threema MDM
BOOL override = ((NSNumber *)threemaMdm[MDM_KEY_THREEMA_OVERRIDE]).boolValue;
NSDictionary *newMdm = [self applyMdmParameters:companyMdm source:threemaMdm[MDM_KEY_THREEMA_PARAMS] override:override];
DDLogNotice(@"\n[MDMSetup] Company MDM");
[self printMDMIntoDebugLog:companyMdm];
DDLogNotice(@"\n[MDMSetup] Threema MDM");
[self printMDMIntoDebugLog:threemaMdm];
DDLogNotice(@"\n[MDMSetup] Merged Company and Threema MDM");
[self printMDMIntoDebugLog:newMdm];
return newMdm;
} else {
// use Company MDM
DDLogNotice(@"\n[MDMSetup] Company MDM");
[self printMDMIntoDebugLog:companyMdm];
return companyMdm;
}
} else if (threemaMdm != nil && [[threemaMdm allKeys] containsObject:MDM_KEY_THREEMA_PARAMS]) {
// use Threema MDM
NSDictionary *destinationMdm = [[NSDictionary alloc] init];
BOOL override = ((NSNumber *)threemaMdm[MDM_KEY_THREEMA_OVERRIDE]).boolValue;
NSDictionary *newMdm = [self applyMdmParameters:destinationMdm source:threemaMdm[MDM_KEY_THREEMA_PARAMS] override:override];
DDLogNotice(@"[MDMSetup] Threema MDM");
[self printMDMIntoDebugLog:newMdm];
return newMdm;
}
// Print empty mdm
DDLogNotice(@"[MDMSetup] Company and Threema MDM is empty");
return [[NSDictionary alloc] init];
}
- (NSDictionary*)applyMdmParameters:(NSDictionary*)destination source:(NSDictionary*)source override:(BOOL)override {
NSMutableDictionary *mdmParameters = [[NSMutableDictionary alloc] initWithDictionary:destination];
// Apply parameter if is override and renewable or missing
[[source allKeys] enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL * _Nonnull stop) {
if ([self shouldDiscardInThreemaMDM:key]) {
return;
}
if (override == YES && [self isRenewable:key]) {
if ([[mdmParameters allKeys] containsObject:key]) {
[mdmParameters setValue:source[key] forKey:key];
} else {
[mdmParameters addEntriesFromDictionary:@{key: source[key]}];
}
}
if (isSetup == YES && [[mdmParameters allKeys] containsObject:key] == NO) {
[mdmParameters addEntriesFromDictionary:@{key: source[key]}];
}
else if (isSetup == YES && override == YES && [[mdmParameters allKeys] containsObject:key] == YES) {
[mdmParameters setValue:source[key] forKey:key];
}
}];
return mdmParameters;
}
- (void)deleteThreemaMdm {
dispatch_sync(queue, ^{
NSUserDefaults *defaults = AppGroup.userDefaults;
[defaults removeObjectForKey:MDM_THREEMA_CONFIGURATION_KEY];
[defaults synchronize];
[MDMSetup clearMdmCache];
});
}
- (BOOL)isRenewable:(NSString *)mdmKey {
NSArray *renewableKeys = @[MDM_KEY_LICENSE_USERNAME, MDM_KEY_LICENSE_PASSWORD, MDM_KEY_NICKNAME, MDM_KEY_FIRST_NAME, MDM_KEY_LAST_NAME, MDM_KEY_CSI, MDM_KEY_JOB_TITLE, MDM_KEY_DEPARTMENT, MDM_KEY_CATEGORY, MDM_KEY_READONLY_PROFILE, MDM_KEY_BLOCK_UNKNOWN, MDM_KEY_HIDE_INACTIVE_IDS, MDM_KEY_DISABLE_SAVE_TO_GALLERY, MDM_KEY_DISABLE_SCREENSHOTS, MDM_KEY_DISABLE_ADD_CONTACT, MDM_KEY_DISABLE_EXPORT, MDM_KEY_DISABLE_BACKUPS, MDM_KEY_DISABLE_ID_EXPORT, MDM_KEY_DISABLE_SYSTEM_BACKUPS, MDM_KEY_DISABLE_IOS_SYSTEM_BACKUPS_ID_KEY_INCLUSION, MDM_KEY_DISABLE_MESSAGE_PREVIEW, MDM_KEY_DISABLE_SEND_PROFILE_PICTURE, MDM_KEY_DISABLE_CALLS, MDM_KEY_DISABLE_GROUP_CALLS, MDM_KEY_DISABLE_CREATE_GROUP, MDM_KEY_DISABLE_WEB, MDM_KEY_DISABLE_MULTIDEVICE, MDM_KEY_WEB_HOSTS, MDM_KEY_SAFE_ENABLE, MDM_KEY_SAFE_PASSWORD, MDM_KEY_SAFE_SERVER_URL, MDM_KEY_SAFE_SERVER_USERNAME, MDM_KEY_SAFE_SERVER_PASSWORD, MDM_KEY_SAFE_PASSWORD_PATTERN, MDM_KEY_SAFE_PASSWORD_MESSAGE, MDM_KEY_DISABLE_SHARE_MEDIA, MDM_KEY_DISABLE_WORK_DIRECTORY, MDM_KEY_CONTACT_SYNC, MDM_KEY_DISABLE_VIDEO_CALLS, MDM_KEY_ONPREM_SERVER, MDM_KEY_KEEP_MESSAGE_DAYS, MDM_KEY_ENABLE_REMOTE_SECRET];
return [renewableKeys containsObject:mdmKey];
}
- (BOOL)shouldDiscardInThreemaMDM:(NSString *)mdmKey{
NSArray *notAvailableInThreemaMDM = @[MDM_KEY_ID_BACKUP, MDM_KEY_ID_BACKUP_PASSWORD, MDM_KEY_SAFE_PASSWORD, MDM_KEY_LICENSE_USERNAME, MDM_KEY_LICENSE_PASSWORD];
return [notAvailableInThreemaMDM containsObject:mdmKey];
}
- (NSNumber*)getMdmConfigurationBoolForKey:(NSString*)key {
// Some MDMs cannot send real booleans, so we support "true"/"1" and "false"/"0" as strings also
id mdmVal = [self getMdmConfigurationValueForKey:key];
if ([mdmVal isKindOfClass:[NSNumber class]]) {
return mdmVal;
} else if ([mdmVal isKindOfClass:[NSString class]]) {
if ([mdmVal caseInsensitiveCompare:@"true"] == NSOrderedSame || [mdmVal isEqualToString:@"1"]) {
return [NSNumber numberWithBool:YES];
} else if ([mdmVal caseInsensitiveCompare:@"false"] == NSOrderedSame || [mdmVal isEqualToString:@"0"]) {
return [NSNumber numberWithBool:NO];
}
}
return nil;
}
- (id)getMdmConfigurationValueForKey:(NSString*)key {
NSDictionary *mdm = [self getMdmConfiguration];
return mdm[key];
}
- (void)printMDMIntoDebugLog:(NSDictionary *)mdm {
if ([[mdm allKeys] containsObject:MDM_KEY_THREEMA_PARAMS]) {
BOOL override = ((NSNumber *)mdm[MDM_KEY_THREEMA_OVERRIDE]).boolValue;
DDLogNotice(@"[MDMSetup] %@: %@", MDM_KEY_THREEMA_OVERRIDE, override ? @"true" : @"false");
for(NSString *key in mdm[MDM_KEY_THREEMA_PARAMS]) {
[self checkIsPersonalInfoAndLog:key value:[mdm[MDM_KEY_THREEMA_PARAMS] objectForKey:key]];
}
} else {
for(NSString *key in mdm) {
[self checkIsPersonalInfoAndLog:key value:[mdm objectForKey:key]];
}
}
}
+ (NSSet *)defaultPersonalInfoKeys {
return [NSSet setWithArray:@[
MDM_KEY_LICENSE_USERNAME,
MDM_KEY_FIRST_NAME,
MDM_KEY_LAST_NAME,
MDM_KEY_CSI,
MDM_KEY_CATEGORY,
MDM_KEY_JOB_TITLE,
MDM_KEY_DEPARTMENT,
MDM_KEY_NICKNAME,
]];
}
- (BOOL)isPersonalInfoKey:(NSString *)key {
return [self.personalInfoKeys containsObject:key];
}
- (void)checkIsPersonalInfoAndLog:(NSString *)key value:(NSString *)value {
if ([key containsString:@"pass"] && ![key isEqualToString:MDM_KEY_SAFE_PASSWORD_PATTERN] && ![key isEqualToString:MDM_KEY_SAFE_PASSWORD_MESSAGE] && value != nil) {
DDLogNotice(@"[MDMSetup] %@: ***", key);
}
else if (value != nil && [value isKindOfClass:[NSString class]] && [self isPersonalInfoKey:key]) {
if ([value length] == 0) {
DDLogNotice(@"[MDMSetup] %@: [empty]", key);
}
else if ([value length] <= 2) {
DDLogNotice(@"[MDMSetup] %@: %@***", key, [value substringToIndex:value.length - 1]);
}
else {
DDLogNotice(@"[MDMSetup] %@: %@***", key, [value substringToIndex:2]);
}
}
else {
DDLogNotice(@"[MDMSetup] %@: %@", key, value);
}
}
@end