Skip to content

Commit

Permalink
6.4.8 (#1330)
Browse files Browse the repository at this point in the history
- Make sure to not enable accounts without password after being restored
via iCloud backup
- Improve OMEMO handling when offline
- Fix more background crashes
- Workaround for broken stun/turn on ejabberd < 24.12
- Christmas/winter special
  • Loading branch information
tmolitor-stud-tu authored Dec 22, 2024
2 parents 6544999 + 6b279ea commit dc7034c
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 59 deletions.
81 changes: 61 additions & 20 deletions Monal/Classes/HelperTools.m
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,23 @@ @interface MLDelayableTimer()
-(void) invalidate;
@end

//make method visible
@interface DDLog()
-(void) queueLogMessage:(DDLogMessage*) logMessage asynchronously:(BOOL) asyncFlag;
@end

@interface DDLog (AllowQueueFreeze)
-(void) swizzled_queueLogMessage:(DDLogMessage*) logMessage asynchronously:(BOOL) asyncFlag;
@end

static char* _crashBundleName = "UnifiedReport";
static NSString* _processID;
static DDFileLogger* _fileLogger = nil;
static char _origLogfilePath[1024] = "";
static char _logfilePath[1024] = "";
static NSObject* _isAppExtensionLock = nil;
static NSObject* _suspensionHandlingLock = nil;
static BOOL _suspensionHandlingIsSuspended = NO;
static NSObject* _suspensionHandling_lock = nil;
static BOOL _suspensionHandling_isSuspended = NO;
static NSMutableDictionary* _versionInfoCache;
static MLStreamRedirect* _stdoutRedirector = nil;
static MLStreamRedirect* _stderrRedirector = nil;
Expand Down Expand Up @@ -293,12 +302,36 @@ -(id) initWithObj:(id) obj
}
@end

@implementation DDLog (AllowQueueFreeze)

-(void) swizzled_queueLogMessage:(DDLogMessage*) logMessage asynchronously:(BOOL) asyncFlag
{
//don't do sync logging for any message (usually ERROR), while the global logging queue is suspended
@synchronized(_suspensionHandling_lock) {
return [self swizzled_queueLogMessage:logMessage asynchronously:_suspensionHandling_isSuspended ? YES : asyncFlag];
}
}

//see https://stackoverflow.com/a/13326633 and https://fek.io/blog/method-swizzling-in-obj-c-and-swift/
+(void) load
{
if(self == DDLog.self)
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
swizzle([self class], @selector(queueLogMessage:asynchronously:), @selector(swizzled_queueLogMessage:asynchronously:));
});
}
}

@end

@implementation HelperTools

+(void) initialize
{
_suspensionHandlingLock = [NSObject new];
_suspensionHandlingIsSuspended = NO;
_suspensionHandling_lock = [NSObject new];
_suspensionHandling_isSuspended = NO;
_isAppExtensionLock = [NSObject new];
_versionInfoCache = [NSMutableDictionary new];

Expand Down Expand Up @@ -586,13 +619,13 @@ +(NSRunLoop*) getExtraRunloopWithIdentifier:(MLRunLoopIdentifier) identifier
runloops = [NSMutableDictionary new];
});

//every identifier has its own thread priority/qos class
__block dispatch_queue_priority_t priority;
//every identifier has its own thread qos class
__block NSQualityOfService qos;
__block char* name;
switch(identifier)
{
case MLRunLoopIdentifierNetwork: priority = DISPATCH_QUEUE_PRIORITY_BACKGROUND; name = "im.monal.runloop.networking"; break;
case MLRunLoopIdentifierTimer: priority = DISPATCH_QUEUE_PRIORITY_BACKGROUND; name = "im.monal.runloop.timer"; break;
case MLRunLoopIdentifierNetwork: qos = NSQualityOfServiceBackground; name = "im.monal.runloop.networking"; break;
case MLRunLoopIdentifierTimer: qos = NSQualityOfServiceBackground; name = "im.monal.runloop.timer"; break;
default: unreachable(@"unknown runloop identifier!");
}

