pEpObjCAdapter/PEPObjCAdapter.m
author Edouard Tisserant <edouard@pep-project.org>
Wed, 17 May 2017 17:57:53 +0200
changeset 191 c0872370f8fa
parent 187 1f630a05e444
child 192 9e94360802b5
permissions -rw-r--r--
Use weak refs to keep track of sessions. Otherwise it was impossible to deallocate them.
dirk@187
     1
//
edouard@191
     2
//  pEpObjCAdapter.m
edouard@191
     3
//  pEpObjCAdapter
dirk@187
     4
//
dirk@187
     5
//  Created by Volker Birk on 28.04.15.
dirk@187
     6
//  Copyright (c) 2015 p≡p. All rights reserved.
dirk@187
     7
//
dirk@187
     8
dirk@187
     9
@import Foundation;
dirk@187
    10
dirk@187
    11
#import "PEPObjCAdapter.h"
dirk@187
    12
#import "PEPObjCAdapter+Internal.h"
dirk@187
    13
#import "PEPMessage.h"
dirk@187
    14
#include "keymanagement.h"
dirk@187
    15
dirk@187
    16
///////////////////////////////////////////////////////////////////////////////
dirk@187
    17
//  Keyserver and Identity lookup - C part
dirk@187
    18
dirk@187
    19
dirk@187
    20
int examine_identity(pEp_identity *ident, void *management)
dirk@187
    21
{
dirk@187
    22
    //PEPQueue *q = (__bridge PEPQueue *)management;
dirk@187
    23
    PEPQueue *q = [PEPObjCAdapter getLookupQueue];
dirk@187
    24
    
dirk@187
    25
    NSDictionary *identity = PEP_identityDictFromStruct(ident);
dirk@187
    26
    
dirk@187
    27
    [q enqueue:identity];
dirk@187
    28
    return 0;
dirk@187
    29
}
dirk@187
    30
dirk@187
    31
static pEp_identity *retrieve_next_identity(void *management)
dirk@187
    32
{
dirk@187
    33
    //PEPQueue *q = (__bridge PEPQueue *)management;
dirk@187
    34
    PEPQueue *q = [PEPObjCAdapter getLookupQueue];
dirk@187
    35
    
dirk@187
    36
    // Dequeue is a blocking operation
dirk@187
    37
    // that returns nil when queue is killed
dirk@187
    38
    NSDictionary *ident = [q dequeue];
dirk@187
    39
    
dirk@187
    40
    if (ident)
dirk@187
    41
        return PEP_identityDictToStruct(ident);
dirk@187
    42
    else
dirk@187
    43
        return NULL;
dirk@187
    44
}
dirk@187
    45
dirk@187
    46
///////////////////////////////////////////////////////////////////////////////
dirk@187
    47
// Sync - C part
dirk@187
    48
dirk@187
    49
// Called by sync thread only
dirk@187
    50
PEP_STATUS notify_handshake(void *unused_object, pEp_identity *me, pEp_identity *partner, sync_handshake_signal signal)
dirk@187
    51
{
dirk@187
    52
    id <PEPSyncDelegate> syncDelegate = [PEPObjCAdapter getSyncDelegate];
dirk@187
    53
    if ( syncDelegate )
dirk@187
    54
        return [syncDelegate
dirk@187
    55
                notifyHandshakeWithSignal:signal
dirk@187
    56
                me:PEP_identityDictFromStruct(me)
dirk@187
    57
                partner:PEP_identityDictFromStruct(partner)];
dirk@187
    58
    else
dirk@187
    59
        return PEP_SYNC_NO_NOTIFY_CALLBACK;
dirk@187
    60
}
dirk@187
    61
dirk@187
    62
// Called by sync thread only
dirk@187
    63
PEP_STATUS message_to_send(void *unused_object, message *msg)
dirk@187
    64
{
dirk@187
    65
    id <PEPSyncDelegate> syncDelegate = [PEPObjCAdapter getSyncDelegate];
dirk@187
    66
    if ( syncDelegate )
dirk@187
    67
        return [syncDelegate sendMessage:PEP_messageDictFromStruct(msg)];
dirk@187
    68
    else
dirk@187
    69
        return PEP_SEND_FUNCTION_NOT_REGISTERED;
dirk@187
    70
}
dirk@187
    71
dirk@187
    72
// called indirectly by decrypt message - any thread/session
dirk@187
    73
