Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Threema/ThreemaFramework/Business/MDMSetup/   (Social Medium ©)  Datei vom 26.9.2026 mit Größe 42 kB image not shown  

Quelle  MDMSetup.m  Sprache: unbekannt

 
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

[zur Elbe Produktseite wechseln0.58Quellennavigators]