Fixed a couple of bugs found during testing

Removed image dimension byte checking for attachments (we have file size checks already)
Fixed a couple of bugs where the message status for uploading attachments wasn't reflecting the current status
Fixed a bug where the conversation 'jump to bottom' was jumping to the bottom of the current page instead of to the end of the conversation
pull/856/head
Morgan Pretty 2 years ago
parent e28b4b4531
commit 499b20db6d

@ -48,6 +48,25 @@ public enum AttachmentUploadJob: JobExecutor {
return return
} }
// If this upload is related to sending a message then trigger the 'handleMessageWillSend' logic
// as if this is a retry the logic wouldn't run until after the upload has completed resulting in
// a potentially incorrect delivery status
Storage.shared.write { db in
guard
let sendJob: Job = try Job.fetchOne(db, id: details.messageSendJobId),
let sendJobDetails: Data = sendJob.details,
let details: MessageSendJob.Details = try? JSONDecoder()
.decode(MessageSendJob.Details.self, from: sendJobDetails)
else { return }
MessageSender.handleMessageWillSend(
db,
message: details.message,
interactionId: interactionId,
isSyncMessage: details.isSyncMessage
)
}
// Note: In the AttachmentUploadJob we intentionally don't provide our own db instance to prevent // Note: In the AttachmentUploadJob we intentionally don't provide our own db instance to prevent
// reentrancy issues when the success/failure closures get called before the upload as the JobRunner // reentrancy issues when the success/failure closures get called before the upload as the JobRunner
// will attempt to update the state of the job immediately // will attempt to update the state of the job immediately
@ -58,7 +77,29 @@ public enum AttachmentUploadJob: JobExecutor {
.sinkUntilComplete( .sinkUntilComplete(
receiveCompletion: { result in receiveCompletion: { result in
switch result { switch result {
case .failure(let error): failure(job, error, false) case .failure(let error):
// If this upload is related to sending a message then trigger the
// 'handleFailedMessageSend' logic as we want to ensure the message
// has the correct delivery status
Storage.shared.read { db in
guard
let sendJob: Job = try Job.fetchOne(db, id: details.messageSendJobId),
let sendJobDetails: Data = sendJob.details,
let details: MessageSendJob.Details = try? JSONDecoder()
.decode(MessageSendJob.Details.self, from: sendJobDetails)
else { return }
MessageSender.handleFailedMessageSend(
db,
message: details.message,
with: .other(error),
interactionId: interactionId,
isSyncMessage: details.isSyncMessage
)
}
failure(job, error, false)
case .finished: success(job, false) case .finished: success(job, false)
} }
} }

@ -168,7 +168,8 @@ public enum MessageSendJob: JobExecutor {
message: details.message, message: details.message,
to: details.destination, to: details.destination,
namespace: details.destination.defaultNamespace, namespace: details.destination.defaultNamespace,
interactionId: job.interactionId interactionId: job.interactionId,
isSyncMessage: details.isSyncMessage
) )
} }
.map { sendData in sendData.with(fileIds: messageFileIds) } .map { sendData in sendData.with(fileIds: messageFileIds) }
@ -227,7 +228,7 @@ extension MessageSendJob {
public let destination: Message.Destination public let destination: Message.Destination
public let message: Message public let message: Message
public let isSyncMessage: Bool? public let isSyncMessage: Bool
public let variant: Message.Variant? public let variant: Message.Variant?
// MARK: - Initialization // MARK: - Initialization
@ -235,7 +236,7 @@ extension MessageSendJob {
public init( public init(
destination: Message.Destination, destination: Message.Destination,
message: Message, message: Message,
isSyncMessage: Bool? = nil isSyncMessage: Bool = false
) { ) {
self.destination = destination self.destination = destination
self.message = message self.message = message
@ -256,7 +257,7 @@ extension MessageSendJob {
self = Details( self = Details(
destination: try container.decode(Message.Destination.self, forKey: .destination), destination: try container.decode(Message.Destination.self, forKey: .destination),
message: try variant.decode(from: container, forKey: .message), message: try variant.decode(from: container, forKey: .message),
isSyncMessage: try? container.decode(Bool.self, forKey: .isSyncMessage) isSyncMessage: ((try? container.decode(Bool.self, forKey: .isSyncMessage)) ?? false)
) )
} }
@ -270,7 +271,7 @@ extension MessageSendJob {
try container.encode(destination, forKey: .destination) try container.encode(destination, forKey: .destination)
try container.encode(message, forKey: .message) try container.encode(message, forKey: .message)
try container.encodeIfPresent(isSyncMessage, forKey: .isSyncMessage) try container.encode(isSyncMessage, forKey: .isSyncMessage)
try container.encode(variant, forKey: .variant) try container.encode(variant, forKey: .variant)
} }
} }

