pEpObjCAdapter/PEPSync.m
author Dirk Zimmermann <dz@pep.security>
Thu, 12 Sep 2019 16:41:52 +0200
branchIOSAD-141
changeset 1245 cd15ff3a40b9
parent 1152 c5d8810c470e
child 1252 e43abf52f198
permissions -rw-r--r--
IOSAD-141 Don't share session between threads.
     1 //
     2 //  PEPSync.m
     3 //  pEpObjCAdapter
     4 //
     5 //  Created by Dirk Zimmermann on 04.10.18.
     6 //  Copyright © 2018 p≡p. All rights reserved.
     7 //
     8 
     9 #import <os/log.h>
    10 
    11 #import "PEPSync.h"
    12 
    13 #import "pEpEngine.h"
    14 
    15 #import "PEPSendMessageDelegate.h"
    16 #import "PEPNotifyHandshakeDelegate.h"
    17 #import "PEPMessageUtil.h"
    18 #import "PEPMessage.h"
    19 #import "PEPQueue.h"
    20 #import "PEPObjCAdapter.h"
    21 #import "NSError+PEP+Internal.h"
    22 #import "PEPSessionProvider.h"
    23 #import "PEPInternalSession.h"
    24 
    25 // MARK: - Internals
    26 
    27 os_log_t s_logger;
    28 
    29 typedef PEP_STATUS (* t_messageToSendCallback)(struct _message *msg);
    30 typedef int (* t_injectSyncCallback)(SYNC_EVENT ev, void *management);
    31 
    32 @interface PEPSync ()
    33 
    34 + (PEPSync * _Nullable)instance;
    35 
    36 @property (nonatomic, nonnull) PEPQueue *queue;
    37 @property (nonatomic, nullable) NSThread *syncThread;
    38 @property (nonatomic, nullable) NSConditionLock *conditionLockForJoiningSyncThread;
    39 
    40 /**
    41  @Return: The callback for message sending that should be used on every session init.
    42  */
    43 + (t_messageToSendCallback)messageToSendCallback;
    44 
    45 /**
    46  @Return: The callback for injectiong sync messages that should be used on every session init.
    47  */
    48 + (t_injectSyncCallback)injectSyncCallback;
    49 
    50 - (PEP_STATUS)messageToSend:(struct _message *)msg;
    51 
    52 - (int)injectSyncEvent:(SYNC_EVENT)event;
    53 
    54 - (PEP_STATUS)notifyHandshake:(pEp_identity *)me
    55                       partner:(pEp_identity *)partner
    56                        signal:(sync_handshake_signal)signal;
    57 
    58 - (SYNC_EVENT)retrieveNextSyncEvent:(time_t)threshold;
    59 
    60 @end
    61 
    62 // MARK: - Callbacks called by the engine, used in session init
    63 
    64 static PEP_STATUS s_messageToSendObjc(struct _message *msg)
    65 {
    66     PEPSync *pEpSync = [PEPSync instance];
    67 
    68     if (pEpSync) {
    69         return [pEpSync messageToSend:msg];
    70     } else {
    71         return PEP_SYNC_NO_NOTIFY_CALLBACK;
    72     }
    73 }
    74 
    75 static int s_inject_sync_event(SYNC_EVENT ev, void *management)
    76 {
    77     PEPSync *pEpSync = [PEPSync instance];
    78 
    79     if (pEpSync) {
    80         return [pEpSync injectSyncEvent:ev];
    81     } else {
    82         return 1;
    83     }
    84 }
    85 
    86 // MARK: - Callbacks called by the engine, used in register_sync_callbacks
    87 
    88 static PEP_STATUS s_notifyHandshake(pEp_identity *me,
    89                                     pEp_identity *partner,
    90                                     sync_handshake_signal signal)
    91 {
    92     PEPSync *pEpSync = [PEPSync instance];
    93 
    94     if (pEpSync) {
    95         return [pEpSync notifyHandshake:me partner:partner signal:signal];
    96     } else {
    97         return PEP_SYNC_NO_NOTIFY_CALLBACK;
    98     }
    99 }
   100 
   101 static SYNC_EVENT s_retrieve_next_sync_event(void *management, unsigned threshold)
   102 {
   103     PEPSync *sync = [PEPSync instance];
   104     return [sync retrieveNextSyncEvent:threshold];
   105 }
   106 
   107 // MARK: - Internal globals
   108 
   109 static __weak PEPSync *s_pEpSync;
   110 
   111 // MARK: - Public PEPSync class
   112 
   113 @implementation PEPSync
   114 
   115 + (t_messageToSendCallback)messageToSendCallback
   116 {
   117     return s_messageToSendObjc;
   118 }
   119 
   120 + (t_injectSyncCallback)injectSyncCallback
   121 {
   122     return s_inject_sync_event;
   123 }
   124 
   125 + (PEP_SESSION)createSession:(NSError **)error
   126 {
   127     PEP_SESSION session = NULL;
   128 
   129     PEP_STATUS status = init(&session,
   130                              [PEPSync messageToSendCallback],
   131                              [PEPSync injectSyncCallback]);
   132 
   133     if (status != PEP_STATUS_OK) {
   134         if (error) {
   135             *error = [NSError errorWithPEPStatusInternal:status];
   136             os_log(s_logger, "error creating session: %{public}@", *error);
   137         }
   138         return nil;
   139     }
   140 
   141     return session;
   142 }
   143 
   144 - (instancetype)initWithSendMessageDelegate:(id<PEPSendMessageDelegate>
   145                                              _Nullable)sendMessageDelegate
   146                     notifyHandshakeDelegate:(id<PEPNotifyHandshakeDelegate>
   147                                              _Nullable)notifyHandshakeDelegate
   148 {
   149     if (self = [super init]) {
   150         _sendMessageDelegate = sendMessageDelegate;
   151         _notifyHandshakeDelegate = notifyHandshakeDelegate;
   152         _queue = [PEPQueue new];
   153         s_pEpSync = self;
   154     }
   155     return self;
   156 }
   157 
   158 - (void)startup
   159 {
   160     [self assureMainSessionExists];
   161 
   162     self.conditionLockForJoiningSyncThread = [[NSConditionLock alloc] initWithCondition:NO];
   163     NSThread *theSyncThread = [[NSThread alloc] initWithTarget:self
   164                                                       selector:@selector(syncThreadLoop:)
   165                                                         object:nil];
   166     theSyncThread.name = @"pEp-sync-loop";
   167     self.syncThread = theSyncThread;
   168     [theSyncThread start];
   169 }
   170 
   171 - (void)shutdown
   172 {
   173     if (self.syncThread) {
   174         [self injectSyncEvent:nil];
   175         [self.conditionLockForJoiningSyncThread lockWhenCondition:YES];
   176         [self.conditionLockForJoiningSyncThread unlock];
   177     }
   178     self.conditionLockForJoiningSyncThread = nil;
   179 }
   180 
   181 // MARK: - Private
   182 
   183 + (void)initialize
   184 {
   185     s_logger = os_log_create("security.pEp.adapter", "PEPSync");
   186 }
   187 
   188 + (PEPSync * _Nullable)instance
   189 {
   190     return s_pEpSync;
   191 }
   192 
   193 - (void)assureMainSessionExists
   194 {
   195     PEPInternalSession *session __attribute__((unused)) = [PEPSessionProvider session];
   196 }
   197 
   198 - (void)syncThreadLoop:(id)object
   199 {
   200     [self.conditionLockForJoiningSyncThread lock];
   201 
   202     os_log(s_logger, "trying to start the sync loop");
   203 
   204     PEPInternalSession *session = [PEPSessionProvider session];
   205 
   206     if (session) {
   207         PEP_STATUS status = register_sync_callbacks(session.session, nil, s_notifyHandshake,
   208                                                     s_retrieve_next_sync_event);
   209         if (status == PEP_STATUS_OK) {
   210             status = do_sync_protocol(session.session, nil);
   211             if (status != PEP_STATUS_OK) {
   212                 os_log_error(s_logger, "do_sync_protocol returned PEP_STATUS %d", status);
   213                 os_log(s_logger, "sync loop is NOT running");
   214             }
   215             unregister_sync_callbacks(session.session);
   216         } else {
   217             os_log_error(s_logger, "register_sync_callbacks returned PEP_STATUS %d", status);
   218             os_log(s_logger, "sync loop is NOT running");
   219         }
   220     } else {
   221         os_log_error(s_logger, "could not create session for starting the sync loop");
   222     }
   223 
   224     os_log(s_logger, "sync loop finished");
   225 
   226     session = nil;
   227 
   228     [self.conditionLockForJoiningSyncThread unlockWithCondition:YES];
   229 }
   230 
   231 - (PEP_STATUS)messageToSend:(struct _message *)msg
   232 {
   233     if (self.sendMessageDelegate) {
   234         PEPMessage *theMessage = pEpMessageFromStruct(msg);
   235         return (PEP_STATUS) [self.sendMessageDelegate sendMessage:theMessage];
   236     } else {
   237         return PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
   238     }
   239 }
   240 
   241 - (int)injectSyncEvent:(SYNC_EVENT)event
   242 {
   243     [self.queue enqueue:[NSValue valueWithBytes:&event objCType:@encode(SYNC_EVENT)]];
   244     return 0;
   245 }
   246 
   247 - (PEP_STATUS)notifyHandshake:(pEp_identity *)me
   248                       partner:(pEp_identity *)partner
   249                        signal:(sync_handshake_signal)signal
   250 {
   251     if (self.notifyHandshakeDelegate) {
   252         PEPIdentity *meIdentity = PEP_identityFromStruct(me);
   253         PEPIdentity *partnerIdentity = PEP_identityFromStruct(partner);
   254         return (PEP_STATUS) [self.notifyHandshakeDelegate
   255                              notifyHandshake:NULL
   256                              me:meIdentity
   257                              partner:partnerIdentity
   258                              signal:(PEPSyncHandshakeSignal) signal];
   259     } else {
   260         return PEP_SYNC_NO_NOTIFY_CALLBACK;
   261     }
   262 }
   263 
   264 - (SYNC_EVENT)retrieveNextSyncEvent:(time_t)threshold
   265 {
   266     NSValue *value = [self.queue timedDequeue:&threshold];
   267     if (value) {
   268         SYNC_EVENT event;
   269         [value getValue:&event];
   270         return event;
   271     } else {
   272         return new_sync_timeout_event();
   273     }
   274 }
   275 
   276 @end