SOSViewManager.c   [plain text]


/*
 * Copyright (c) 2015 Apple Inc. All Rights Reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

/*
 * SOSViewManager.c -  Implementation of a view manager
 */

#include <Security/SecureObjectSync/SOSViewManager.h>
#include <Security/SecureObjectSync/SOSInternal.h>

#if 0
/* SOSViewManager implementation. */
struct __OpaqueSOSViewManager {
    CFRuntimeBase _base;
    CFMutableDictionaryRef views;
};

const CFStringRef kSOSContextChildInfoKey = CFSTR("cntx");
const CFStringRef kSOSFunctionChildInfoKey = CFSTR("fctn");
const CFStringRef kSOSViewNamesChildInfoKey = CFSTR("vwns");


static CFStringRef SOSViewManagerCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
    SOSViewManagerRef vmgr = (SOSViewManagerRef)cf;
    CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorDefault, formatOptions, CFSTR("<ViewManager %@ >"), vmgr->views);
    return desc;
}

static void SOSViewManagerDestroy(CFTypeRef cf) {
    SOSViewManagerRef vmgr = (SOSViewManagerRef)cf;
    CFReleaseSafe(vmgr->views);
}

CFGiblisFor(SOSViewManager);


static SOSViewManagerRef SOSViewManagerCreate(CFAllocatorRef allocator, CFErrorRef *error) {
    SOSViewManagerRef vmgr = NULL;
    vmgr = CFTypeAllocate(SOSViewManager, struct __OpaqueSOSViewManager, allocator);
    if (vmgr)
        vmgr->views = CFDictionaryCreateMutableForCFTypes(allocator);
    return vmgr;
}

CFGiblisGetSingleton(SOSViewManagerRef, SOSGetViewManager, sSOSViewManager,  ^{
    *sSOSViewManager = SOSViewManagerCreate(kCFAllocatorDefault, NULL);
});


static CFStringRef CFStringCreateWithViewNames(CFArrayRef viewNames) {
    CFIndex count = CFArrayGetCount(viewNames);
    CFMutableArrayRef mvn = CFArrayCreateMutableCopy(kCFAllocatorDefault, count, viewNames);
    CFArraySortValues(mvn, CFRangeMake(0, count), (CFComparatorFunction)CFStringCompare, 0);
    CFStringRef string = CFStringCreateByCombiningStrings(kCFAllocatorDefault, mvn, CFSTR(":"));
    CFRelease(mvn);
    return string;
}

static SOSViewRef SOSViewManangerCopyViewWithName(SOSViewManagerRef vmgr, CFMutableDictionaryRef referencedViews, CFStringRef viewName, bool isConcrete, CFErrorRef *error) {
    SOSViewRef view = (SOSViewRef)CFDictionaryGetValue(vmgr->views, viewName);
    if (view) {
        if (isConcrete)
            SOSViewSetConcrete(view, true);
        CFRetain(view);
    } else {
        view = SOSViewCreate(CFGetAllocator(vmgr), isConcrete, NULL, error);
        if (view) {
            CFDictionarySetValue(vmgr->views, viewName, view);
        }
        // TODO: Query for the initial manifest.
    }
    if (view) {
        if (isConcrete)
            CFDictionarySetValue(referencedViews, viewName, kCFBooleanTrue);
        else if (!CFDictionaryContainsKey(referencedViews, viewName))
            CFDictionarySetValue(referencedViews, viewName, kCFBooleanFalse);
    }
    return view;
}

