Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6.4.6 #1288

Merged
merged 25 commits into from
Nov 19, 2024
Merged

6.4.6 #1288

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c21138b
Show timestamp of last received push in notification debug menu
tmolitor-stud-tu Oct 15, 2024
42df817
Improve bare muc handling
tmolitor-stud-tu Oct 15, 2024
1081da1
Use dedicated resource string for quicksy users
tmolitor-stud-tu Oct 16, 2024
047cd72
Fix pm bug in eb5da94afff8a27546a630988a4a92be2c6da0d6
tmolitor-stud-tu Oct 19, 2024
e688d9e
Fix unsolicited settings close
tmolitor-stud-tu Oct 20, 2024
967cf60
Fix bug in MLStream on slow connections
tmolitor-stud-tu Nov 2, 2024
530f34d
Fix bogus assertion (possibly some race condition)
tmolitor-stud-tu Nov 8, 2024
daf055a
Fix crash in MLDelayableTimer when calling invalidate
tmolitor-stud-tu Nov 9, 2024
3256769
6.4.6-rc1 (#1287)
tmolitor-stud-tu Nov 9, 2024
2535064
Use dedicated resource string for quicksy users
tmolitor-stud-tu Oct 16, 2024
55ae57a
Fix unsolicited settings close
tmolitor-stud-tu Oct 20, 2024
22ee93a
Fix bug in MLStream on slow connections
tmolitor-stud-tu Nov 2, 2024
05e87e6
Fix bogus assertion (possibly some race condition)
tmolitor-stud-tu Nov 8, 2024
306a44f
Fix crash in MLDelayableTimer when calling invalidate
tmolitor-stud-tu Nov 9, 2024
68d3029
Revert "6.4.6-rc1"
tmolitor-stud-tu Nov 9, 2024
961bbc2
6.4.6-rc2 (#1289)
tmolitor-stud-tu Nov 9, 2024
fe89a77
6.4.6-rc3 (#1290)
tmolitor-stud-tu Nov 9, 2024
b7b4a79
Fix backported code
tmolitor-stud-tu Nov 9, 2024
18f42d2
6.4.6-rc4 (#1291)
tmolitor-stud-tu Nov 9, 2024
e64a13b
Fix backported code
tmolitor-stud-tu Nov 9, 2024
944db5d
Merge branch 'beta' into oldstable/6.4-line
tmolitor-stud-tu Nov 9, 2024
c1f9a5b
6.4.6-rc1 (#1292)
tmolitor-stud-tu Nov 9, 2024
490e41d
Fix double-disconnect assertion when creating unusable account
tmolitor-stud-tu Nov 12, 2024
f09b4a6
Fix regression when doing starttls
tmolitor-stud-tu Nov 12, 2024
b31093f
6.4.6-rc2 (#1294)
tmolitor-stud-tu Nov 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Monal/Classes/ActiveChatsViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, weak) IBOutlet UIBarButtonItem* settingsButton;
@property (weak, nonatomic) IBOutlet UIBarButtonItem* spinnerButton;
@property (nonatomic, weak) IBOutlet UIBarButtonItem* composeButton;
@property (nonatomic, strong) chatViewController* currentChatViewController;
@property (nonatomic, strong) UIActivityIndicatorView* spinner;
@property (atomic, readonly) chatViewController* _Nullable currentChatView;

-(void) showCallContactNotFoundAlert:(NSString*) jid;
-(void) callContact:(MLContact*) contact withUIKitSender:(_Nullable id) sender;
Expand Down
19 changes: 15 additions & 4 deletions Monal/Classes/ActiveChatsViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -361,13 +361,13 @@ -(void) refreshDisplay

dispatch_async(dispatch_get_main_queue(), ^{
//make sure we don't display a chat view for a disabled account
if(self.currentChatViewController != nil && self.currentChatViewController.contact != nil)
if([MLNotificationManager sharedInstance].currentContact != nil)
{
BOOL found = NO;
for(NSDictionary* accountDict in [[DataLayer sharedInstance] enabledAccountList])
{
NSNumber* accountNo = accountDict[kAccountID];
if(self.currentChatViewController.contact.accountId.intValue == accountNo.intValue)
NSNumber* accountId = accountDict[kAccountID];
if([MLNotificationManager sharedInstance].currentContact.accountId.intValue == accountId.intValue)
found = YES;
}
if(!found)
Expand Down Expand Up @@ -879,6 +879,7 @@ -(void) presentSplitPlaceholder
UIViewController* detailsViewController = [[SwiftuiInterface new] makeViewWithName:@"ChatPlaceholder"];
[self showDetailViewController:detailsViewController sender:self];
}
[MLNotificationManager sharedInstance].currentContact = nil;
}

-(void) showNotificationSettings
Expand Down Expand Up @@ -1094,7 +1095,6 @@ -(void) prepareForSegue:(UIStoryboardSegue*) segue sender:(id) sender
UIBarButtonItem* barButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = barButtonItem;
[chatVC setupWithContact:sender];
self.currentChatViewController = chatVC;
}
else if([segue.identifier isEqualToString:@"showDetails"])
{
Expand Down Expand Up @@ -1513,6 +1513,17 @@ -(void) dismissRecursorWithViewControllers:(NSMutableArray*) viewControllers ani
}
}

-(chatViewController* _Nullable) currentChatView
{
NSArray* controllers = ((UINavigationController*)self.splitViewController.viewControllers[0]).viewControllers;
chatViewController* chatView = nil;
if(controllers.count > 1)
chatView = [((UINavigationController*)controllers[1]).viewControllers firstObject];
if(![chatView isKindOfClass:NSClassFromString(@"chatViewController")])
chatView = nil;
return chatView;
}

-(void) scrollToContact:(MLContact*) contact
{
__block NSIndexPath* indexPath = nil;
Expand Down
4 changes: 4 additions & 0 deletions Monal/Classes/HelperTools.m
Original file line number Diff line number Diff line change
Expand Up @@ -2352,8 +2352,12 @@ +(NSString*) encodeRandomResource
u_int32_t i=arc4random();
#if TARGET_OS_MACCATALYST
NSString* resource = [NSString stringWithFormat:@"Monal-macOS.%@", [self hexadecimalString:[NSData dataWithBytes: &i length: sizeof(i)]]];
#else
#if IS_QUICKSY
NSString* resource = [NSString stringWithFormat:@"Quicksy-iOS.%@", [self hexadecimalString:[NSData dataWithBytes: &i length: sizeof(i)]]];
#else
NSString* resource = [NSString stringWithFormat:@"Monal-iOS.%@", [self hexadecimalString:[NSData dataWithBytes: &i length: sizeof(i)]]];
#endif
#endif
return resource;
}
Expand Down
27 changes: 25 additions & 2 deletions Monal/Classes/MLDelayableTimer.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ -(void) start
return;
}
DDLogDebug(@"Starting timer: %@", self);
[[HelperTools getExtraRunloopWithIdentifier:MLRunLoopIdentifierTimer] addTimer:_wrappedTimer forMode:NSRunLoopCommonModes];
//scheduling and unscheduling of a timer must be done from the same thread --> use our runloop
[self scheduleBlockInRunLoop:^{
[[HelperTools getExtraRunloopWithIdentifier:MLRunLoopIdentifierTimer] addTimer:self->_wrappedTimer forMode:NSRunLoopCommonModes];
}];
}
}

Expand Down Expand Up @@ -131,8 +134,28 @@ -(void) invalidate
return;
}
//DDLogVerbose(@"Invalidating timer: %@", self);
[_wrappedTimer invalidate];
//scheduling and unscheduling of a timer must be done from the same thread --> use our runloop
[self scheduleBlockInRunLoop:^{
[self->_wrappedTimer invalidate];
}];
}
}

