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