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