pEpObjCAdapter/PEPSession.m
author Edouard Tisserant <edouard@pep-project.org>
Fri, 22 Sep 2017 17:22:09 +0200
changeset 240 90664b3a2988
parent 233 cb333feb4f0a
child 245 d37e60cad266
permissions -rw-r--r--
ISOAD-34 protect session's init() with a critical section
     1 //
     2 //  PEPSession.m
     3 //  pEpiOSAdapter
     4 //
     5 //  Created by Volker Birk on 08.07.15.
     6 //  Copyright (c) 2015 p≡p. All rights reserved.
     7 //
     8 
     9 #import "PEPSession.h"
    10 #import "PEPSession+Internal.h"
    11 #import "PEPObjCAdapter.h"
    12 #import "PEPObjCAdapter+Internal.h"
    13 #import "PEPMessage.h"
    14 #import "PEPLanguage.h"
    15 #import "PEPCSVScanner.h"
    16 #import "NSArray+Extension.h"
    17 #import "NSDictionary+Extension.h"
    18 
    19 @implementation PEPSession
    20 
    21 // serialize all session access
    22 + (dispatch_queue_t)sharedSessionQueue
    23 {
    24     static dispatch_once_t once;
    25     static dispatch_queue_t sharedSessionQueue;
    26     dispatch_once(&once, ^{
    27         sharedSessionQueue = dispatch_queue_create("pEp session queue", DISPATCH_QUEUE_CONCURRENT);
    28     });
    29     return sharedSessionQueue;
    30 }
    31 
    32 + (PEPSession *)session
    33 {
    34     PEPSession *_session = [[PEPSession alloc] init];
    35     return _session;
    36 }
    37 
    38 + (void)dispatchAsyncOnSession:(PEPSessionBlock)block
    39 {
    40     dispatch_async([self sharedSessionQueue], ^{
    41         PEPSession *pepSession = [[PEPSession alloc] init];
    42         block(pepSession);
    43     });
    44 }
    45 
    46 + (void)dispatchSyncOnSession:(PEPSessionBlock)block
    47 {
    48     PEPSession *pepSession = [[PEPSession alloc] init];
    49     block(pepSession);
    50 }
    51 
    52 + (void)setupTrustWordsDB
    53 {
    54     static dispatch_once_t once;
    55     dispatch_once(&once, ^{
    56         [PEPObjCAdapter setupTrustWordsDB:[NSBundle bundleForClass:[self class]]];
    57     });
    58 }
    59 
    60 static NSString *threadCountKey = @"PEPSession.threadCount";
    61 static NSObject *sessionInitLock = nil;
    62 static dispatch_once_t sessionInitLockOnce;
    63 
    64 - (id)init
    65 {
    66     [PEPSession setupTrustWordsDB];
    67     
    68     dispatch_once(&sessionInitLockOnce, ^{
    69         sessionInitLock = [[NSObject alloc] init];
    70     });
    71     @synchronized (sessionInitLock) {
    72 
    73         PEP_STATUS status = init(&_session);
    74 
    75         if (status != PEP_STATUS_OK) {
    76             return nil;
    77         }
    78 
    79         [PEPObjCAdapter bindSession:self];
    80 
    81         [self incSessionCount];
    82     }
    83     return self;
    84 }
    85 
    86 - (void)dealloc
    87 {
    88     [PEPObjCAdapter unbindSession:self];
    89 
    90     @synchronized (sessionInitLock) {
    91         release(_session);
    92 
    93         [self decSessionCount];
    94     }
    95 }
    96 
    97 - (void)incSessionCount
    98 {
    99     NSNumber *sessionCount = [[NSThread currentThread] threadDictionary][threadCountKey];
   100     if (!sessionCount) {
   101         sessionCount = [NSNumber numberWithInteger:1];
   102     } else {
   103         sessionCount = [NSNumber numberWithInteger:sessionCount.integerValue + 1];
   104     }
   105     [[NSThread currentThread] threadDictionary][threadCountKey] = sessionCount;
   106 }
   107 
   108 - (void)decSessionCount
   109 {
   110     NSNumber *sessionCount = [[NSThread currentThread] threadDictionary][threadCountKey];
   111     if (sessionCount) {
   112         sessionCount = [NSNumber numberWithInteger:sessionCount.integerValue - 1];
   113         [[NSThread currentThread] threadDictionary][threadCountKey] = sessionCount;
   114     } else {
   115         NSAssert(NO, @"Session count on thread below 0, thread mixup between init and dealloc?");
   116     }
   117 }
   118 
   119 - (void)dumpThreadCount:(NSString *)contextName
   120 {
   121     NSNumber *sessionCount = [[NSThread currentThread] threadDictionary][threadCountKey];
   122     NSLog(@"%@: (%@) %ld", contextName,
   123           [[NSThread currentThread] name], (long) sessionCount.integerValue);
   124 }
   125 
   126 /**
   127  Saves the given message dict as a plist to the local filesystem
   128  (directly under NSApplicationSupportDirectory).
   129  Since the complete output file will be logged by `debugSaveToFilePath`,
   130  you can get access to the files easily when it's the simulator.
   131  */
   132 - (void)debugOutPutMessageDict:(nonnull PEPDict *)src
   133 {
   134     NSString *from = src[kPepFrom][kPepAddress];
   135     NSArray *tos = src[kPepTo];
   136     NSString *to = tos[0][kPepAddress];
   137     NSString *msgID = src[kPepID];
   138     NSString *fileName = [NSString stringWithFormat:@"%@_from(%@)_%@",
   139                           to, from, msgID];
   140     [src debugSaveToFilePath:fileName];
   141 }
   142 
   143 - (PEP_rating)decryptMessageDict:(nonnull PEPDict *)src
   144                             dest:(PEPDict * _Nullable * _Nullable)dst
   145                             keys:(PEPStringList * _Nullable * _Nullable)keys
   146 {
   147     message * _src = PEP_messageDictToStruct(src);
   148     message * _dst = NULL;
   149     stringlist_t * _keys = NULL;
   150     PEP_rating color = PEP_rating_undefined;
   151     PEP_decrypt_flags_t flags = 0;
   152 
   153     @synchronized (self) {
   154         decrypt_message(_session, _src, &_dst, &_keys, &color, &flags);
   155     }
   156 
   157     NSDictionary * dst_;
   158 
   159     if (_dst) {
   160         dst_ = PEP_messageDictFromStruct(_dst);
   161     }
   162     else {
   163         dst_ = PEP_messageDictFromStruct(_src);
   164     }
   165 
   166     NSArray * keys_ = nil;
   167     if (_keys)
   168         keys_ = PEP_arrayFromStringlist(_keys);
   169 
   170     free_message(_src);
   171     free_message(_dst);
   172     free_stringlist(_keys);
   173 
   174     if (dst) {
   175         *dst = dst_;
   176     }
   177     if (keys) {
   178         *keys = keys_;
   179     }
   180     return color;
   181 }
   182 
   183 - (PEP_rating)reEvaluateMessageRating:(nonnull PEPDict *)src
   184 {
   185     message * _src = PEP_messageDictToStruct(src);
   186     PEP_rating color = PEP_rating_undefined;
   187 
   188     @synchronized (self) {
   189         re_evaluate_message_rating(_session, _src, NULL, PEP_rating_undefined, &color);
   190     }
   191 
   192     free_message(_src);
   193 
   194     return color;
   195 }
   196 
   197 - (void)removeEmptyArrayKey:(NSString *)key inDict:(PEPMutableDict *)dict
   198 {
   199     if ([[dict objectForKey:key] count] == 0) {
   200         [dict removeObjectForKey:key];
   201     }
   202 }
   203 
   204 - (NSDictionary *)removeEmptyRecipients:(PEPDict *)src
   205 {
   206     NSMutableDictionary *dest = src.mutableCopy;
   207 
   208     [self removeEmptyArrayKey:kPepTo inDict:dest];
   209     [self removeEmptyArrayKey:kPepCC inDict:dest];
   210     [self removeEmptyArrayKey:kPepBCC inDict:dest];
   211 
   212     return [NSDictionary dictionaryWithDictionary:dest];
   213 }
   214 
   215 - (PEP_STATUS)encryptMessageDict:(nonnull PEPDict *)src
   216                            extra:(nullable NSArray *)keys
   217                             dest:(PEPDict * _Nullable * _Nullable)dst
   218 {
   219     PEP_STATUS status;
   220     PEP_encrypt_flags_t flags = 0;
   221 
   222     message * _src = PEP_messageDictToStruct([self removeEmptyRecipients:src]);
   223     message * _dst = NULL;
   224     stringlist_t * _keys = PEP_arrayToStringlist(keys);
   225 
   226     @synchronized (self) {
   227         status = encrypt_message(_session, _src, _keys, &_dst, PEP_enc_PGP_MIME, flags);
   228     }
   229 
   230     NSDictionary * dst_;
   231 
   232     if (_dst) {
   233         dst_ = PEP_messageDictFromStruct(_dst);
   234     }
   235     else {
   236         dst_ = PEP_messageDictFromStruct(_src);
   237     }
   238     if (dst) {
   239         *dst = dst_;
   240     }
   241 
   242     free_message(_src);
   243     free_message(_dst);
   244     free_stringlist(_keys);
   245 
   246     return status;
   247 }
   248 
   249 - (PEP_STATUS)encryptMessageDict:(nonnull PEPDict *)src
   250                         identity:(nonnull PEPDict *)identity
   251                             dest:(PEPDict * _Nullable * _Nullable)dst
   252 {
   253     PEP_STATUS status;
   254     PEP_encrypt_flags_t flags = 0;
   255 
   256     message * _src = PEP_messageDictToStruct([self removeEmptyRecipients:src]);
   257     pEp_identity *ident = PEP_identityDictToStruct(identity);
   258     message * _dst = NULL;
   259 
   260     @synchronized (self) {
   261         status = encrypt_message_for_self(_session, ident, _src, &_dst, PEP_enc_PGP_MIME, flags);
   262     }
   263 
   264     NSDictionary * dst_;
   265 
   266     if (_dst) {
   267         dst_ = PEP_messageDictFromStruct(_dst);
   268     }
   269     else {
   270         dst_ = PEP_messageDictFromStruct(_src);
   271     }
   272 
   273     if (dst) {
   274         *dst = dst_;
   275     }
   276 
   277     free_message(_src);
   278     free_message(_dst);
   279     free_identity(ident);
   280 
   281     return status;
   282 }
   283 
   284 - (PEP_rating)outgoingMessageColor:(PEPDict *)msg
   285 {
   286     message * _msg = PEP_messageDictToStruct(msg);
   287     PEP_rating color = PEP_rating_undefined;
   288 
   289     @synchronized (self) {
   290         outgoing_message_rating(_session, _msg, &color);
   291     }
   292 
   293     free_message(_msg);
   294 
   295     return color;
   296 }
   297 
   298 - (PEP_rating)identityRating:(nonnull PEPDict *)identity
   299 {
   300     pEp_identity *ident = PEP_identityDictToStruct(identity);
   301     PEP_rating color = PEP_rating_undefined;
   302 
   303     @synchronized (self) {
   304         identity_rating(_session, ident, &color);
   305     }
   306 
   307     free_identity(ident);
   308 
   309     return color;
   310 }
   311 
   312 DYNAMIC_API PEP_STATUS identity_rating(PEP_SESSION session, pEp_identity *ident, PEP_rating *color);
   313 
   314 
   315 - (NSArray *)trustwords:(NSString *)fpr forLanguage:(NSString *)languageID shortened:(BOOL)shortened
   316 {
   317     NSMutableArray *array = [NSMutableArray array];
   318 
   319     for (int i = 0; i < [fpr length]; i += 4) {
   320         if (shortened && i >= 20)
   321             break;
   322 
   323         NSString *str = [fpr substringWithRange:NSMakeRange(i, 4)];
   324 
   325         unsigned int value;
   326         [[NSScanner scannerWithString:str] scanHexInt:&value];
   327 
   328         char *word;
   329         size_t size;
   330 
   331         @synchronized (self) {
   332             trustword(_session, value, [languageID UTF8String], &word, &size);
   333         }
   334 
   335         [array addObject:[NSString stringWithUTF8String:word]];
   336         free(word);
   337     }
   338 
   339     return array;
   340 }
   341 
   342 - (void)mySelf:(PEPMutableDict *)identity
   343 {
   344     [identity removeObjectForKey:kPepUserID];
   345 
   346     pEp_identity *ident = PEP_identityDictToStruct(identity);
   347 
   348     @synchronized(self) {
   349         myself(_session, ident);
   350     }
   351 
   352     [identity setValuesForKeysWithDictionary:PEP_identityDictFromStruct(ident)];
   353     free_identity(ident);
   354 }
   355 
   356 - (void)updateIdentity:(PEPMutableDict *)identity
   357 {
   358     pEp_identity *ident = PEP_identityDictToStruct(identity);
   359 
   360     @synchronized(self) {
   361         update_identity(_session, ident);
   362     }
   363 
   364     [identity setValuesForKeysWithDictionary:PEP_identityDictFromStruct(ident)];
   365     free_identity(ident);
   366 }
   367 
   368 - (void)trustPersonalKey:(PEPMutableDict *)identity
   369 {
   370     pEp_identity *ident = PEP_identityDictToStruct(identity);
   371 
   372     @synchronized(self) {
   373         trust_personal_key(_session, ident);
   374     }
   375 
   376     [identity setValuesForKeysWithDictionary:PEP_identityDictFromStruct(ident)];
   377     free_identity(ident);
   378 }
   379 
   380 - (void)keyResetTrust:(PEPMutableDict *)identity
   381 {
   382     pEp_identity *ident = PEP_identityDictToStruct(identity);
   383 
   384     @synchronized(self) {
   385         key_reset_trust(_session, ident);
   386     }
   387 
   388     [identity setValuesForKeysWithDictionary:PEP_identityDictFromStruct(ident)];
   389     free_identity(ident);
   390 }
   391 
   392 - (void)keyMistrusted:(PEPMutableDict *)identity
   393 {
   394     pEp_identity *ident = PEP_identityDictToStruct(identity);
   395 
   396     @synchronized(self) {
   397         key_mistrusted(_session, ident);
   398     }
   399 
   400     [identity setValuesForKeysWithDictionary:PEP_identityDictFromStruct(ident)];
   401     free_identity(ident);
   402 }
   403 
   404 - (void)importKey:(NSString *)keydata
   405 {
   406     @synchronized(self) {
   407         import_key(_session, [keydata UTF8String], [keydata length], NULL);
   408     }
   409 
   410 }
   411 
   412 - (void)logTitle:(nonnull NSString *)title entity:(nonnull NSString *)entity
   413      description:(nullable NSString *)description comment:(nullable NSString *)comment
   414 {
   415     log_event(self.session, [[title precomposedStringWithCanonicalMapping] UTF8String],
   416               [[entity precomposedStringWithCanonicalMapping] UTF8String],
   417               [[description precomposedStringWithCanonicalMapping] UTF8String],
   418               [[comment precomposedStringWithCanonicalMapping] UTF8String]);
   419 }
   420 
   421 - (nonnull NSString *)getLog
   422 {
   423     char *data;
   424     get_crashdump_log(self.session, 0, &data);
   425     NSString *logString = [NSString stringWithUTF8String:data];
   426     return logString;
   427 }
   428 
   429 - (nullable NSString *)getTrustwordsIdentity1:(nonnull PEPDict *)identity1
   430                                     identity2:(nonnull PEPDict *)identity2
   431                                      language:(nullable NSString *)language
   432                                          full:(BOOL)full
   433 {
   434     NSString *result = nil;
   435     char *trustwords = nil;
   436     size_t sizeWritten = 0;
   437 
   438     pEp_identity *ident1 = PEP_identityDictToStruct(identity1);
   439     pEp_identity *ident2 = PEP_identityDictToStruct(identity2);
   440     PEP_STATUS status = get_trustwords(_session, ident1, ident2,
   441                                        [[language precomposedStringWithCanonicalMapping]
   442                                         UTF8String],
   443                                        &trustwords, &sizeWritten, full);
   444     if (status == PEP_STATUS_OK) {
   445         result = [NSString stringWithCString:trustwords
   446                                     encoding:NSUTF8StringEncoding];
   447     }
   448     if (trustwords) {
   449         free(trustwords);
   450     }
   451     return result;
   452 }
   453 
   454 - (nullable NSString *)getTrustwordsMessageDict:(nonnull PEPDict *)messageDict
   455                                    receiverDict:(nonnull PEPDict *)receiverDict
   456                                       keysArray:(PEPStringList * _Nullable)keysArray
   457                                        language:(nullable NSString *)language
   458                                            full:(BOOL)full
   459                                 resultingStatus:(PEP_STATUS * _Nullable)resultingStatus
   460 {
   461     NSString *result = nil;
   462     char *trustwords = nil;
   463 
   464     message *theMessage = PEP_messageDictToStruct(messageDict);
   465 
   466     stringlist_t *keyList = nil;
   467     if (keysArray) {
   468         keyList = PEP_arrayToStringlist(keysArray);
   469     }
   470 
   471     pEp_identity *receiver = PEP_identityDictToStruct(receiverDict);
   472 
   473     PEP_STATUS status = get_message_trustwords(_session, theMessage, keyList, receiver,
   474                                                [[language
   475                                                  precomposedStringWithCanonicalMapping] UTF8String],
   476                                                &trustwords, full);
   477 
   478     if (resultingStatus) {
   479         *resultingStatus = status;
   480     }
   481 
   482     if (status == PEP_STATUS_OK) {
   483         result = [NSString stringWithCString:trustwords
   484                                     encoding:NSUTF8StringEncoding];
   485     }
   486     if (trustwords) {
   487         free(trustwords);
   488     }
   489     return result;
   490 }
   491 
   492 - (NSArray<PEPLanguage *> * _Nonnull)languageList
   493 {
   494     char *chLangs;
   495     get_languagelist(self.session, &chLangs);
   496     NSString *parserInput = [NSString stringWithUTF8String:chLangs];
   497 
   498     NSMutableArray<NSString *> *tokens = [NSMutableArray array];
   499     PEPCSVScanner *scanner = [[PEPCSVScanner alloc] initWithString:parserInput];
   500     while (YES) {
   501         NSString *token = [scanner nextString];
   502         if (!token) {
   503             break;
   504         }
   505         [tokens addObject:token];
   506     }
   507 
   508     NSArray *theTokens = [NSArray arrayWithArray:tokens];
   509     NSMutableArray<PEPLanguage *> *langs = [NSMutableArray new];
   510     while (YES) {
   511         ArrayTake *take = [theTokens takeOrNil:3];
   512         if (!take) {
   513             break;
   514         }
   515         NSArray *elements = take.elements;
   516         PEPLanguage *lang = [[PEPLanguage alloc]
   517                              initWithCode:[elements objectAtIndex:0]
   518                              name:[elements objectAtIndex:1]
   519                              sentence:[elements objectAtIndex:2]];
   520         [langs addObject:lang];
   521         theTokens = take.rest;
   522     }
   523     
   524     return [NSArray arrayWithArray:langs];
   525 }
   526 
   527 @end