int inject_sync_msg(void *msg, void *unused_management)
dirk@187
    74
{
dirk@187
    75
    PEPQueue *q = [PEPObjCAdapter getSyncQueue];
dirk@187
    76
    
dirk@187
    77
    [q enqueue:[NSValue valueWithPointer:msg]];
dirk@187
    78
    
dirk@187
    79
    return 0;
dirk@187
    80
}
dirk@187
    81
dirk@187
    82
// Called by sync thread only
dirk@187
    83
void *retrieve_next_sync_msg(void *unused_mamagement, time_t *timeout)
dirk@187
    84
{
dirk@187
    85
    PEPQueue *q = [PEPObjCAdapter getSyncQueue];
dirk@187
    86
    
dirk@187
    87
    return (void*)[[q dequeue] pointerValue];
dirk@187
    88
}
dirk@187
    89
dirk@187
    90
dirk@187
    91
///////////////////////////////////////////////////////////////////////////////
dirk@187
    92
// DB and paths
dirk@187
    93
dirk@187
    94
const char* _Nullable SystemDB = NULL;
dirk@187
    95
NSURL *s_homeURL;
dirk@187
    96
dirk@187
    97
@implementation PEPObjCAdapter
dirk@187
    98
dirk@187
    99
+ (void)initialize
dirk@187
   100
{
dirk@187
   101
    s_homeURL = [self createApplicationDirectory];
dirk@187
   102
}
dirk@187
   103
dirk@187
   104
+ (NSURL *)homeURL
dirk@187
   105
{
dirk@187
   106
    return s_homeURL;
dirk@187
   107
}
dirk@187
   108
dirk@187
   109
+ (NSURL *)createApplicationDirectory
dirk@187
   110
{
dirk@187
   111
    NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
dirk@187
   112
    if (!bundleID) {
dirk@187
   113
        // This can happen in unit tests
dirk@187
   114
        bundleID = @"test";
dirk@187
   115
    }
dirk@187
   116
    NSFileManager *fm = [NSFileManager defaultManager];
dirk@187
   117
    NSURL *dirPath = nil;
dirk@187
   118
dirk@187
   119
    // Find the application support directory in the home directory.
dirk@187
   120
    NSArray *appSupportDir = [fm URLsForDirectory:NSApplicationSupportDirectory
dirk@187
   121
                                        inDomains:NSUserDomainMask];
dirk@187
   122
    if ([appSupportDir count] > 0)
dirk@187
   123
    {
dirk@187
   124
        // Append the bundle ID to the URL for the
dirk@187
   125
        // Application Support directory.
dirk@187
   126
        // Mainly needed for OS X, but doesn't do any harm on iOS
dirk@187
   127
        dirPath = [[appSupportDir objectAtIndex:0] URLByAppendingPathComponent:bundleID];
dirk@187
   128
dirk@187
   129
        // If the directory does not exist, this method creates it.
dirk@187
   130
        // This method is only available in OS X v10.7 and iOS 5.0 or later.
dirk@187
   131
        NSError *theError = nil;
dirk@187
   132
        if (![fm createDirectoryAtURL:dirPath withIntermediateDirectories:YES
dirk@187
   133
                           attributes:nil error:&theError])
dirk@187
   134
        {
dirk@187
   135
            // Handle the error.
dirk@187
   136
            return nil;
dirk@187
   137
        }
dirk@187
   138
    }
dirk@187
   139
dirk@187
   140
    return dirPath;
dirk@187
   141
}
dirk@187
   142
dirk@187
   143
+ (NSURL *)createAndSetHomeDirectory
dirk@187
   144
{
dirk@187
   145
    // create and set home directory
dirk@187
   146
    setenv("HOME", [[s_homeURL path] cStringUsingEncoding:NSUTF8StringEncoding], 1);
dirk@187
   147
dirk@187
   148
    // create and set temp directory
dirk@187
   149
    NSURL *tmpDirUrl = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
dirk@187
   150
    setenv("TEMP", [[tmpDirUrl path] cStringUsingEncoding:NSUTF8StringEncoding], 1);
dirk@187
   151
dirk@187
   152
    return s_homeURL;
dirk@187
   153
}
dirk@187
   154
dirk@187
   155
+ (NSString *) getBundlePathFor: (NSString *) filename
dirk@187
   156
{
dirk@187
   157
    return nil;
dirk@187
   158
}
dirk@187
   159
dirk@187
   160
+ (NSString *) copyAssetIntoDocumentsDirectory:(NSBundle *)rootBundle
dirk@187
   161
                                                          :(NSString *)bundleName
