You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

425 lines
13 KiB

3 years ago
3 years ago
3 years ago
  1. //
  2. // PEPSync.m
  3. // pEpObjCAdapter
  4. //
  5. // Created by Dirk Zimmermann on 04.10.18.
  6. // Copyright © 2018 pp. All rights reserved.
  7. //
  8. #import "pEpEngine.h"
  9. #import "PEPSync.h"
  10. #import "PEPSync_Internal.h"
  11. #import "message_api.h"
  12. #import "PEPSendMessageDelegate.h"
  13. #import "PEPNotifyHandshakeDelegate.h"
  14. #import "PEPMessage.h"
  15. #import "PEPQueue.h"
  16. #import "PEPObjCAdapter.h"
  17. #import "NSError+PEP+Internal.h"
  18. #import "PEPSessionProvider.h"
  19. #import "PEPInternalSession.h"
  20. #import "PEPPassphraseCache.h"
  21. #import "PEPPassphraseUtil.h"
  22. #import "Logger.h"
  23. #import "PEPIdentity+Engine.h"
  24. #import "PEPMessage+Engine.h"
  25. #import "PEPAutoPointer+Message.h"
  26. // MARK: - Internals
  27. typedef PEP_STATUS (* t_messageToSendCallback)(struct _message * _Nullable msg);
  28. typedef int (* t_injectSyncCallback)(SYNC_EVENT ev, void *management);
  29. @interface PEPSync ()
  30. @property (nonatomic, nonnull) PEPQueue *queue;
  31. @property (nonatomic, nullable) NSThread *syncThread;
  32. @property (nonatomic, nullable) NSConditionLock *conditionLockForJoiningSyncThread;
  33. /// Used to block messageToSend() until the client configured a passphrase.
  34. @property (atomic, nullable) dispatch_group_t blockmessageToSendGroup;
  35. /// Object used for synchronizing modifications of `blockmessageToSendGroup`.
  36. @property (atomic, nonnull) NSObject *lockObjectBlockmessageToSendGroupChanges;
  37. /// True if someone called `shutdown()`
  38. @property (atomic) BOOL shutdownRequested;
  39. /// The session created and used by the sync loop
  40. @property (nonatomic, nullable) PEPInternalSession *syncLoopSession;
  41. /// @Return: The callback for message sending that should be used on every session init.
  42. + (t_messageToSendCallback)messageToSendCallback;
  43. /// @Return: The callback for injectiong sync messages that should be used on every session init.
  44. + (t_injectSyncCallback)injectSyncCallback;
  45. /// @Return: The callback for ensure_passphrase that should be used on every session init.
  46. + (ensure_passphrase_t)ensurePassphraseCallback;
  47. - (PEP_STATUS)messageToSend:(struct _message * _Nullable)msg;
  48. - (int)injectSyncEvent:(SYNC_EVENT)event isFromShutdown:(BOOL)isFromShutdown;
  49. - (PEP_STATUS)notifyHandshake:(pEp_identity *)me
  50. partner:(pEp_identity *)partner
  51. signal:(sync_handshake_signal)signal;
  52. - (SYNC_EVENT)retrieveNextSyncEvent:(time_t)threshold;
  53. @end
  54. // MARK: - Callbacks called by the engine, used in session init
  55. static PEP_STATUS s_messageToSendObjc(struct _message * _Nullable msg)
  56. {
  57. PEPSync *pEpSync = [PEPSync sharedInstance];
  58. if (pEpSync) {
  59. return [pEpSync messageToSend:msg];
  60. } else {
  61. return PEP_SYNC_NO_NOTIFY_CALLBACK;
  62. }
  63. }
  64. static int s_inject_sync_event(SYNC_EVENT ev, void *management)
  65. {
  66. PEPSync *pEpSync = [PEPSync sharedInstance];
  67. if (pEpSync) {
  68. // The inject comes from the engine, so we know it's not the
  69. // adapter client calling shutdown.
  70. return [pEpSync injectSyncEvent:ev isFromShutdown:NO];
  71. } else {
  72. return 1;
  73. }
  74. }
  75. static PEP_STATUS s_ensure_passphrase(PEP_SESSION session, const char *fpr)
  76. {
  77. PEP_STATUS status = (PEP_STATUS) [PEPPassphraseUtil
  78. runWithPasswordsSession:session
  79. block:^PEP_STATUS(PEP_SESSION session) {
  80. return probe_encrypt(session, fpr);
  81. }];
  82. return status;
  83. }
  84. // MARK: - Callbacks called by the engine, used in register_sync_callbacks
  85. static PEP_STATUS s_notifyHandshake(pEp_identity *me,
  86. pEp_identity *partner,
  87. sync_handshake_signal signal)
  88. {
  89. PEPSync *pEpSync = [PEPSync sharedInstance];
  90. if (pEpSync) {
  91. return [pEpSync notifyHandshake:me partner:partner signal:signal];
  92. } else {
  93. return PEP_SYNC_NO_NOTIFY_CALLBACK;
  94. }
  95. }
  96. static SYNC_EVENT s_retrieve_next_sync_event(void *management, unsigned threshold)
  97. {
  98. PEPSync *sync = [PEPSync sharedInstance];
  99. return [sync retrieveNextSyncEvent:threshold];
  100. }
  101. // MARK: - Internal globals
  102. static __weak PEPSync *s_pEpSync;
  103. // MARK: - Public PEPSync class
  104. @implementation PEPSync
  105. + (t_messageToSendCallback)messageToSendCallback
  106. {
  107. return s_messageToSendObjc;
  108. }
  109. + (t_injectSyncCallback)injectSyncCallback
  110. {
  111. return s_inject_sync_event;
  112. }
  113. + (ensure_passphrase_t)ensurePassphraseCallback
  114. {
  115. return s_ensure_passphrase;
  116. }
  117. + (PEP_SESSION)createSession:(NSError **)error
  118. {
  119. PEP_SESSION session = NULL;
  120. PEP_STATUS status = init(&session,
  121. [PEPSync messageToSendCallback],
  122. [PEPSync injectSyncCallback],
  123. [PEPSync ensurePassphraseCallback]);
  124. if (status != PEP_STATUS_OK) {
  125. if (error) {
  126. *error = [NSError errorWithPEPStatusInternal:status];
  127. LogError(@"error creating session: %@", *error);
  128. }
  129. return nil;
  130. }
  131. return session;
  132. }
  133. - (instancetype)initWithSendMessageDelegate:(id<PEPSendMessageDelegate>
  134. _Nullable)sendMessageDelegate
  135. notifyHandshakeDelegate:(id<PEPNotifyHandshakeDelegate>
  136. _Nullable)notifyHandshakeDelegate
  137. {
  138. if (self = [super init]) {
  139. _sendMessageDelegate = sendMessageDelegate;
  140. _notifyHandshakeDelegate = notifyHandshakeDelegate;
  141. _queue = [PEPQueue new];
  142. s_pEpSync = self;
  143. _lockObjectBlockmessageToSendGroupChanges = [NSObject new];
  144. _shutdownRequested = NO;
  145. }
  146. return self;
  147. }
  148. - (void)startup
  149. {
  150. self.shutdownRequested = NO;
  151. [self stopWaiting];
  152. if (self.syncThread != nil) {
  153. // already started
  154. return;
  155. }
  156. NSThread *theSyncThread = [[NSThread alloc] initWithTarget:self
  157. selector:@selector(syncThreadLoop:)
  158. object:nil];
  159. theSyncThread.name = @"pEp-sync-loop";
  160. self.syncThread = theSyncThread;
  161. // Make sure queue is empty when we start.
  162. [self.queue removeAllObjects];
  163. self.conditionLockForJoiningSyncThread = [[NSConditionLock alloc] initWithCondition:NO];
  164. [theSyncThread start];
  165. }
  166. - (void)shutdown
  167. {
  168. self.shutdownRequested = YES;
  169. [self stopWaiting];
  170. if (self.syncThread) {
  171. [self injectSyncEvent:nil isFromShutdown:YES];
  172. }
  173. }
  174. - (void)handleNewPassphraseConfigured {
  175. [self stopWaiting];
  176. }
  177. // MARK: - Internal
  178. + (PEPSync * _Nullable)sharedInstance
  179. {
  180. return s_pEpSync;
  181. }
  182. // MARK: - Private
  183. - (void)assureMainSessionExists
  184. {
  185. PEPInternalSession *session __attribute__((unused)) = [PEPSessionProvider session];
  186. }
  187. - (void)syncThreadLoop:(id)object
  188. {
  189. [self.conditionLockForJoiningSyncThread lock];
  190. LogInfo(@"trying to start the sync loop");
  191. self.syncLoopSession = [PEPSessionProvider session];
  192. if (self.syncLoopSession) {
  193. PEP_STATUS status = register_sync_callbacks(self.syncLoopSession.session,
  194. nil,
  195. s_notifyHandshake,
  196. s_retrieve_next_sync_event);
  197. if (status == PEP_STATUS_OK) {
  198. status = do_sync_protocol(self.syncLoopSession.session, nil);
  199. if (status != PEP_STATUS_OK) {
  200. LogError(@"do_sync_protocol returned PEP_STATUS %d", status);
  201. LogInfo(@"sync loop is NOT running");
  202. }
  203. unregister_sync_callbacks(self.syncLoopSession.session);
  204. } else {
  205. LogError(@"register_sync_callbacks returned PEP_STATUS %d", status);
  206. LogInfo(@"sync loop is NOT running");
  207. }
  208. } else {
  209. LogError(@"could not create session for starting the sync loop");
  210. }
  211. LogInfo(@"sync loop finished")
  212. self.syncLoopSession = nil;
  213. self.syncThread = nil;
  214. [self.conditionLockForJoiningSyncThread unlockWithCondition:YES];
  215. }
  216. - (PEP_STATUS)messageToSend:(struct _message * _Nullable)msg
  217. {
  218. // auto destruct
  219. PEPAutoPointer *msgPtr = [PEPAutoPointer autoPointerWithMessage:msg];
  220. [self blockUntilPassphraseIsEnteredIfRequired];
  221. if (self.shutdownRequested) {
  222. // The client has signalled that she was unable to provide a passphrase by calling
  223. // `shutdown()`.
  224. // We signal the same to the Engine.
  225. return PEP_SYNC_NO_CHANNEL;
  226. }
  227. if (msg == NULL && [NSThread currentThread] == self.syncThread) {
  228. static NSMutableArray *passphrasesCopy = nil;
  229. static BOOL makeNewCopy = YES;
  230. if (makeNewCopy) {
  231. passphrasesCopy = [NSMutableArray
  232. arrayWithArray:[self.syncLoopSession.passphraseCache passphrases]];
  233. if (self.syncLoopSession.passphraseCache.storedPassphrase) {
  234. [passphrasesCopy
  235. insertObject:self.syncLoopSession.passphraseCache.storedPassphrase
  236. atIndex:0];
  237. }
  238. if ([passphrasesCopy count] == 0) {
  239. makeNewCopy = YES;
  240. [self nextCallMustWait];
  241. return PEP_PASSPHRASE_REQUIRED;
  242. } else {
  243. makeNewCopy = NO;
  244. }
  245. }
  246. if ([passphrasesCopy count] == 0) {
  247. makeNewCopy = YES;
  248. [self nextCallMustWait];
  249. return PEP_WRONG_PASSPHRASE;
  250. } else {
  251. NSString *password = [passphrasesCopy firstObject];
  252. [passphrasesCopy removeObjectAtIndex:0];
  253. [self.syncLoopSession configurePassphrase:password error:nil];
  254. return PEP_STATUS_OK;
  255. }
  256. } else if (msg != NULL) {
  257. if (self.sendMessageDelegate) {
  258. PEPMessage *theMessage = [PEPMessage fromStruct:msg];
  259. return (PEP_STATUS) [self.sendMessageDelegate sendMessage:theMessage];
  260. } else {
  261. return PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
  262. }
  263. } else {
  264. return PEP_SYNC_ILLEGAL_MESSAGE;
  265. }
  266. msgPtr = nil; // please the compiler
  267. }
  268. /// Injects the given event into the queue.
  269. /// @param event The event to inject, which may contain a nil value, which means the
  270. /// sync loop should stop.
  271. /// @param isFromShutdown This is `YES` when coming from `shutdown` itself, and `NO`
  272. /// otherwise (e.g., when the engine requests a shutdown by injecting a nil event.
  273. - (int)injectSyncEvent:(SYNC_EVENT)event isFromShutdown:(BOOL)isFromShutdown
  274. {
  275. NSValue *value = [NSValue valueWithBytes:&event objCType:@encode(SYNC_EVENT)];
  276. if (event) {
  277. [self.queue enqueue:value];
  278. } else {
  279. // This is a nil event, which means shut it all down.
  280. if ([NSThread currentThread] != self.syncThread) {
  281. // Only do this when the shutdown is not coming in on the sync thread.
  282. // Otherwise it will just exit out of the sync loop and be done.
  283. [self.queue prequeue:value];
  284. [self.conditionLockForJoiningSyncThread lockWhenCondition:YES];
  285. [self.conditionLockForJoiningSyncThread unlock];
  286. self.conditionLockForJoiningSyncThread = nil;
  287. }
  288. if (!isFromShutdown) {
  289. // Only inform the delegate if the shutdown came from the engine
  290. [self.notifyHandshakeDelegate
  291. notifyHandshake:NULL
  292. me:nil
  293. partner:nil
  294. signal:PEPSyncHandshakeSignalStop];
  295. }
  296. }
  297. return 0;
  298. }
  299. - (PEP_STATUS)notifyHandshake:(pEp_identity *)me
  300. partner:(pEp_identity *)partner
  301. signal:(sync_handshake_signal)signal
  302. {
  303. if (self.notifyHandshakeDelegate) {
  304. PEPIdentity *meIdentity = [PEPIdentity fromStruct:me];
  305. PEPIdentity *partnerIdentity = partner != nil ? [PEPIdentity fromStruct:partner] : nil;
  306. return (PEP_STATUS) [self.notifyHandshakeDelegate
  307. notifyHandshake:NULL
  308. me:meIdentity
  309. partner:partnerIdentity
  310. signal:(PEPSyncHandshakeSignal) signal];
  311. } else {
  312. return PEP_SYNC_NO_NOTIFY_CALLBACK;
  313. }
  314. }
  315. - (SYNC_EVENT)retrieveNextSyncEvent:(time_t)threshold
  316. {
  317. NSValue *value = [self.queue timedDequeue:&threshold];
  318. if (value) {
  319. SYNC_EVENT event;
  320. [value getValue:&event];
  321. return event;
  322. } else {
  323. return new_sync_timeout_event();
  324. }
  325. }
  326. // MARK: Blocking (messageToSend)
  327. - (void)blockUntilPassphraseIsEnteredIfRequired {
  328. if (self.blockmessageToSendGroup) {
  329. dispatch_group_wait(self.blockmessageToSendGroup, DISPATCH_TIME_FOREVER);
  330. }
  331. }
  332. - (void)nextCallMustWait {
  333. @synchronized (self.blockmessageToSendGroup) {
  334. if (!self.blockmessageToSendGroup) {
  335. self.blockmessageToSendGroup = dispatch_group_create();
  336. }
  337. dispatch_group_enter(self.blockmessageToSendGroup);
  338. }
  339. }
  340. - (void)stopWaiting {
  341. @synchronized (self.blockmessageToSendGroup) {
  342. if (self.blockmessageToSendGroup) {
  343. dispatch_group_leave(self.blockmessageToSendGroup);
  344. self.blockmessageToSendGroup = nil;
  345. }
  346. }
  347. }
  348. @end