Expand All @@ -601,9 +634,7 @@ +(NSRunLoop*) getExtraRunloopWithIdentifier:(MLRunLoopIdentifier) identifier
{
NSCondition* condition = [NSCondition new];
[condition lock];
dispatch_async(dispatch_queue_create_with_target(name, DISPATCH_QUEUE_SERIAL, dispatch_get_global_queue(priority, 0)), ^{
//set thread name, too (not only runloop name)
[NSThread.currentThread setName:[NSString stringWithFormat:@"%s", name]];
NSThread* newRunloopThread = [[NSThread alloc] initWithBlock:^{
//we don't need an @synchronized block around this because the @synchronized block of the outer thread
//waits until we signal our condition (e.g. no other thread can race with us)
NSRunLoop* localLoop = runloops[@(identifier)] = [NSRunLoop currentRunLoop];
Expand All @@ -615,7 +646,13 @@ +(NSRunLoop*) getExtraRunloopWithIdentifier:(MLRunLoopIdentifier) identifier
[localLoop run];
usleep(10000); //sleep 10ms if we ever return from our runloop to not consume too much cpu
}
});
}];
//configure and start thread
[newRunloopThread setName:[NSString stringWithFormat:@"%s", name]];
//newRunloopThread.threadPriority = 1.0;
newRunloopThread.qualityOfService = qos;
[newRunloopThread start];
//wait for the new thread to create a new runloop, it will immediately spin the runloop after signalling this condition
[condition wait];
[condition unlock];
}
Expand Down Expand Up @@ -1880,27 +1917,31 @@ +(void) flushLogsWithTimeout:(double) timeout

