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