-(void) scheduleBlockInRunLoop:(monal_void_block_t) block
{
NSRunLoop* runLoop = [HelperTools getExtraRunloopWithIdentifier:MLRunLoopIdentifierTimer];
// NSCondition* condition = [NSCondition new];
// [condition lock];
CFRunLoopPerformBlock([runLoop getCFRunLoop], (__bridge CFStringRef)NSDefaultRunLoopMode, ^{
block();
// [condition lock];
// [condition signal];
// [condition unlock];
});
CFRunLoopWakeUp([runLoop getCFRunLoop]); //trigger wakeup of runloop to execute the block as soon as possible
// //wait for our block to finish executing
// [condition wait];
// [condition unlock];
}

@end
12 changes: 6 additions & 6 deletions Monal/Classes/MLStream.m
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ @interface MLInputStream()
//(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)
dispatch_semaphore_t _read_sem;
}
@property (atomic, readonly) void (^incoming_data_handler)(NSData* _Nullable, BOOL, NSError* _Nullable, BOOL allow_next_read);
@property (atomic, readonly) void (^incoming_data_handler)(NSData* _Nullable, BOOL, NSError* _Nullable, BOOL);
@end

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

//try to read again
if(!is_complete && !generate_bytes_available_event && allow_next_read)
if(!is_complete && !generate_error_event && !generate_bytes_available_event && polling_active)
[self schedule_read];
};
return self;
Expand Down Expand Up @@ -235,8 +235,8 @@ -(void) schedule_read
DDLogDebug(@"now calling nw_framer_parse_input inside framer queue");
nw_framer_parse_input(self.shared_state.framer, 1, BUFFER_SIZE, nil, ^size_t(uint8_t* buffer, size_t buffer_length, bool is_complete) {
DDLogDebug(@"nw_framer_parse_input got callback with is_complete:%@, length=%zu", bool2str(is_complete), (unsigned long)buffer_length);
//we only want to allow new calls to schedule_read if we received some data --> set last arg accordingly
self.incoming_data_handler([NSData dataWithBytes:buffer length:buffer_length], is_complete, nil, buffer_length > 0);
//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
self.incoming_data_handler([NSData dataWithBytes:buffer length:buffer_length], is_complete, nil, NO);
return buffer_length;
});
});
Expand All @@ -249,7 +249,7 @@ -(void) schedule_read
NSError* st_error = nil;
if(receive_error)
st_error = (NSError*)CFBridgingRelease(nw_error_copy_cf_error(receive_error));
//we always want to allow new calls to schedule_read --> set last arg to YES
//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)
self.incoming_data_handler((NSData*)content, is_complete, st_error, YES);
});
}
Expand Down
8 changes: 4 additions & 4 deletions Monal/Classes/MonalAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -1972,10 +1972,10 @@ -(void) sendAllOutboxes
monal_id_block_t cleanup = ^(NSDictionary* payload) {
[[DataLayer sharedInstance] deleteShareSheetPayloadWithId:payload[@"id"]];
[[MLNotificationQueue currentQueue] postNotificationName:kMonalRefresh object:nil userInfo:nil];
if(self.activeChats.currentChatViewController != nil)
if(self.activeChats.currentChatView != nil)
{
[self.activeChats.currentChatViewController scrollToBottomAnimated:NO];
[self.activeChats.currentChatViewController hideUploadHUD];
[self.activeChats.currentChatView scrollToBottomAnimated:NO];
[self.activeChats.currentChatView hideUploadHUD];
}
//send next item (if there is one left)
[self sendAllOutboxes];
Expand Down Expand Up @@ -2007,7 +2007,7 @@ -(void) sendAllOutboxes
else if([payload[@"type"] isEqualToString:@"image"] || [payload[@"type"] isEqualToString:@"file"] || [payload[@"type"] isEqualToString:@"contact"] || [payload[@"type"] isEqualToString:@"audiovisual"])
{
DDLogInfo(@"Got %@ upload: %@", payload[@"type"], payload[@"data"]);
[self.activeChats.currentChatViewController showUploadHUD];
[self.activeChats.currentChatView showUploadHUD];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
$call(payload[@"data"], $ID(account), $BOOL(encrypted), $ID(completion, (^(NSString* url, NSString* mimeType, NSNumber* size, NSError* error) {
dispatch_async(dispatch_get_main_queue(), ^{
Expand Down
3 changes: 2 additions & 1 deletion Monal/Classes/chatViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -3560,7 +3560,8 @@ -(NSInteger)collectionView:(nonnull UICollectionView*) collectionView numberOfIt

-(void) notifyUploadQueueRemoval:(NSUInteger) index
{
MLAssert(index < self.uploadQueue.count, @"index is only allowed to be smaller than uploadQueue.count");
if(index >= self.uploadQueue.count)
return;
[self.uploadMenuView performBatchUpdates:^{
[self deleteQueueItemAtIndex:index];
} completion:^(BOOL finished) {
Expand Down
133 changes: 72 additions & 61 deletions Monal/Classes/xmpp.m
Original file line number Diff line number Diff line change
Expand Up @@ -829,19 +829,21 @@ -(void) unfreeze
//this operation has highest priority to make sure it will be executed first once unfrozen
NSBlockOperation* unfreezeOperation = [NSBlockOperation blockOperationWithBlock:^{
//this has to be the very first thing even before unfreezing the parse or send queues
if(self.accountState < kStateReconnecting)
{
DDLogInfo(@"Reloading UNfrozen account %@", self.accountNo);
//(re)read persisted state (could be changed by appex)
[self readState];
@synchronized(self->_stateLockObject) {
if(self.accountState < kStateReconnecting)
{
DDLogInfo(@"Reloading UNfrozen account %@", self.accountNo);
//(re)read persisted state (could be changed by appex)
[self readState];
}
else
DDLogInfo(@"Not reloading UNfrozen account %@, already connected", self.accountNo);

//this must be inside the dispatch async, because it will dispatch *SYNC* to the receive queue and potentially block or even deadlock the system
[self unfreezeParseQueue];

[self unfreezeSendQueue];
}
else
DDLogInfo(@"Not reloading UNfrozen account %@, already connected", self.accountNo);

//this must be inside the dispatch async, because it will dispatch *SYNC* to the receive queue and potentially block or even deadlock the system
[self unfreezeParseQueue];

[self unfreezeSendQueue];
}];
unfreezeOperation.queuePriority = NSOperationQueuePriorityVeryHigh; //make sure this will become the first operation executed once unfrozen
[self->_receiveQueue addOperations: @[unfreezeOperation] waitUntilFinished:NO];
Expand Down Expand Up @@ -962,11 +964,67 @@ -(void) disconnectWithStreamError:(MLXMLNode* _Nullable) streamError
-(void) disconnectWithStreamError:(MLXMLNode* _Nullable) streamError andExplicitLogout:(BOOL) explicitLogout
{
DDLogInfo(@"disconnect called...");
//commonly used by shortcut outside of receive queue and called from inside the receive queue, too
monal_void_block_t doExplicitLogout = ^{
@synchronized(self->_stateLockObject) {
DDLogVerbose(@"explicitLogout == YES --> clearing state");

//preserve unAckedStanzas even on explicitLogout and resend them on next connect
//if we don't do this, messages could get lost when logging out directly after sending them
//and: sending messages twice is less intrusive than silently loosing them
NSMutableArray* stanzas = self.unAckedStanzas;

//reset smacks state to sane values (this can be done even if smacks is not supported)
[self initSM3];
self.unAckedStanzas = stanzas;

//inform all old iq handlers of invalidation and clear _iqHandlers dictionary afterwards
@synchronized(self->_iqHandlers) {
for(NSString* iqid in [self->_iqHandlers allKeys])
{
DDLogWarn(@"Invalidating iq handler for iq id '%@'", iqid);
if(self->_iqHandlers[iqid][@"handler"] != nil)
$invalidate(self->_iqHandlers[iqid][@"handler"], $ID(account, self), $ID(reason, @"disconnect"));
else if(self->_iqHandlers[iqid][@"errorHandler"])
((monal_iq_handler_t)self->_iqHandlers[iqid][@"errorHandler"])(nil);
}
self->_iqHandlers = [NSMutableDictionary new];
}

//invalidate pubsub queue (*after* iq handlers that also might invalidate a result handler of the queued operation)
[self.pubsub invalidateQueue];

//clear pipeline cache
self->_pipeliningState = kPipelinedNothing;
self->_cachedStreamFeaturesBeforeAuth = nil;
self->_cachedStreamFeaturesAfterAuth = nil;

//clear all reconnection handlers
@synchronized(self->_reconnectionHandlers) {
[self->_reconnectionHandlers removeAllObjects];
}

//persist these changes
[self persistState];
}

[[DataLayer sharedInstance] resetContactsForAccount:self.accountNo];

//trigger view updates to make sure enabled/disabled account state propagates to all ui elements
[[MLNotificationQueue currentQueue] postNotificationName:kMonalRefresh object:nil userInfo:nil];
};

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

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

Expand Down Expand Up @@ -996,54 +1054,7 @@ -(void) disconnectWithStreamError:(MLXMLNode* _Nullable) streamError andExplicit
{
DDLogVerbose(@"not doing logout because already logged out, but clearing state if explicitLogout was yes");
if(explicitLogout)
{
@synchronized(self->_stateLockObject) {
DDLogVerbose(@"explicitLogout == YES --> clearing state");

//preserve unAckedStanzas even on explicitLogout and resend them on next connect
//if we don't do this, messages could get lost when logging out directly after sending them
//and: sending messages twice is less intrusive than silently loosing them
NSMutableArray* stanzas = self.unAckedStanzas;

//reset smacks state to sane values (this can be done even if smacks is not supported)
[self initSM3];
self.unAckedStanzas = stanzas;

//inform all old iq handlers of invalidation and clear _iqHandlers dictionary afterwards
@synchronized(self->_iqHandlers) {
for(NSString* iqid in [self->_iqHandlers allKeys])
{
DDLogWarn(@"Invalidating iq handler for iq id '%@'", iqid);
if(self->_iqHandlers[iqid][@"handler"] != nil)
$invalidate(self->_iqHandlers[iqid][@"handler"], $ID(account, self), $ID(reason, @"disconnect"));
else if(self->_iqHandlers[iqid][@"errorHandler"])
((monal_iq_handler_t)self->_iqHandlers[iqid][@"errorHandler"])(nil);
}
self->_iqHandlers = [NSMutableDictionary new];
}

//invalidate pubsub queue (*after* iq handlers that also might invalidate a result handler of the queued operation)
[self.pubsub invalidateQueue];

//clear pipeline cache
self->_pipeliningState = kPipelinedNothing;
self->_cachedStreamFeaturesBeforeAuth = nil;
self->_cachedStreamFeaturesAfterAuth = nil;

//clear all reconnection handlers
@synchronized(self->_reconnectionHandlers) {
[self->_reconnectionHandlers removeAllObjects];
}

//persist these changes
[self persistState];
}

[[DataLayer sharedInstance] resetContactsForAccount:self.accountNo];

//trigger view updates to make sure enabled/disabled account state propagates to all ui elements
[[MLNotificationQueue currentQueue] postNotificationName:kMonalRefresh object:nil userInfo:nil];
}
doExplicitLogout();
return;
}
DDLogInfo(@"disconnecting");
Expand Down