You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

420 lines
12 KiB

//
// pEpiOSAdapter.m
// pEpiOSAdapter
//
// Created by Volker Birk on 28.04.15.
// Copyright (c) 2015 p≡p. All rights reserved.
//
@import Foundation;
#import "PEPObjCAdapter.h"
#import "PEPObjCAdapter+Internal.h"
#import "PEPMessage.h"
#include "keymanagement.h"
///////////////////////////////////////////////////////////////////////////////
// Keyserver and Identity lookup - C part
int examine_identity(pEp_identity *ident, void *management)
{
//PEPQueue *q = (__bridge PEPQueue *)management;
PEPQueue *q = [PEPObjCAdapter getLookupQueue];
NSDictionary *identity = PEP_identityDictFromStruct(ident);
[q enqueue:identity];
return 0;
}
static pEp_identity *retrieve_next_identity(void *management)
{
//PEPQueue *q = (__bridge PEPQueue *)management;
PEPQueue *q = [PEPObjCAdapter getLookupQueue];
// Dequeue is a blocking operation
// that returns nil when queue is killed
NSDictionary *ident = [q dequeue];
if (ident)
return PEP_identityDictToStruct(ident);
else
return NULL;
}
///////////////////////////////////////////////////////////////////////////////
// Sync - C part
// Called by sync thread only
PEP_STATUS notify_handshake(void *unused_object, pEp_identity *me, pEp_identity *partner, sync_handshake_signal signal)
{
id <PEPSyncDelegate> syncDelegate = [PEPObjCAdapter getSyncDelegate];
if ( syncDelegate )
return [syncDelegate
notifyHandshakeWithSignal:signal
me:PEP_identityDictFromStruct(me)
partner:PEP_identityDictFromStruct(partner)];
else
return PEP_SYNC_NO_NOTIFY_CALLBACK;
}
// Called by sync thread only
PEP_STATUS message_to_send(void *unused_object, message *msg)
{
id <PEPSyncDelegate> syncDelegate = [PEPObjCAdapter getSyncDelegate];
if ( syncDelegate )
return [syncDelegate sendMessage:PEP_messageDictFromStruct(msg)];
else
return PEP_SEND_FUNCTION_NOT_REGISTERED;
}
// called indirectly by decrypt message - any thread/session
int inject_sync_msg(void *msg, void *unused_management)
{
PEPQueue *q = [PEPObjCAdapter getSyncQueue];
[q enqueue:[NSValue valueWithPointer:msg]];
return 0;
}
// Called by sync thread only
void *retrieve_next_sync_msg(void *unused_mamagement, time_t *timeout)
{
PEPQueue *q = [PEPObjCAdapter getSyncQueue];
return (void*)[[q dequeue] pointerValue];
}
///////////////////////////////////////////////////////////////////////////////
// DB and paths
const char* _Nullable SystemDB = NULL;
NSURL *s_homeURL;
@implementation PEPObjCAdapter
+ (void)initialize
{
s_homeURL = [self createApplicationDirectory];
}
+ (NSURL *)homeURL
{
return s_homeURL;
}
+ (NSURL *)createApplicationDirectory
{
NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
if (!bundleID) {
// This can happen in unit tests
bundleID = @"test";
}
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *dirPath = nil;
// Find the application support directory in the home directory.
NSArray *appSupportDir = [fm URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask];
if ([appSupportDir count] > 0)
{
// Append the bundle ID to the URL for the
// Application Support directory.
// Mainly needed for OS X, but doesn't do any harm on iOS
dirPath = [[appSupportDir objectAtIndex:0] URLByAppendingPathComponent:bundleID];
// If the directory does not exist, this method creates it.
// This method is only available in OS X v10.7 and iOS 5.0 or later.
NSError *theError = nil;
if (![fm createDirectoryAtURL:dirPath withIntermediateDirectories:YES
attributes:nil error:&theError])
{
// Handle the error.
return nil;
}
}
return dirPath;
}
+ (NSURL *)createAndSetHomeDirectory
{
// create and set home directory
setenv("HOME", [[s_homeURL path] cStringUsingEncoding:NSUTF8StringEncoding], 1);
// create and set temp directory
NSURL *tmpDirUrl = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
setenv("TEMP", [[tmpDirUrl path] cStringUsingEncoding:NSUTF8StringEncoding], 1);
return s_homeURL;
}
+ (NSString *) getBundlePathFor: (NSString *) filename
{
return nil;
}
+ (NSString *) copyAssetIntoDocumentsDirectory:(NSBundle *)rootBundle
:(NSString *)bundleName
:(NSString *)fileName{
NSURL *homeUrl = [PEPObjCAdapter createAndSetHomeDirectory];
NSString *documentsDirectory = [homeUrl path];
if(!(documentsDirectory && bundleName && fileName))
return nil;
// Check if the database file exists in the documents directory.
NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:fileName];
if (![[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
// The file does not exist in the documents directory, so copy it from bundle now.
NSBundle *bundleObj = [NSBundle bundleWithPath:
[[rootBundle resourcePath]
stringByAppendingPathComponent: bundleName]];
if (!bundleObj)
return nil;
NSString *sourcePath =[[bundleObj resourcePath] stringByAppendingPathComponent: fileName];
NSError *error;
[[NSFileManager defaultManager]
copyItemAtPath:sourcePath toPath:destinationPath error:&error];
// Check if any error occurred during copying and display it.
if (error != nil) {
NSLog(@"%@", [error localizedDescription]);
return nil;
}
}
NSLog(@"Asset %@ copied into %@", fileName, destinationPath);
return destinationPath;
}
+ (void)setupTrustWordsDB:(NSBundle *)rootBundle{
NSString *systemDBPath = [PEPObjCAdapter copyAssetIntoDocumentsDirectory:rootBundle
:@"pEpTrustWords.bundle"
:@"system.db"];
if (SystemDB) {
free((void *) SystemDB);
}
SystemDB = strdup(systemDBPath.UTF8String);
}
+ (void)setupTrustWordsDB
{
[PEPObjCAdapter setupTrustWordsDB:[NSBundle mainBundle]];
}
static NSMutableArray* boundSessions = nil;
+ (NSMutableArray*)boundSessions
{
static dispatch_once_t once;
dispatch_once(&once, ^{
boundSessions = [[NSMutableArray alloc] init];
});
return boundSessions;
}
///////////////////////////////////////////////////////////////////////////////
// Keyserver and Identity lookup - ObjC part
static PEPQueue *lookupQueue = nil;
static NSThread *lookupThread = nil;
static NSConditionLock *lookupThreadJoinCond = nil;
+ (void)lookupThreadRoutine:(id)object
{
[lookupThreadJoinCond lock];
// FIXME: do_KeyManagement asserts if management is null.
do_keymanagement(retrieve_next_identity, "NOTNULL" /* (__bridge void *)queue */);
// Set and signal join()
[lookupThreadJoinCond unlockWithCondition:YES];
}
+ (void)startKeyserverLookup
{
if (!lookupQueue)
{
lookupQueue = [[PEPQueue alloc]init];
// There is no join() call on NSThreads.
lookupThreadJoinCond = [[NSConditionLock alloc] initWithCondition:NO];
lookupThread = [[NSThread alloc] initWithTarget:self selector:@selector(lookupThreadRoutine:) object:nil];
[lookupThread start];
}
}
+ (void)stopKeyserverLookup
{
if (lookupQueue)
{
// Flush queue and kick the consumer
[lookupQueue kill];
// Thread then bailout. Wait for that.
[lookupThreadJoinCond lockWhenCondition:YES];
[lookupThreadJoinCond unlock];
lookupThread = nil;
lookupQueue = nil;
lookupThreadJoinCond = nil;
}
}
+ (void)registerExamineFunction:(PEP_SESSION)session
{
register_examine_function(session, examine_identity, NULL /* (__bridge void *)queue */);
}
+ (PEPQueue*)getLookupQueue
{
return lookupQueue;
}
///////////////////////////////////////////////////////////////////////////////
// Sync - ObjC part
static PEPQueue *syncQueue = nil;
static NSThread *syncThread = nil;
static NSConditionLock *syncThreadJoinCond = nil;
static PEP_SESSION sync_session = NULL;
static id <PEPSyncDelegate> syncDelegate = nil;
+ (void)syncThreadRoutine:(id)object
{
[syncThreadJoinCond lock];
PEP_STATUS status = init(&sync_session);
if (status != PEP_STATUS_OK) {
return;
}
register_sync_callbacks(sync_session,
/* "management" : queuing (unused) */
"NOTNULL",
message_to_send,
notify_handshake,
inject_sync_msg,
retrieve_next_sync_msg);
status = do_sync_protocol(sync_session,
/* "object" : notifying, sending (unused) */
"NOTNULL");
// TODO : detach all attached sessions
release(sync_session);
[syncThreadJoinCond unlockWithCondition:YES];
}
+ (void)attachSyncSession:(PEP_SESSION)session
{
if(sync_session)
attach_sync_session(session, sync_session);
}
+ (void)detachSyncSession:(PEP_SESSION)session
{
detach_sync_session(session);
}
+ (void)startSync:(id <PEPSyncDelegate>)delegate;
{
syncDelegate = delegate;
if (!syncQueue)
{
syncQueue = [[PEPQueue alloc]init];
syncThreadJoinCond = [[NSConditionLock alloc] initWithCondition:NO];
syncThread = [[NSThread alloc]
initWithTarget:self
selector:@selector(syncThreadRoutine:)
object:nil];
[syncThread start];
}
NSMutableArray* sessionList = [PEPObjCAdapter boundSessions];
PEPSession* session;
@synchronized (sessionList) {
for (session in sessionList) {
[PEPObjCAdapter attachSyncSession:[session session]];
}
}
}
+ (void)stopSync
{
NSMutableArray* sessionList = [PEPObjCAdapter boundSessions];
PEPSession* session;
@synchronized (sessionList) {
for (session in sessionList) {
[PEPObjCAdapter detachSyncSession:[session session]];
}
}
syncDelegate = nil;
if (syncQueue)
{
// FIXME : memory leak ! unallocate sync message waiting in the queue
[syncQueue kill];
[syncThreadJoinCond lockWhenCondition:YES];
[syncThreadJoinCond unlock];
syncThread = nil;
syncQueue = nil;
syncThreadJoinCond = nil;
}
}
+ (PEPQueue*)getSyncQueue
{
return syncQueue;
}
+ (id <PEPSyncDelegate>)getSyncDelegate
{
return syncDelegate;
}
+ (void)bindSession:(PEPSession*)session
{
NSMutableArray* sessionList = [PEPObjCAdapter boundSessions];
@synchronized (sessionList) {
[sessionList addObject:session];
}
[PEPObjCAdapter registerExamineFunction:[session session]];
[PEPObjCAdapter attachSyncSession:[session session]];
}
+ (void)unbindSession:(PEPSession*)session
{
[PEPObjCAdapter detachSyncSession:[session session]];
NSMutableArray* sessionList = [PEPObjCAdapter boundSessions];
@synchronized (sessionList) {
[sessionList removeObject:session];
}
}
@end