dirk@187
   162
                                                          :(NSString *)fileName{
dirk@187
   163
dirk@187
   164
    NSURL *homeUrl = [PEPObjCAdapter createAndSetHomeDirectory];
dirk@187
   165
    NSString *documentsDirectory = [homeUrl path];
dirk@187
   166
    
dirk@187
   167
    if(!(documentsDirectory && bundleName && fileName))
dirk@187
   168
        return nil;
dirk@187
   169
    
dirk@187
   170
    // Check if the database file exists in the documents directory.
dirk@187
   171
    NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:fileName];
dirk@187
   172
    if (![[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
dirk@187
   173
        // The file does not exist in the documents directory, so copy it from bundle now.
dirk@187
   174
        NSBundle *bundleObj = [NSBundle bundleWithPath:
dirk@187
   175
                               [[rootBundle resourcePath]
dirk@187
   176
                                stringByAppendingPathComponent: bundleName]];
dirk@187
   177
        
dirk@187
   178
        if (!bundleObj)
dirk@187
   179
            return nil;
dirk@187
   180
        
dirk@187
   181
        NSString *sourcePath =[[bundleObj resourcePath] stringByAppendingPathComponent: fileName];
dirk@187
   182
        
dirk@187
   183
        NSError *error;
dirk@187
   184
        [[NSFileManager defaultManager]
dirk@187
   185
         copyItemAtPath:sourcePath toPath:destinationPath error:&error];
dirk@187
   186
        
dirk@187
   187
        // Check if any error occurred during copying and display it.
dirk@187
   188
        if (error != nil) {
dirk@187
   189
            NSLog(@"%@", [error localizedDescription]);
dirk@187
   190
            return nil;
dirk@187
   191
        }
dirk@187
   192
    }
dirk@187
   193
    NSLog(@"Asset %@ copied into %@", fileName, destinationPath);
dirk@187
   194
    return destinationPath;
dirk@187
   195
}
dirk@187
   196
dirk@187
   197
+ (void)setupTrustWordsDB:(NSBundle *)rootBundle{
dirk@187
   198
    NSString *systemDBPath = [PEPObjCAdapter copyAssetIntoDocumentsDirectory:rootBundle
dirk@187
   199
                                                                           :@"pEpTrustWords.bundle"
dirk@187
   200
                                                                           :@"system.db"];
dirk@187
   201
    if (SystemDB) {
dirk@187
   202
        free((void *) SystemDB);
dirk@187
   203
    }
dirk@187
   204
    SystemDB = strdup(systemDBPath.UTF8String);
dirk@187
   205
}
dirk@187
   206
dirk@187
   207
+ (void)setupTrustWordsDB
dirk@187
   208
{
dirk@187
   209
    [PEPObjCAdapter setupTrustWordsDB:[NSBundle mainBundle]];
dirk@187
   210
}
dirk@187
   211
dirk@187
   212
static NSMutableArray* boundSessions = nil;
dirk@187
   213
dirk@187
   214
+ (NSMutableArray*)boundSessions
dirk@187
   215
{
dirk@187
   216
    static dispatch_once_t once;
dirk@187
   217
    dispatch_once(&once, ^{
dirk@187
   218
        boundSessions =  [[NSMutableArray alloc] init];
dirk@187
   219
    });
dirk@187
   220
    return boundSessions;
dirk@187
   221
}
dirk@187
   222
dirk@187
   223
///////////////////////////////////////////////////////////////////////////////
dirk@187
   224
//  Keyserver and Identity lookup - ObjC part
dirk@187
   225
dirk@187
   226
static PEPQueue *lookupQueue = nil;
dirk@187
   227
static NSThread *lookupThread = nil;
dirk@187
   228
static NSConditionLock *lookupThreadJoinCond = nil;
dirk@187
   229
dirk@187
   230
+ (void)lookupThreadRoutine:(id)object
dirk@187
   231
{
dirk@187
   232
    [lookupThreadJoinCond lock];
dirk@187
   233
dirk@187
   234
    // FIXME: do_KeyManagement asserts if management is null.
dirk@187
   235
    do_keymanagement(retrieve_next_identity, "NOTNULL" /* (__bridge void *)queue */);
dirk@187
   236
    
dirk@187
   237
    // Set and signal join()
dirk@187
   238
    [lookupThreadJoinCond unlockWithCondition:YES];
dirk@187
   239
}
dirk@187
   240
dirk@187
   241
+ (void)startKeyserverLookup
dirk@187
   242
{
dirk@187
   243
    if (!lookupQueue)
dirk@187
   244
    {
dirk@187
   245
        lookupQueue = [[PEPQueue alloc]init];
dirk@187
   246
        
dirk@187
   247
        // There is no join() call on NSThreads.
dirk@187
   248
        lookupThreadJoinCond = [[NSConditionLock alloc] initWithCondition:NO];
dirk@187
   249
        
dirk@187
   250
        lookupThread = [[NSThread alloc] initWithTarget:self selector:@selector(lookupThreadRoutine:) object:nil];
dirk@187
   251
        [lookupThread start];
dirk@187
   252
    }
dirk@187
   253
}
dirk@187
   254
dirk@187
   255
+ (void)stopKeyserverLookup
dirk@187
   256
{
dirk@187
   257
    
dirk@187
   258
    if (lookupQueue)
dirk@187
   259
    {
dirk@187
   260
        // Flush queue and kick the consumer
dirk@187
   261
        [lookupQueue kill];
dirk@187
   262
        
dirk@187
   263
        // Thread then bailout. Wait for that.
dirk@187
   264
        [lookupThreadJoinCond lockWhenCondition:YES];
dirk@187
   265
        [lookupThreadJoinCond unlock];
dirk@187
   266
        
dirk@187
   267
        lookupThread = nil;
dirk@187
   268
        lookupQueue = nil;
dirk@187
   269
        lookupThreadJoinCond = nil;
dirk@187
   270
    }
dirk@187
   271
}
dirk@187
   272
dirk@187
   273
+ (void)registerExamineFunction:(PEP_SESSION)session
dirk@187
   274
{
dirk@187
   275
    register_examine_function(session, examine_identity, NULL /* (__bridge void *)queue */);
dirk@187
   276
}
dirk@187
   277
dirk@187
   278
+ (PEPQueue*)getLookupQueue
dirk@187
   279
{
dirk@187
   280
    return lookupQueue;
dirk@187
   281
}
dirk@187
   282
dirk@187
   283
///////////////////////////////////////////////////////////////////////////////
dirk@187
   284
// Sync - ObjC part
dirk@187
   285
dirk@187
   286
static PEPQueue *syncQueue = nil;
dirk@187
   287
static NSThread *syncThread = nil;
dirk@187
   288
static NSConditionLock *syncThreadJoinCond = nil;
dirk@187
   289
static PEP_SESSION sync_session = NULL;
dirk@187
   290
static id <PEPSyncDelegate> syncDelegate = nil;
dirk@187
   291
dirk@187
   292
dirk@187
   293
+ (void)syncThreadRoutine:(id)object
dirk@187
   294
{
dirk@187
   295
    [syncThreadJoinCond lock];
dirk@187
   296
    
dirk@187
   297
    PEP_STATUS status = init(&sync_session);
dirk@187
   298
    if (status != PEP_STATUS_OK) {
dirk@187
   299
        return;
dirk@187
   300
    }
dirk@187
   301
    
dirk@187
   302
    register_sync_callbacks(sync_session,
dirk@187
   303
                             /* "management" : queuing (unused) */
dirk@187
   304
                            "NOTNULL",
dirk@187
   305
                            message_to_send,
dirk@187
   306
                            notify_handshake,
dirk@187
   307
                            inject_sync_msg,
dirk@187
   308
                            retrieve_next_sync_msg);
dirk@187
   309
dirk@187
   310
    status = do_sync_protocol(sync_session,
dirk@187
   311
                              /* "object" : notifying, sending (unused) */
dirk@187
   312
                              "NOTNULL");
dirk@187
   313
    
dirk@187
   314
    // TODO : detach all attached sessions
dirk@187
   315
    
dirk@187
   316
    release(sync_session);
dirk@187
   317
    
dirk@187
   318
    [syncThreadJoinCond unlockWithCondition:YES];
dirk@187
   319
}
dirk@187
   320
dirk@187
   321
+ (void)attachSyncSession:(PEP_SESSION)session
dirk@187
   322
{
dirk@187
   323
    if(sync_session)
dirk@187
   324
        attach_sync_session(session, sync_session);
dirk@187
   325
}
dirk@187
   326
dirk@187
   327
+ (void)detachSyncSession:(PEP_SESSION)session
dirk@187
   328
{
dirk@187
   329
    detach_sync_session(session);
dirk@187
   330
}
dirk@187
   331
dirk@187
   332
+ (void)startSync:(id <PEPSyncDelegate>)delegate;
dirk@187
   333
{
dirk@187
   334
    syncDelegate = delegate;
dirk@187
   335
    
dirk@187
   336
    if (!syncQueue)
dirk@187
   337
    {
dirk@187
   338
        syncQueue = [[PEPQueue alloc]init];
dirk@187
   339
        
dirk@187
   340
        syncThreadJoinCond = [[NSConditionLock alloc] initWithCondition:NO];
dirk@187
   341
        
dirk@187
   342
        syncThread = [[NSThread alloc]
dirk@187
   343
                      initWithTarget:self
dirk@187
   344
                      selector:@selector(syncThreadRoutine:)
dirk@187
   345
                      object:nil];
dirk@187
   346
dirk@187
   347
        [syncThread start];
dirk@187
   348
    }
dirk@187
   349
dirk@187
   350
    NSMutableArray* sessionList = [PEPObjCAdapter boundSessions];
edouard@191
   351
    NSValue* v;
dirk@187
   352
    PEPSession* session;
dirk@187
   353
    @synchronized (sessionList) {
edouard@191
   354
        for (v in sessionList) {
edouard@191
   355
            session = [v nonretainedObjectValue];
dirk@187
   356
            [PEPObjCAdapter attachSyncSession:[session session]];
dirk@187
   357
        }
dirk@187
   358
    }
dirk@187
   359
}
dirk@187
   360
dirk@187
   361
+ (void)stopSync
dirk@187
   362
{
dirk@187
   363
    NSMutableArray* sessionList = [PEPObjCAdapter boundSessions];
edouard@191
   364
    NSValue* v;
dirk@187
   365
    PEPSession* session;
dirk@187
   366
    @synchronized (sessionList) {
edouard@191
   367
        for (v in sessionList) {
edouard@191
   368
            session = [v nonretainedObjectValue];
dirk@187
   369
            [PEPObjCAdapter detachSyncSession:[session session]];
dirk@187
   370
        }
dirk@187
   371
    }
dirk@187
   372
    
dirk@187
   373
    syncDelegate = nil;
dirk@187
   374
    
dirk@187
   375
    if (syncQueue)
dirk@187
   376
    {
dirk@187
   377
        // FIXME : memory leak ! unallocate sync message waiting in the queue
dirk@187
   378
        
dirk@187
   379
        [syncQueue kill];
dirk@187
   380
        
dirk@187
   381
        [syncThreadJoinCond lockWhenCondition:YES];
dirk@187
   382
        [syncThreadJoinCond unlock];
dirk@187
   383
        
dirk@187
   384
        syncThread = nil;
dirk@187
   385
        syncQueue = nil;
dirk@187
   386
        syncThreadJoinCond = nil;
dirk@187
   387
    }
dirk@187
   388
}
dirk@187
   389
dirk@187
   390
+ (PEPQueue*)getSyncQueue
dirk@187
   391
{
dirk@187
   392
    return syncQueue;
dirk@187
   393
}
dirk@187
   394
dirk@187
   395
+ (id <PEPSyncDelegate>)getSyncDelegate
dirk@187
   396
{
dirk@187
   397
    return syncDelegate;
dirk@187
   398
}
dirk@187
   399
dirk@187
   400
+ (void)bindSession:(PEPSession*)session
dirk@187
   401
{
dirk@187
   402
    NSMutableArray* sessionList = [PEPObjCAdapter boundSessions];
dirk@187
   403
    @synchronized (sessionList) {
edouard@191
   404
        [sessionList addObject:[NSValue valueWithNonretainedObject:session]];
dirk@187
   405
    }
dirk@187
   406
dirk@187
   407
    [PEPObjCAdapter registerExamineFunction:[session session]];
dirk@187
   408
    [PEPObjCAdapter attachSyncSession:[session session]];
dirk@187
   409
}
dirk@187
   410
dirk@187
   411
+ (void)unbindSession:(PEPSession*)session
dirk@187
   412
{
dirk@187
   413
    [PEPObjCAdapter detachSyncSession:[session session]];
dirk@187
   414
    
dirk@187
   415
    NSMutableArray* sessionList = [PEPObjCAdapter boundSessions];
dirk@187
   416
    @synchronized (sessionList) {
edouard@191
   417
        [sessionList removeObject:[NSValue valueWithNonretainedObject:session]];
dirk@187
   418
    }
dirk@187
   419
dirk@187
   420
}
dirk@187
   421
dirk@187
   422
dirk@187
   423
dirk@187
   424
@end