Skip to content

Commit b31093f

Browse files
6.4.6-rc2 (#1294)
- Fix regression when connecting solely using old STARTTLS connection method - Fix crash when being unable to connect while creating new account
2 parents c1f9a5b + f09b4a6 commit b31093f

File tree

2 files changed

+79
-66
lines changed

2 files changed

+79
-66
lines changed

Monal/Classes/MLStream.m

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ @interface MLInputStream()
5050
//(mutexes can not be unlocked in a thread different from the one it got locked in and NSLock internally uses mutext --> both can not be used)
5151
dispatch_semaphore_t _read_sem;
5252
}
53-
@property (atomic, readonly) void (^incoming_data_handler)(NSData* _Nullable, BOOL, NSError* _Nullable);
53+
@property (atomic, readonly) void (^incoming_data_handler)(NSData* _Nullable, BOOL, NSError* _Nullable, BOOL);
5454
@end
5555

5656
@interface MLOutputStream()
@@ -89,7 +89,7 @@ -(instancetype) initWithSharedState:(MLSharedStreamState*) shared
8989
//this handler will be called by the schedule_read method
9090
//since the framer swallows all data, nw_connection_receive() and the framer cannot race against each other and deliver reordered data
9191
weakify(self);
92-
_incoming_data_handler = ^(NSData* _Nullable content, BOOL is_complete, NSError* _Nullable st_error) {
92+
_incoming_data_handler = ^(NSData* _Nullable content, BOOL is_complete, NSError* _Nullable st_error, BOOL polling_active) {
9393
strongify(self);
9494
if(self == nil)
9595
return;
@@ -142,7 +142,7 @@ -(instancetype) initWithSharedState:(MLSharedStreamState*) shared
142142
[self generateEvent:NSStreamEventEndEncountered];
143143

144144
//try to read again
145-
if(!is_complete && !generate_bytes_available_event)
145+
if(!is_complete && !generate_error_event && !generate_bytes_available_event && polling_active)
146146
[self schedule_read];
147147
};
148148
return self;
@@ -235,7 +235,8 @@ -(void) schedule_read
235235
DDLogDebug(@"now calling nw_framer_parse_input inside framer queue");
236236
nw_framer_parse_input(self.shared_state.framer, 1, BUFFER_SIZE, nil, ^size_t(uint8_t* buffer, size_t buffer_length, bool is_complete) {
237237
DDLogDebug(@"nw_framer_parse_input got callback with is_complete:%@, length=%zu", bool2str(is_complete), (unsigned long)buffer_length);
238-
self.incoming_data_handler([NSData dataWithBytes:buffer length:buffer_length], is_complete, nil);
238+
//we don't want to do "polling" here, our next nw_framer_parse_input will be triggered by the nw_framer_set_input_handler block
239+
self.incoming_data_handler([NSData dataWithBytes:buffer length:buffer_length], is_complete, nil, NO);
239240
return buffer_length;
240241
});
241242
});
@@ -248,7 +249,8 @@ -(void) schedule_read
248249
NSError* st_error = nil;
249250
if(receive_error)
250251
st_error = (NSError*)CFBridgingRelease(nw_error_copy_cf_error(receive_error));
251-
self.incoming_data_handler((NSData*)content, is_complete, st_error);
252+
//we want to do "polling" here (e.g. start our next blocking nw_connection_receive call if we did not receive new data nor any error)
253+
self.incoming_data_handler((NSData*)content, is_complete, st_error, YES);
252254
});
253255
}
254256
}

Monal/Classes/xmpp.m