static SOSViewRef SOSViewManangerCopyCompositeViewWithNames(SOSViewManagerRef vmgr, CFMutableDictionaryRef referencedViews, CFArrayRef viewNames, CFErrorRef *error) {
    CFStringRef compositeName = CFStringCreateWithViewNames(viewNames);
    SOSViewRef compositeView = (SOSViewRef)CFDictionaryGetValue(vmgr->views, compositeName);
    if (compositeView) {
        CFDictionarySetValue(referencedViews, compositeName, kCFBooleanTrue);
        CFRetain(compositeView);
    } else {
        compositeView = SOSViewCreate(CFGetAllocator(vmgr), true, NULL, error);
        if (compositeView) {
            // Find the views for each name, and add the new view as a child to each one.
            CFStringRef viewName;
            CFArrayForEachC(viewNames, viewName) {
                SOSViewRef parent = SOSViewManangerCopyViewWithName(vmgr, referencedViews, viewName, false, error);
                if (!parent) {
                    CFReleaseNull(compositeView);
                    break;
                }
                SOSViewAddChild(parent, compositeView);
                // Update the composite view's manifest by adding each parents manifest.
                // TODO: Potentially move this out of the loop and create a single multi way manifest union operation
                SOSManifestRef pmf = SOSViewCopyManifest(parent, error);
                SOSViewUpdateManifest(compositeView, kSOSDataSourceSOSTransaction, NULL, pmf, error);
                CFReleaseSafe(pmf);
            }
            CFDictionarySetValue(vmgr->views, viewName, compositeView);
        }
    }
    CFReleaseSafe(compositeName);
    return compositeView;
}

static bool SOSViewManangerAddChildWithInfo(SOSViewManagerRef vmgr, CFMutableDictionaryRef referencedViews, CFDictionaryRef childInfo, CFErrorRef *error) {
    CFArrayRef viewNames = (CFArrayRef)CFDictionaryGetValue(childInfo, kSOSViewNamesChildInfoKey);
//    const void *context = (const void *)CFDictionaryGetValue(childInfo, kSOSContextChildInfoKey);
//    const void *func = (const void *)CFDictionaryGetValue(childInfo, kSOSFunctionChildInfoKey);

    CFIndex count = CFArrayGetCount(viewNames);
    if (count == 1) {
        CFStringRef key = CFArrayGetValueAtIndex(viewNames, 0);
        SOSViewRef view = SOSViewManangerCopyViewWithName(vmgr, referencedViews, key, true, error);
        // TODO: Fix this...
        //SOSViewAddClient(view, context, func);
        if (view) count++;  // TODO: REMOVE MOVE -- HERE ONLY FOR COMPILER WARNING
    } else if (count > 1) {
        SOSViewRef view = SOSViewManangerCopyCompositeViewWithNames(vmgr, referencedViews, viewNames, error);
        // TODO: Fix this...
        //SOSViewAddClient(view, context, func);
        if (view) count++;  // TODO: REMOVE MOVE -- HERE ONLY FOR COMPILER WARNING
    }

    return true;
}

struct SOSViewManagerContext {
    SOSViewManagerRef vmgr;
    CFDictionaryRef referencedViews;
};

static void SOSViewManagerUpdateView(const void *key, const void *value, void *context) {
    struct SOSViewManagerContext *vmc = context;
    CFBooleanRef isConcrete = CFDictionaryGetValue(vmc->referencedViews, key);
    if (!isConcrete) {
        CFDictionaryRemoveValue(vmc->vmgr->views, key);
    } else if (CFBooleanGetValue(isConcrete) == 0) {
        SOSViewRef view = (SOSViewRef)CFDictionaryGetValue(vmc->vmgr->views, key);
        SOSViewSetConcrete(view, false);
    }
}

bool SOSViewManagerSetChildren(SOSViewManagerRef vmgr, CFArrayRef children, CFErrorRef *error) {
    bool ok = true;
    CFMutableDictionaryRef referencedViews = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
    CFDictionaryRef childInfo;
    CFArrayForEachC(children, childInfo) {
        ok &= SOSViewManangerAddChildWithInfo(vmgr, referencedViews, childInfo, error);
    }

    // Potentially populate all views here.

    // Cleanup, remove any views we no longer reference, and set any views which need not be concrete as such.
    struct SOSViewManagerContext vmc = {
        .vmgr = vmgr,
        .referencedViews = referencedViews,
    };
    CFDictionaryApplyFunction(vmgr->views, SOSViewManagerUpdateView, &vmc);
    CFRetainSafe(referencedViews);

    return ok;
}
#endif