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.9-rc1 #1357

Merged
merged 19 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
684e4e5
Revert "Use christmas logo"
tmolitor-stud-tu Dec 31, 2024
d798d54
Don't pile up already fired ping request delaying timers
tmolitor-stud-tu Dec 24, 2024
78f221b
Add more logging to xmpp.m
tmolitor-stud-tu Dec 24, 2024
3dbfe20
Fix weird apple foo creating a second (unusable) framer
tmolitor-stud-tu Dec 24, 2024
9dad64e
Fix KVOObserver usage in ContactEntry
tmolitor-stud-tu Dec 27, 2024
3f5e630
Fix crash on LMC replace without id (spec violation!)
tmolitor-stud-tu Dec 31, 2024
e38e8cf
Send out log messages via UDP even if global logging queue is suspended
tmolitor-stud-tu Dec 29, 2024
ab22e7c
Fix weird double shutdown bug
tmolitor-stud-tu Dec 29, 2024
63f5691
Request background task before disconnecting
tmolitor-stud-tu Dec 29, 2024
be14aa0
Ping mucs on open to make sure we are still joined
tmolitor-stud-tu Dec 29, 2024
cf9544a
Bump all rust dependencies
tmolitor-stud-tu Dec 31, 2024
5692f8c
Bump cocoapods
tmolitor-stud-tu Dec 31, 2024
38011e5
Make sure to not crash in udp logger
tmolitor-stud-tu Jan 3, 2025
4cfce1e
Fix appex reporting in crash reports
tmolitor-stud-tu Jan 3, 2025
f20e28f
Make gateway detection more reliable when generating a room address
lissine0 Jan 3, 2025
a3c09e4
Update the copyright notice to 2025
lissine0 Jan 2, 2025
318162b
Add new action to block subscription requests, fixes #1320
tmolitor-stud-tu Jan 4, 2025
5a0f76a
Improve UDP logging while logger queue is suspended
tmolitor-stud-tu Dec 31, 2024
6fda990
Fix ios 14 compatibility
tmolitor-stud-tu Jan 4, 2025
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
5 changes: 3 additions & 2 deletions Monal/Classes/ContactEntry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
//