@ -522,10 +522,7 @@ extension MessageReceiver {
didAdminLeave || // If the admin leaves the group is disbanded didAdminLeave || // If the admin leaves the group is disbanded
member.profileId == sender member.profileId == sender
} }
let updatedMemberIds: Set<String> = members let memberIdsToRemove: [String] = members.map { $0.profileId }
.map { $0.profileId }
.asSet()
.subtracting(membersToRemove.map { $0.profileId })
// Update libSession // Update libSession
try? SessionUtil.update( try? SessionUtil.update(
@ -547,7 +544,7 @@ extension MessageReceiver {
// Delete the members to remove // Delete the members to remove
try GroupMember try GroupMember
.filter(GroupMember.Columns.groupId == threadId) .filter(GroupMember.Columns.groupId == threadId)
.filter(updatedMemberIds.contains(GroupMember.Columns.profileId)) .filter(memberIdsToRemove.contains(GroupMember.Columns.profileId))
.deleteAll(db) .deleteAll(db)
if didAdminLeave || sender == userPublicKey { if didAdminLeave || sender == userPublicKey {

@ -986,7 +986,7 @@ public final class MessageSender {
) )
} }
@discardableResult private static func handleFailedMessageSend( @discardableResult internal static func handleFailedMessageSend(
_ db: Database, _ db: Database,
message: Message, message: Message,
with error: MessageSenderError, with error: MessageSenderError,

@ -861,6 +861,7 @@ public extension SessionThreadViewModel {
\(openGroup[.permissions]) AS \(ViewModel.openGroupPermissionsKey), \(openGroup[.permissions]) AS \(ViewModel.openGroupPermissionsKey),
\(Interaction.self).\(ViewModel.interactionIdKey), \(Interaction.self).\(ViewModel.interactionIdKey),
\(Interaction.self).\(ViewModel.interactionTimestampMsKey),
\(SQL("\(userPublicKey)")) AS \(ViewModel.currentUserPublicKeyKey) \(SQL("\(userPublicKey)")) AS \(ViewModel.currentUserPublicKeyKey)
@ -871,7 +872,7 @@ public extension SessionThreadViewModel {
SELECT SELECT
\(interaction[.id]) AS \(ViewModel.interactionIdKey), \(interaction[.id]) AS \(ViewModel.interactionIdKey),
\(interaction[.threadId]), \(interaction[.threadId]),
MAX(\(interaction[.timestampMs])), MAX(\(interaction[.timestampMs])) AS \(ViewModel.interactionTimestampMsKey),
SUM(\(interaction[.wasRead]) = false) AS \(ViewModel.threadUnreadCountKey) SUM(\(interaction[.wasRead]) = false) AS \(ViewModel.threadUnreadCountKey)

@ -34,6 +34,7 @@ public struct ProfileManager {
// Before encrypting and submitting we NULL pad the name data to this length. // Before encrypting and submitting we NULL pad the name data to this length.
private static let nameDataLength: UInt = 64 private static let nameDataLength: UInt = 64
public static let maxAvatarDiameter: CGFloat = 640 public static let maxAvatarDiameter: CGFloat = 640
private static let maxAvatarBytes: UInt = (5 * 1000 * 1000)
public static let avatarAES256KeyByteLength: Int = 32 public static let avatarAES256KeyByteLength: Int = 32
private static let avatarNonceLength: Int = 12 private static let avatarNonceLength: Int = 12
private static let avatarTagLength: Int = 16 private static let avatarTagLength: Int = 16
@ -370,7 +371,6 @@ public struct ProfileManager {
// If the profile avatar was updated or removed then encrypt with a new profile key // If the profile avatar was updated or removed then encrypt with a new profile key
// to ensure that other users know that our profile picture was updated // to ensure that other users know that our profile picture was updated
let newProfileKey: Data let newProfileKey: Data
let maxAvatarBytes: UInt = (5 * 1000 * 1000)
let avatarImageData: Data? let avatarImageData: Data?
do { do {

@ -157,14 +157,6 @@ typedef struct {
return CGSizeZero; return CGSizeZero;
} }
const CGFloat kExpectedBytePerPixel = 4;
CGFloat kMaxValidImageDimension = OWSMediaUtils.kMaxAnimatedImageDimensions;
CGFloat kMaxBytes = kMaxValidImageDimension * kMaxValidImageDimension * kExpectedBytePerPixel;
if (data.length > kMaxBytes) {
return CGSizeZero;
}
return imageSize; return imageSize;
} }
@ -176,7 +168,8 @@ typedef struct {
ImageDimensionInfo dimensionInfo = [self ows_imageDimensionWithImageSource:imageSource isAnimated:isAnimated]; ImageDimensionInfo dimensionInfo = [self ows_imageDimensionWithImageSource:imageSource isAnimated:isAnimated];
CFRelease(imageSource); CFRelease(imageSource);
if (![self ows_isValidImageDimension:dimensionInfo.pixelSize depthBytes:dimensionInfo.depthBytes isAnimated:isAnimated]) { if (dimensionInfo.pixelSize.width < 1 || dimensionInfo.pixelSize.height < 1 || dimensionInfo.depthBytes < 1) {
// Invalid metadata.
return CGSizeZero; return CGSizeZero;
} }

Loading…
Cancel
Save