+(void) signalSuspension
{
@synchronized(_suspensionHandlingLock) {
if(!_suspensionHandlingIsSuspended)
@synchronized(_suspensionHandling_lock) {
if(!_suspensionHandling_isSuspended)
{
DDLogVerbose(@"Suspending logger queue...");
[HelperTools flushLogsWithTimeout:0.100];
dispatch_suspend([DDLog loggingQueue]);
_suspensionHandlingIsSuspended = YES;
_suspensionHandling_isSuspended = YES;

DDLogVerbose(@"Posting kMonalFrozen notification now...");
[[NSNotificationCenter defaultCenter] postNotificationName:kMonalFrozen object:nil];
}
}
DDLogVerbose(@"Posting kMonalIsFreezed notification now...");
[[NSNotificationCenter defaultCenter] postNotificationName:kMonalIsFreezed object:nil];
}

+(void) signalResumption
{
@synchronized(_suspensionHandlingLock) {
if(_suspensionHandlingIsSuspended)
@synchronized(_suspensionHandling_lock) {
if(_suspensionHandling_isSuspended)
{
DDLogVerbose(@"Resuming logger queue...");
dispatch_resume([DDLog loggingQueue]);
_suspensionHandlingIsSuspended = NO;
_suspensionHandling_isSuspended = NO;

DDLogVerbose(@"Posting kMonalUnfrozen notification now...");
[[NSNotificationCenter defaultCenter] postNotificationName:kMonalUnfrozen object:nil];
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion Monal/Classes/MLConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ static inline NSString* _Nonnull LocalizationNotNeeded(NSString* _Nonnull s)
#define kMonalIncomingSDP @"kMonalIncomingSDP"
#define kMonalIncomingICECandidate @"kMonalIncomingICECandidate"
#define kMonalWillBeFreezed @"kMonalWillBeFreezed"
#define kMonalIsFreezed @"kMonalIsFreezed"
#define kMonalFrozen @"kMonalFrozen"
#define kMonalUnfrozen @"kMonalUnfrozen"
#define kMonalNewMessageNotice @"kMonalNewMessageNotice"
#define kMonalMucSubjectChanged @"kMonalMucSubjectChanged"
#define kMonalDeletedMessageNotice @"kMonalDeletedMessageNotice"
Expand Down
9 changes: 5 additions & 4 deletions Monal/Classes/MLOMEMO.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

NS_ASSUME_NONNULL_BEGIN

static const size_t MIN_OMEMO_KEYS = 25;
static const size_t MIN_OMEMO_KEYS = 90;
static const size_t MAX_OMEMO_KEYS = 100;
static const int KEY_SIZE = 16;

Expand Down Expand Up @@ -736,7 +736,7 @@ -(void) processOMEMOKeys:(MLXMLNode*) item forJid:(NSString*) jid andRid:(NSNumb
uint32_t deviceId = (uint32_t)rid.unsignedIntValue;
SignalAddress* address = [[SignalAddress alloc] initWithName:jid deviceId:deviceId];
SignalSessionBuilder* builder = [[SignalSessionBuilder alloc] initWithAddress:address context:self.signalContext];
NSArray<NSNumber*>* preKeyIds = [bundle find:@"prekeys/preKeyPublic@preKeyId|uint"];
NSMutableArray<NSNumber*>* preKeyIds = [[bundle find:@"prekeys/preKeyPublic@preKeyId|uint"] mutableCopy];

if(preKeyIds == nil || preKeyIds.count == 0)
{
Expand All @@ -750,8 +750,9 @@ -(void) processOMEMOKeys:(MLXMLNode*) item forJid:(NSString*) jid andRid:(NSNumb
{
// select random preKey and try to import it
const uint32_t preKeyIdxToTest = arc4random_uniform((uint32_t)preKeyIds.count);
// load preKey
NSNumber* preKeyId = preKeyIds[preKeyIdxToTest];
[preKeyIds removeObjectAtIndex:preKeyIdxToTest];
processedKeys++;
if(preKeyId == nil)
continue;;
NSData* key = [bundle findFirst:@"prekeys/preKeyPublic<preKeyId=%@>#|base64", preKeyId];
Expand Down Expand Up @@ -790,7 +791,7 @@ -(void) processOMEMOKeys:(MLXMLNode*) item forJid:(NSString*) jid andRid:(NSNumb
[self notifyKnownDevicesUpdated:jid];

return;
} while(++processedKeys < preKeyIds.count);
} while(preKeyIds.count > 0);
DDLogError(@"Could not import a single prekey from bundle for rid %@ (tried %lu keys)", rid, processedKeys);
//TODO: should we blacklist this device id?
@synchronized(self.state.queuedSessionRepairs) {
Expand Down
2 changes: 1 addition & 1 deletion Monal/Classes/MLProcessLock.m
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ +(void) lock
lock = flock(_ownLockFD, LOCK_EX | LOCK_NB);
if(lock != 0)
@throw [NSException exceptionWithName:@"LockingError" reason:[NSString stringWithFormat:@"flock returned: %d (%d) on file: %s", lock, errno, _ownLockPath] userInfo:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(unlock) name:kMonalIsFreezed object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(unlock) name:kMonalFrozen object:nil];
}
}

Expand Down
12 changes: 6 additions & 6 deletions Monal/Classes/MonalAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -1595,9 +1595,6 @@ -(void) handleBackgroundProcessingTask:(BGTask*) task
}];
};

//resume logging and other core tasks
[HelperTools signalResumption];

//only proceed with our BGTASK if the NotificationServiceExtension is not running
[MLProcessLock lock];
[[IPC sharedInstance] sendMessage:@"Monal.disconnectAll" withData:nil to:@"NotificationServiceExtension"];
Expand Down Expand Up @@ -1702,9 +1699,6 @@ -(void) handleBackgroundRefreshingTask:(BGTask*) task
}];
};

//resume logging and other core tasks
[HelperTools signalResumption];

//only proceed with our BGTASK if the NotificationServiceExtension is not running
[MLProcessLock lock];
[[IPC sharedInstance] sendMessage:@"Monal.disconnectAll" withData:nil to:@"NotificationServiceExtension"];
Expand Down Expand Up @@ -1748,6 +1742,9 @@ -(void) handleBackgroundRefreshingTask:(BGTask*) task
-(void) configureBackgroundTasks
{
[[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:kBackgroundProcessingTask usingQueue:dispatch_get_main_queue() launchHandler:^(BGTask *task) {
//resume logging and other core tasks
[HelperTools signalResumption];

DDLogDebug(@"RUNNING BGPROCESSING LAUNCH HANDLER");
DDLogInfo(@"BG time available: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
if(![HelperTools isInBackground])
Expand All @@ -1768,6 +1765,9 @@ -(void) configureBackgroundTasks
}];

[[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:kBackgroundRefreshingTask usingQueue:dispatch_get_main_queue() launchHandler:^(BGTask *task) {
//resume logging and other core tasks
[HelperTools signalResumption];

DDLogDebug(@"RUNNING BGREFRESHING LAUNCH HANDLER");
DDLogInfo(@"BG time available: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
if(![HelperTools isInBackground])
Expand Down
40 changes: 22 additions & 18 deletions Monal/Classes/PasswordMigration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,23 @@ struct PasswordMigration: View {
DDLogInfo("Migration needed: \(String(describing:self.needingMigration))")
}

func updatePassword(_ password: String, for id: Int) {
self.needingMigration[id]?["password"] = password as NSString
if(password.count > 0) {
//first change? --> activate account and use "needs_password_migration" to record
//the fact that we just activated this account automatically
//(making the password field empty will reset this)
if((self.needingMigration[id]?["needs_password_migration"] as! NSNumber).boolValue) {
self.needingMigration[id]?["enabled"] = NSNumber(value:true)
self.needingMigration[id]?["needs_password_migration"] = NSNumber(value:false)
}
//reset our "account automatically activated" flag and deactivate our account
} else {
self.needingMigration[id]?["enabled"] = NSNumber(value:false)
self.needingMigration[id]?["needs_password_migration"] = NSNumber(value:true)
}
}

var body: some View {
//ScrollView {
VStack {
Expand Down Expand Up @@ -61,26 +78,11 @@ struct PasswordMigration: View {

SecureField(NSLocalizedString("Password", comment: "placeholder when migrating account"), text:Binding(
get: { self.needingMigration[id]?["password"] as? String ?? "" },
set: {
self.needingMigration[id]?["password"] = $0 as NSString
if($0.count > 0) {
//first change? --> activate account and use "needs_password_migration" to record
//the fact that we just activated this account automatically
//(making the password field empty will reset this)
if((self.needingMigration[id]?["needs_password_migration"] as! NSNumber).boolValue) {
self.needingMigration[id]?["enabled"] = NSNumber(value:true)
self.needingMigration[id]?["needs_password_migration"] = NSNumber(value:false)
}
//reset our "account automatically activated" flag and deactivate our account
} else {
self.needingMigration[id]?["enabled"] = NSNumber(value:false)
self.needingMigration[id]?["needs_password_migration"] = NSNumber(value:true)
}
}
set: { updatePassword($0, for:id) }
))
.addClearButton(isEditing: true, text:Binding(
get: { self.needingMigration[id]?["password"] as? String ?? "" },
set: { self.needingMigration[id]?["password"] = $0 as NSString }
set: { updatePassword($0, for:id) }
))
}
}
Expand All @@ -94,7 +96,7 @@ struct PasswordMigration: View {
ToolbarItem(placement: .navigationBarTrailing) {
HStack{
Button(action: {
DDLogInfo("Saving migrated accounts: \(String(describing:self.needingMigration))")
DDLogInfo("Saving migrated accounts...")
for id in self.needingMigration.keys {
var dic = self.needingMigration[id]!
//don't show this migration dialog again, even if the user did not activate this account
Expand All @@ -108,6 +110,8 @@ struct PasswordMigration: View {
MLXMPPManager.sharedInstance().connectAccount(dic["account_id"] as! NSNumber)
}
} else {
//make sure to never enable accounts without password
dic["enabled"] = NSNumber(value:false)
DDLogDebug("Updating account in DB: enabled=\(String(describing:dic["enabled"])), needs_password_migration=\(String(describing:dic["needs_password_migration"])), password.count=0")
DataLayer.sharedInstance().updateAccoun(with:dic)
}
Expand Down
12 changes: 7 additions & 5 deletions Monal/Classes/xmpp.m
Original file line number Diff line number Diff line change
Expand Up @@ -1380,18 +1380,19 @@ -(void) prepareXMPPParser
DDLogWarn(@"Throwing away incoming stanza queued in parse queue, accountState < kStateConnected");
return;
}
NSString* loggedStanza = [NSString stringWithFormat:@"%@: %@", parsedStanza.element, nilDefault([parsedStanza findFirst:@"/@id"], parsedStanza)];
[MLNotificationQueue queueNotificationsInBlock:^{
//add whole processing of incoming stanzas to one big transaction
//this will make it impossible to leave inconsistent database entries on app crashes or iphone crashes/reboots
DDLogVerbose(@"Starting transaction for: %@", parsedStanza);
DDLogVerbose(@"Starting transaction for: %@", loggedStanza);
[[DataLayer sharedInstance] createTransaction:^{
DDLogVerbose(@"Started transaction for: %@", parsedStanza);
DDLogVerbose(@"Started transaction for: %@", loggedStanza);
//don't write data to our tcp stream while inside this db transaction (all effects to the outside world should be transactional, too)
[self freezeSendQueue];
[self processInput:parsedStanza withDelayedReplay:NO];
DDLogVerbose(@"Ending transaction for: %@", parsedStanza);
DDLogVerbose(@"Ending transaction for: %@", loggedStanza);
}];
DDLogVerbose(@"Ended transaction for: %@", parsedStanza);
DDLogVerbose(@"Ended transaction for: %@", loggedStanza);
[self unfreezeSendQueue]; //this will flush all stanzas added inside the db transaction and now waiting in the send queue
} onQueue:@"receiveQueue"];
[self persistState]; //make sure to persist all state changes triggered by the events in the notification queue
Expand Down Expand Up @@ -4011,7 +4012,8 @@ -(void) queryExternalServiceCredentialsFor:(NSDictionary*) service completion:(m
} andChildren:@[] andData:nil]
] andData:nil]];
[self sendIq:credentialsQuery withResponseHandler:^(XMPPIQ* response) {
completion([response findFirst:@"{urn:xmpp:extdisco:2}credentials/service@@"]);
//ejabberd <= 24.10 incorrectly uses {urn:xmpp:extdisco:2}services/service, support this as fallback
completion(nilDefault([response findFirst:@"{urn:xmpp:extdisco:2}credentials/service@@"], [response findFirst:@"{urn:xmpp:extdisco:2}services/service@@"]));
} andErrorHandler:^(XMPPIQ* error) {
DDLogWarn(@"Got error while quering for credentials of external service %@: %@", service, error);
completion(@{});
Expand Down
Binary file modified Monal/Images.xcassets/AppIcon.appiconset/Monal-ios_1024.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Monal/Images.xcassets/AppIcon.appiconset/Monal-macos-1024.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Monal/Images.xcassets/AppIcon.appiconset/Monal-macos-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Monal/Images.xcassets/AppLogo.imageset/Monal-ios_1024.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit dc7034c

Please sign in to comment.