struct ContactEntry<AdditionalContent: View>: View {
let contact: ObservableKVOWrapper<MLContact>
let selfnotesPrefix: Bool
let fallback: String?
@ViewBuilder let additionalContent: () -> AdditionalContent

@StateObject var contact: ObservableKVOWrapper<MLContact>

init(contact:ObservableKVOWrapper<MLContact>, selfnotesPrefix: Bool = true, fallback: String? = nil) where AdditionalContent == EmptyView {
self.init(contact:contact, selfnotesPrefix:selfnotesPrefix, fallback:fallback, additionalContent:{ EmptyView() })
}
Expand All @@ -33,7 +34,7 @@ struct ContactEntry<AdditionalContent: View>: View {
}

init(contact:ObservableKVOWrapper<MLContact>, selfnotesPrefix: Bool, fallback: String?, @ViewBuilder additionalContent: @escaping () -> AdditionalContent) {
self.contact = contact
_contact = StateObject(wrappedValue: contact)
self.selfnotesPrefix = selfnotesPrefix
self.fallback = fallback
self.additionalContent = additionalContent
Expand Down
11 changes: 11 additions & 0 deletions Monal/Classes/ContactRequestsMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ struct ContactRequestsMenuEntry: View {
//see https://www.hackingwithswift.com/forums/swiftui/tap-button-in-hstack-activates-all-button-actions-ios-14-swiftui-2/2952
.buttonStyle(BorderlessButtonStyle())

Button {
// deny request
MLXMPPManager.sharedInstance().remove(contact)
MLXMPPManager.sharedInstance().block(true, contact:contact)
} label: {
Image(systemName: "xmark.circle")
.accentColor(.red)
}
//see https://www.hackingwithswift.com/forums/swiftui/tap-button-in-hstack-activates-all-button-actions-ios-14-swiftui-2/2952
.buttonStyle(BorderlessButtonStyle())

Button {
// deny request
MLXMPPManager.sharedInstance().remove(contact)
Expand Down
5 changes: 5 additions & 0 deletions Monal/Classes/HelperTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ void swizzle(Class c, SEL orig, SEL new);
-(id) initWithObj:(id) obj;
@end

@interface DDLogMessage(TaggedMessage)
@property (nonatomic) BOOL ml_isDirect;
@end

@interface HelperTools : NSObject

@property (class, nonatomic, strong, nullable) DDFileLogger* fileLogger;
Expand All @@ -78,6 +82,7 @@ void swizzle(Class c, SEL orig, SEL new);
+(void) installExceptionHandler;
+(int) pendingCrashreportCount;
+(void) flushLogsWithTimeout:(double) timeout;
+(BOOL) isAppSuspended;
+(void) signalSuspension;
+(void) signalResumption;
+(void) __attribute__((noreturn)) MLAssertWithText:(NSString*) text andUserData:(id _Nullable) additionalData andFile:(const char* const) file andLine:(int) line andFunc:(const char* const) func;
Expand Down
95 changes: 69 additions & 26 deletions Monal/Classes/HelperTools.m
Original file line number Diff line number Diff line change
Expand Up @@ -302,14 +302,45 @@ -(id) initWithObj:(id) obj
}
@end

@implementation DDLogMessage(TaggedMessage)
@dynamic ml_isDirect;
-(void) setMl_isDirect:(BOOL) value
{
objc_setAssociatedObject(self, @selector(ml_isDirect), @(value), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(BOOL) ml_isDirect
{
return ((NSNumber*)objc_getAssociatedObject(self, @selector(ml_isDirect))).boolValue;
}
@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];
//make sure this method remains performant even when checking for udp logging presence
static BOOL udpLoggerEnabled = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
udpLoggerEnabled = [[HelperTools defaultsDB] boolForKey:@"udpLoggerEnabled"];
});

//use udp logger to log all messages, even if the loggging queue is in suspended state
//this hopefully enables us to catch strange bugs sometimes hanging and then watchdog-killing the app when resuming from resumption
if(udpLoggerEnabled && _suspensionHandling_isSuspended)
{
//this marks a message as already directly logged to allow the udp logger to later ignore the queued log request for the same message
logMessage.ml_isDirect = YES;
//make sure all udp log messages are still logged chronologically and prevent race conditions with our static counter var
dispatch_async([MLUDPLogger getCurrentInstance].loggerQueue, ^{
[MLUDPLogger directlyWriteLogMessage:logMessage];
});
}

//don't do sync logging for any message (usually ERROR), while the global logging queue is suspended
//don't use _suspensionHandling_lock here because that can introduce deadlocks
//(for example if we have log statements in our MLLogFileManager code rotating the logfile and creating a new one)
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/
Expand Down Expand Up @@ -1867,32 +1898,37 @@ +(NSData* _Nullable) convertLogmessageToJsonData:(DDLogMessage*) logMessage coun

//construct json dictionary
(*counter)++;
NSDictionary* representedObject = @{
@"queueThreadLabel": [self getQueueThreadLabelFor:logMessage],
NSDictionary* tag = @{
@"queueThreadLabel": nilWrapper([self getQueueThreadLabelFor:logMessage]),
@"processType": [self isAppExtension] ? @"appex" : @"mainapp",
@"processName": [[[NSBundle mainBundle] executablePath] lastPathComponent],
@"counter": [NSNumber numberWithUnsignedLongLong:*counter],
@"processID": _processID,
@"qosName": qos2name(logMessage.qos),
@"representedObject": logMessage.representedObject ? logMessage.representedObject : [NSNull null],
@"processName": nilWrapper([[[NSBundle mainBundle] executablePath] lastPathComponent]),
@"counter": @(*counter),
@"processID": nilWrapper(_processID),
@"qosName": nilWrapper(qos2name(logMessage.qos)),
@"loggingQueueSuspended": @(_suspensionHandling_isSuspended),
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@"tag": nilWrapper(logMessage.tag),
#pragma clang diagnostic pop
};
NSDictionary* msgDict = @{
@"messageFormat": logMessage.messageFormat,
@"message": logMessage.message,
@"level": [NSNumber numberWithInteger:logMessage.level],
@"flag": [NSNumber numberWithInteger:logMessage.flag],
@"context": [NSNumber numberWithInteger:logMessage.context],
@"file": logMessage.file,
@"fileName": logMessage.fileName,
@"function": logMessage.function,
@"line": [NSNumber numberWithInteger:logMessage.line],
@"tag": representedObject,
@"options": [NSNumber numberWithInteger:logMessage.options],
@"messageFormat": nilWrapper(logMessage.messageFormat),
@"message": nilWrapper(logMessage.message),
@"level": @(logMessage.level),
@"flag": @(logMessage.flag),
@"context": @(logMessage.context),
@"file": nilWrapper(logMessage.file),
@"fileName": nilWrapper(logMessage.fileName),
@"function": nilWrapper(logMessage.function),
@"line": @(logMessage.line),
@"representedObject": nilWrapper(logMessage.representedObject),
@"tag": nilWrapper(tag),
@"options": @(logMessage.options),
@"timestamp": [dateFormatter stringFromDate:logMessage.timestamp],
@"threadID": logMessage.threadID,
@"threadName": logMessage.threadName,
@"queueLabel": logMessage.queueLabel,
@"qos": [NSNumber numberWithInteger:logMessage.qos],
@"threadID": nilWrapper(logMessage.threadID),
@"threadName": nilWrapper(logMessage.threadName),
@"queueLabel": nilWrapper(logMessage.queueLabel),
@"qos": @(logMessage.qos),
};

//encode json into NSData
Expand All @@ -1915,6 +1951,13 @@ +(void) flushLogsWithTimeout:(double) timeout
[MLUDPLogger flushWithTimeout:timeout];
}

+(BOOL) isAppSuspended
{
@synchronized(_suspensionHandling_lock) {
return _suspensionHandling_isSuspended;
}
}

+(void) signalSuspension
{
@synchronized(_suspensionHandling_lock) {
Expand Down Expand Up @@ -2074,7 +2117,7 @@ +(void) installCrashHandler
handler.maxReportCount = 4;
handler.deadlockWatchdogInterval = 0; // no main thread watchdog
handler.userInfo = @{
@"isAppex": @([self isAppExtension]),
@"isAppex": bool2str([self isAppExtension]),
@"processName": [[[NSBundle mainBundle] executablePath] lastPathComponent],
@"bundleName": nilWrapper([[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]),
@"appVersion": [self appBuildVersionInfoFor:MLVersionTypeLog],
Expand Down
25 changes: 15 additions & 10 deletions Monal/Classes/MLMessageProcessor.m
Original file line number Diff line number Diff line change
Expand Up @@ -579,18 +579,23 @@ +(MLMessage* _Nullable) processMessage:(XMPPMessage*) messageNode andOuterMessag
{
NSString* messageIdToReplace = [messageNode findFirst:@"{urn:xmpp:message-correct:0}replace@id"];
DDLogVerbose(@"Message id to LMC-replace: %@", messageIdToReplace);
//this checks if this message is from the same jid as the message it tries to do the LMC for (e.g. inbound can only correct inbound and outbound only outbound)
historyId = [[DataLayer sharedInstance] getLMCHistoryIDForMessageId:messageIdToReplace from:messageNode.fromUser occupantId:occupantId participantJid:participantJid andAccount:account.accountNo];
DDLogVerbose(@"History id to LMC-replace: %@", historyId);
//now check if the LMC is allowed (we use historyIdToUse for MLhistory mam queries to only check LMC for the 3 messages coming before this ID in this converastion)
//historyIdToUse will be nil, for messages going forward in time which means (check for the newest 3 messages in this conversation)
if(historyId != nil && [[DataLayer sharedInstance] checkLMCEligible:historyId encrypted:encrypted historyBaseID:historyIdToUse])
if(messageIdToReplace == nil)
DDLogWarn(@"Ignoring LMC message not carrying a replacement id, spec vialoation!");
else
{
[[DataLayer sharedInstance] updateMessageHistory:historyId withText:body];
LMCReplaced = YES;
//this checks if this message is from the same jid as the message it tries to do the LMC for (e.g. inbound can only correct inbound and outbound only outbound)
historyId = [[DataLayer sharedInstance] getLMCHistoryIDForMessageId:messageIdToReplace from:messageNode.fromUser occupantId:occupantId participantJid:participantJid andAccount:account.accountNo];
DDLogVerbose(@"History id to LMC-replace: %@", historyId);
//now check if the LMC is allowed (we use historyIdToUse for MLhistory mam queries to only check LMC for the 3 messages coming before this ID in this converastion)
//historyIdToUse will be nil, for messages going forward in time which means (check for the newest 3 messages in this conversation)
if(historyId != nil && [[DataLayer sharedInstance] checkLMCEligible:historyId encrypted:encrypted historyBaseID:historyIdToUse])
{
[[DataLayer sharedInstance] updateMessageHistory:historyId withText:body];
LMCReplaced = YES;
}
else
historyId = nil;
}
else
historyId = nil;
}

//handle normal messages or LMC messages that can not be found
Expand Down
5 changes: 4 additions & 1 deletion Monal/Classes/MLMucProcessor.m
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,10 @@ -(NSString* _Nullable) generateMucJid
NSString* mucServer = nil;
for(NSString* jid in _account.connectionProperties.conferenceServers)
{
if([_account.connectionProperties.conferenceServers[jid] check:@"identity<type=text>"])
// Do not use gateways
if(![_account.connectionProperties.conferenceServers[jid] check:@"identity<category=gateway>"]
&& [_account.connectionProperties.conferenceServers[jid] check:@"identity<category=conference>"]
&& [_account.connectionProperties.conferenceServers[jid] check:@"identity<type=text>"])
{
mucServer = jid;
break;
Expand Down
65 changes: 27 additions & 38 deletions Monal/Classes/MLStream.m
Original file line number Diff line number Diff line change
Expand Up @@ -470,59 +470,48 @@ +(void) connectWithSNIDomain:(NSString*) SNIDomain connectHost:(NSString*) host
return YES;
});

/*
//some weird apple stuff creates the framer twice: once directly when starting the tcp handshake
//and again later after the tcp connection was established successfully --> ignore the first one
//and once a few milliseconds later, presumably after the tcp connection was established successfully
//--> ignore all but the first one
if(framerId < 1)
{
nw_framer_set_input_handler(framer, ^size_t(nw_framer_t framer) {
nw_framer_parse_input(framer, 1, BUFFER_SIZE, nil, ^size_t(uint8_t* buffer, size_t buffer_length, bool is_complete) {
MLAssert(NO, @"Unexpected incoming bytes in first framer!", (@{
@"logtag": nilWrapper(logtag),
@"framer": framer,
@"buffer": [NSData dataWithBytes:buffer length:buffer_length],
@"buffer_length": @(buffer_length),
@"is_complete": bool2str(is_complete),
}));
return buffer_length;
DDLogVerbose(@"Framer is the first one, using it...");
//we have to simulate nw_connection_state_ready because the connection state will not reflect that while our framer is active
//--> use framer start as "connection active" signal
//first framer start is allowed to directly send data which will be used as tcp early data
if(!wasOpenOnce)
{
wasOpenOnce = YES;
@synchronized(shared_state) {
shared_state.open = YES;
}
//make sure to not do this inside the framer thread to not cause any deadlocks
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[input generateEvent:NSStreamEventOpenCompleted];
[output generateEvent:NSStreamEventOpenCompleted];
});
return 0; //why that?
}

nw_framer_set_input_handler(framer, ^size_t(nw_framer_t framer) {
DDLogDebug(@"Got new input for framer: %@", framer);
[input schedule_read];
return 0; //why that??
});
nw_framer_set_output_handler(framer, ^(nw_framer_t framer, nw_framer_message_t message, size_t message_length, bool is_complete) {
MLAssert(NO, @"Unexpected outgoing bytes in first framer!", (@{
MLAssert(NO, @"Unexpected outgoing bytes in framer!", (@{
@"logtag": nilWrapper(logtag),
@"framer": framer,
@"message": message,
@"message_length": @(message_length),
@"is_complete": bool2str(is_complete),
}));
});
return nw_framer_start_result_will_mark_ready;
}
*/

//we have to simulate nw_connection_state_ready because the connection state will not reflect that while our framer is active
//--> use framer start as "connection active" signal
//first framer start is allowed to directly send data which will be used as tcp early data
if(!wasOpenOnce)
{
wasOpenOnce = YES;
@synchronized(shared_state) {
shared_state.open = YES;
}
//make sure to not do this inside the framer thread to not cause any deadlocks
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[input generateEvent:NSStreamEventOpenCompleted];
[output generateEvent:NSStreamEventOpenCompleted];
});

shared_state.framer = framer;
}
else
DDLogVerbose(@"Ignoring subsequent framer...");

nw_framer_set_input_handler(framer, ^size_t(nw_framer_t framer) {
[input schedule_read];
return 0; //why that??
});

shared_state.framer = framer;
return nw_framer_start_result_will_mark_ready;
});
DDLogInfo(@"%@: Not doing direct TLS: appending framer to protocol stack...", logtag);
Expand Down
4 changes: 4 additions & 0 deletions Monal/Classes/MLUDPLogger.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@

NS_ASSUME_NONNULL_BEGIN

FOUNDATION_EXPORT DDLoggerName const DDLoggerNameUDP NS_SWIFT_NAME(DDLoggerName.udp); // MLUDPLogger

@interface MLUDPLogger : DDAbstractLogger <DDLogger>

+(void) flushWithTimeout:(double) timeout;
+(void) directlyWriteLogMessage:(DDLogMessage*) logMessage;
+(instancetype) getCurrentInstance;

@end

Expand Down
Loading
Loading