Lines changed: 72 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -829,19 +829,21 @@ -(void) unfreeze
829829
//this operation has highest priority to make sure it will be executed first once unfrozen
830830
NSBlockOperation* unfreezeOperation = [NSBlockOperation blockOperationWithBlock:^{
831831
//this has to be the very first thing even before unfreezing the parse or send queues
832-
if(self.accountState < kStateReconnecting)
833-
{
834-
DDLogInfo(@"Reloading UNfrozen account %@", self.accountNo);
835-
//(re)read persisted state (could be changed by appex)
836-
[self readState];
832+
@synchronized(self->_stateLockObject) {
833+
if(self.accountState < kStateReconnecting)
834+
{
835+
DDLogInfo(@"Reloading UNfrozen account %@", self.accountNo);
836+
//(re)read persisted state (could be changed by appex)
837+
[self readState];
838+
}
839+
else
840+
DDLogInfo(@"Not reloading UNfrozen account %@, already connected", self.accountNo);
841+
842+
//this must be inside the dispatch async, because it will dispatch *SYNC* to the receive queue and potentially block or even deadlock the system
843+
[self unfreezeParseQueue];
844+
845+
[self unfreezeSendQueue];
837846
}
838-
else
839-
DDLogInfo(@"Not reloading UNfrozen account %@, already connected", self.accountNo);
840-
841-
//this must be inside the dispatch async, because it will dispatch *SYNC* to the receive queue and potentially block or even deadlock the system
842-
[self unfreezeParseQueue];
843-
844-
[self unfreezeSendQueue];
845847
}];
846848
unfreezeOperation.queuePriority = NSOperationQueuePriorityVeryHigh; //make sure this will become the first operation executed once unfrozen
847849
[self->_receiveQueue addOperations: @[unfreezeOperation] waitUntilFinished:NO];
@@ -962,11 +964,67 @@ -(void) disconnectWithStreamError:(MLXMLNode* _Nullable) streamError
962964
-(void) disconnectWithStreamError:(MLXMLNode* _Nullable) streamError andExplicitLogout:(BOOL) explicitLogout
963965
{
964966
DDLogInfo(@"disconnect called...");
967+
//commonly used by shortcut outside of receive queue and called from inside the receive queue, too
968+
monal_void_block_t doExplicitLogout = ^{
969+
@synchronized(self->_stateLockObject) {
970+
DDLogVerbose(@"explicitLogout == YES --> clearing state");
971+
972+
//preserve unAckedStanzas even on explicitLogout and resend them on next connect
973+
//if we don't do this, messages could get lost when logging out directly after sending them
974+
//and: sending messages twice is less intrusive than silently loosing them
975+
NSMutableArray* stanzas = self.unAckedStanzas;
976+
977+
//reset smacks state to sane values (this can be done even if smacks is not supported)
978+
[self initSM3];
979+
self.unAckedStanzas = stanzas;
980+
981+
//inform all old iq handlers of invalidation and clear _iqHandlers dictionary afterwards
982+
@synchronized(self->_iqHandlers) {
983+
for(NSString* iqid in [self->_iqHandlers allKeys])
984+
{
985+
DDLogWarn(@"Invalidating iq handler for iq id '%@'", iqid);
986+
if(self->_iqHandlers[iqid][@"handler"] != nil)
987+
$invalidate(self->_iqHandlers[iqid][@"handler"], $ID(account, self), $ID(reason, @"disconnect"));
988+
else if(self->_iqHandlers[iqid][@"errorHandler"])
989+
((monal_iq_handler_t)self->_iqHandlers[iqid][@"errorHandler"])(nil);
990+
}
991+
self->_iqHandlers = [NSMutableDictionary new];
992+
}
993+
994+
//invalidate pubsub queue (*after* iq handlers that also might invalidate a result handler of the queued operation)
995+
[self.pubsub invalidateQueue];
996+
997+
//clear pipeline cache
998+
self->_pipeliningState = kPipelinedNothing;
999+
self->_cachedStreamFeaturesBeforeAuth = nil;
1000+
self->_cachedStreamFeaturesAfterAuth = nil;
1001+
1002+
//clear all reconnection handlers
1003+
@synchronized(self->_reconnectionHandlers) {
1004+
[self->_reconnectionHandlers removeAllObjects];
1005+
}
1006+
1007+
//persist these changes
1008+
[self persistState];
1009+
}
1010+
1011+
[[DataLayer sharedInstance] resetContactsForAccount:self.accountNo];
1012+
1013+
//trigger view updates to make sure enabled/disabled account state propagates to all ui elements
1014+
[[MLNotificationQueue currentQueue] postNotificationName:kMonalRefresh object:nil userInfo:nil];
1015+
};
9651016

9661017
//short-circuit common case without dispatching to receive queue
9671018
//this allows calling a noop disconnect while the receive queue is frozen
968-
if(self->_accountState<kStateReconnecting && !explicitLogout)
1019+
//every change to our state is locked by our _stateLockObject and the receive queue unfreeze uses this lock, too
1020+
//--> an unfreeze can not happen half way through this explicit logout and therefore can't corrupt any state
1021+
//--> an unfreeze is needed to dispatch to the receive queue which is used by our connect method
1022+
if(self->_accountState<kStateReconnecting)
1023+
{
1024+
if(explicitLogout)
1025+
doExplicitLogout();
9691026
return;
1027+
}
9701028

9711029
MLAssert(!_receiveQueue.suspended, @"receive queue suspended while trying to disconnect!");
9721030

@@ -996,54 +1054,7 @@ -(void) disconnectWithStreamError:(MLXMLNode* _Nullable) streamError andExplicit
9961054
{
9971055
DDLogVerbose(@"not doing logout because already logged out, but clearing state if explicitLogout was yes");
9981056
if(explicitLogout)
999-
{
1000-
@synchronized(self->_stateLockObject) {
1001-
DDLogVerbose(@"explicitLogout == YES --> clearing state");
1002-
1003-
//preserve unAckedStanzas even on explicitLogout and resend them on next connect
1004-
//if we don't do this, messages could get lost when logging out directly after sending them
1005-
//and: sending messages twice is less intrusive than silently loosing them
1006-
NSMutableArray* stanzas = self.unAckedStanzas;
1007-
1008-
//reset smacks state to sane values (this can be done even if smacks is not supported)
1009-
[self initSM3];
1010-
self.unAckedStanzas = stanzas;
1011-
1012-
//inform all old iq handlers of invalidation and clear _iqHandlers dictionary afterwards
1013-
@synchronized(self->_iqHandlers) {
1014-
for(NSString* iqid in [self->_iqHandlers allKeys])
1015-
{
1016-
DDLogWarn(@"Invalidating iq handler for iq id '%@'", iqid);
1017-
if(self->_iqHandlers[iqid][@"handler"] != nil)
1018-
$invalidate(self->_iqHandlers[iqid][@"handler"], $ID(account, self), $ID(reason, @"disconnect"));
1019-
else if(self->_iqHandlers[iqid][@"errorHandler"])
1020-
((monal_iq_handler_t)self->_iqHandlers[iqid][@"errorHandler"])(nil);
1021-
}
1022-
self->_iqHandlers = [NSMutableDictionary new];
1023-
}
1024-
1025-
//invalidate pubsub queue (*after* iq handlers that also might invalidate a result handler of the queued operation)
1026-
[self.pubsub invalidateQueue];
1027-
1028-
//clear pipeline cache
1029-
self->_pipeliningState = kPipelinedNothing;
1030-
self->_cachedStreamFeaturesBeforeAuth = nil;
1031-
self->_cachedStreamFeaturesAfterAuth = nil;
1032-
1033-
//clear all reconnection handlers
1034-
@synchronized(self->_reconnectionHandlers) {
1035-
[self->_reconnectionHandlers removeAllObjects];
1036-
}
1037-
1038-
//persist these changes
1039-
[self persistState];
1040-
}
1041-
1042-
[[DataLayer sharedInstance] resetContactsForAccount:self.accountNo];
1043-
1044-
//trigger view updates to make sure enabled/disabled account state propagates to all ui elements
1045-
[[MLNotificationQueue currentQueue] postNotificationName:kMonalRefresh object:nil userInfo:nil];
1046-
}
1057+
doExplicitLogout();
10471058
return;
10481059
}
10491060
DDLogInfo(@"disconnecting");

0 commit comments

Comments
 (0)