Use UploadSpec for attachment uploads

This commit is contained in:
AsamK 2024-07-25 22:17:46 +02:00
parent e51b1ee23a
commit 2db3d3259e
6 changed files with 67 additions and 46 deletions

View file

@ -2381,6 +2381,13 @@
"name":"org.whispersystems.signalservice.internal.keybackup.protos.RestoreResponse", "name":"org.whispersystems.signalservice.internal.keybackup.protos.RestoreResponse",
"fields":[{"name":"bitField0_"}, {"name":"data_"}, {"name":"status_"}, {"name":"token_"}, {"name":"tries_"}] "fields":[{"name":"bitField0_"}, {"name":"data_"}, {"name":"status_"}, {"name":"token_"}, {"name":"tries_"}]
}, },
{
"name":"org.whispersystems.signalservice.internal.push.AttachmentUploadForm",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":["int","java.lang.String","java.util.Map","java.lang.String"] }, {"name":"<init>","parameterTypes":["int","java.lang.String","java.util.Map","java.lang.String","int","kotlin.jvm.internal.DefaultConstructorMarker"] }]
},
{ {
"name":"org.whispersystems.signalservice.internal.push.AttachmentV2UploadAttributes", "name":"org.whispersystems.signalservice.internal.push.AttachmentV2UploadAttributes",
"allDeclaredFields":true, "allDeclaredFields":true,

View file

@ -44,7 +44,7 @@ public class AttachmentHelper {
} }
public List<SignalServiceAttachment> uploadAttachments(final List<String> attachments) throws AttachmentInvalidException, IOException { public List<SignalServiceAttachment> uploadAttachments(final List<String> attachments) throws AttachmentInvalidException, IOException {
var attachmentStreams = AttachmentUtils.createAttachmentStreams(attachments); var attachmentStreams = createAttachmentStreams(attachments);
// Upload attachments here, so we only upload once even for multiple recipients // Upload attachments here, so we only upload once even for multiple recipients
var attachmentPointers = new ArrayList<SignalServiceAttachment>(attachmentStreams.size()); var attachmentPointers = new ArrayList<SignalServiceAttachment>(attachmentStreams.size());
@ -54,8 +54,21 @@ public class AttachmentHelper {
return attachmentPointers; return attachmentPointers;
} }
private List<SignalServiceAttachmentStream> createAttachmentStreams(List<String> attachments) throws AttachmentInvalidException, IOException {
if (attachments == null) {
return null;
}
final var signalServiceAttachments = new ArrayList<SignalServiceAttachmentStream>(attachments.size());
for (var attachment : attachments) {
final var uploadSpec = dependencies.getMessageSender().getResumableUploadSpec().toProto();
signalServiceAttachments.add(AttachmentUtils.createAttachmentStream(attachment, uploadSpec));
}
return signalServiceAttachments;
}
public SignalServiceAttachmentPointer uploadAttachment(String attachment) throws IOException, AttachmentInvalidException { public SignalServiceAttachmentPointer uploadAttachment(String attachment) throws IOException, AttachmentInvalidException {
var attachmentStream = AttachmentUtils.createAttachmentStream(attachment); final var uploadSpec = dependencies.getMessageSender().getResumableUploadSpec().toProto();
var attachmentStream = AttachmentUtils.createAttachmentStream(attachment, uploadSpec);
return uploadAttachment(attachmentStream); return uploadAttachment(attachmentStream);
} }

View file

@ -109,7 +109,8 @@ public class GroupHelper {
return Optional.empty(); return Optional.empty();
} }
return Optional.of(AttachmentUtils.createAttachmentStream(streamDetails, Optional.empty())); final var uploadSpec = dependencies.getMessageSender().getResumableUploadSpec().toProto();
return Optional.of(AttachmentUtils.createAttachmentStream(streamDetails, Optional.empty(), uploadSpec));
} }
public GroupInfoV2 getOrMigrateGroup( public GroupInfoV2 getOrMigrateGroup(

View file

@ -36,6 +36,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOper
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.internal.push.SyncMessage; import org.whispersystems.signalservice.internal.push.SyncMessage;
import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -115,10 +116,15 @@ public class SyncHelper {
if (groupsFile.exists() && groupsFile.length() > 0) { if (groupsFile.exists() && groupsFile.length() > 0) {
try (var groupsFileStream = new FileInputStream(groupsFile)) { try (var groupsFileStream = new FileInputStream(groupsFile)) {
final var uploadSpec = context.getDependencies()
.getMessageSender()
.getResumableUploadSpec()
.toProto();
var attachmentStream = SignalServiceAttachment.newStreamBuilder() var attachmentStream = SignalServiceAttachment.newStreamBuilder()
.withStream(groupsFileStream) .withStream(groupsFileStream)
.withContentType(MimeUtils.OCTET_STREAM) .withContentType(MimeUtils.OCTET_STREAM)
.withLength(groupsFile.length()) .withLength(groupsFile.length())
.withResumableUploadSpec(ResumableUploadSpec.from(uploadSpec))
.build(); .build();
context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forGroups(attachmentStream)); context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forGroups(attachmentStream));
@ -158,10 +164,15 @@ public class SyncHelper {
if (contactsFile.exists() && contactsFile.length() > 0) { if (contactsFile.exists() && contactsFile.length() > 0) {
try (var contactsFileStream = new FileInputStream(contactsFile)) { try (var contactsFileStream = new FileInputStream(contactsFile)) {
final var uploadSpec = context.getDependencies()
.getMessageSender()
.getResumableUploadSpec()
.toProto();
var attachmentStream = SignalServiceAttachment.newStreamBuilder() var attachmentStream = SignalServiceAttachment.newStreamBuilder()
.withStream(contactsFileStream) .withStream(contactsFileStream)
.withContentType(MimeUtils.OCTET_STREAM) .withContentType(MimeUtils.OCTET_STREAM)
.withLength(contactsFile.length()) .withLength(contactsFile.length())
.withResumableUploadSpec(ResumableUploadSpec.from(uploadSpec))
.build(); .build();
context.getSendHelper() context.getSendHelper()
@ -400,7 +411,8 @@ public class SyncHelper {
return Optional.empty(); return Optional.empty();
} }
return Optional.of(AttachmentUtils.createAttachmentStream(streamDetails, Optional.empty())); final var uploadSpec = context.getDependencies().getMessageSender().getResumableUploadSpec().toProto();
return Optional.of(AttachmentUtils.createAttachmentStream(streamDetails, Optional.empty(), uploadSpec));
} }
private void downloadContactAvatar(SignalServiceAttachment avatar, RecipientAddress address) { private void downloadContactAvatar(SignalServiceAttachment avatar, RecipientAddress address) {

View file

@ -742,8 +742,13 @@ public class ManagerImpl implements Manager {
final var additionalAttachments = new ArrayList<SignalServiceAttachment>(); final var additionalAttachments = new ArrayList<SignalServiceAttachment>();
if (message.messageText().length() > 2000) { if (message.messageText().length() > 2000) {
final var messageBytes = message.messageText().getBytes(StandardCharsets.UTF_8); final var messageBytes = message.messageText().getBytes(StandardCharsets.UTF_8);
final var textAttachment = AttachmentUtils.createAttachmentStream(new StreamDetails(new ByteArrayInputStream( final var uploadSpec = dependencies.getMessageSender().getResumableUploadSpec().toProto();
messageBytes), MimeUtils.LONG_TEXT, messageBytes.length), Optional.empty()); final var streamDetails = new StreamDetails(new ByteArrayInputStream(messageBytes),
MimeUtils.LONG_TEXT,
messageBytes.length);
final var textAttachment = AttachmentUtils.createAttachmentStream(streamDetails,
Optional.empty(),
uploadSpec);
messageBuilder.withBody(message.messageText().substring(0, 2000)); messageBuilder.withBody(message.messageText().substring(0, 2000));
additionalAttachments.add(context.getAttachmentHelper().uploadAttachment(textAttachment)); additionalAttachments.add(context.getAttachmentHelper().uploadAttachment(textAttachment));
} else { } else {
@ -800,11 +805,15 @@ public class ManagerImpl implements Manager {
if (streamDetails == null) { if (streamDetails == null) {
throw new InvalidStickerException("Missing local sticker file"); throw new InvalidStickerException("Missing local sticker file");
} }
final var uploadSpec = dependencies.getMessageSender().getResumableUploadSpec().toProto();
final var stickerAttachment = AttachmentUtils.createAttachmentStream(streamDetails,
Optional.empty(),
uploadSpec);
messageBuilder.withSticker(new SignalServiceDataMessage.Sticker(packId.serialize(), messageBuilder.withSticker(new SignalServiceDataMessage.Sticker(packId.serialize(),
stickerPack.packKey(), stickerPack.packKey(),
stickerId, stickerId,
manifestSticker.emoji(), manifestSticker.emoji(),
AttachmentUtils.createAttachmentStream(streamDetails, Optional.empty()))); stickerAttachment));
} }
if (!message.previews().isEmpty()) { if (!message.previews().isEmpty()) {
final var previews = new ArrayList<SignalServicePreview>(message.previews().size()); final var previews = new ArrayList<SignalServicePreview>(message.previews().size());

View file

@ -1,65 +1,44 @@
package org.asamk.signal.manager.util; package org.asamk.signal.manager.util;
import org.asamk.signal.manager.api.AttachmentInvalidException; import org.asamk.signal.manager.api.AttachmentInvalidException;
import org.signal.protos.resumableuploads.ResumableUpload;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
import org.whispersystems.signalservice.api.push.exceptions.ResumeLocationInvalidException;
import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.api.util.StreamDetails;
import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec; import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
public class AttachmentUtils { public class AttachmentUtils {
public static List<SignalServiceAttachmentStream> createAttachmentStreams(List<String> attachments) throws AttachmentInvalidException { public static SignalServiceAttachmentStream createAttachmentStream(
if (attachments == null) { String attachment, ResumableUpload resumableUpload
return null; ) throws AttachmentInvalidException {
}
final var signalServiceAttachments = new ArrayList<SignalServiceAttachmentStream>(attachments.size());
for (var attachment : attachments) {
signalServiceAttachments.add(createAttachmentStream(attachment));
}
return signalServiceAttachments;
}
public static SignalServiceAttachmentStream createAttachmentStream(String attachment) throws AttachmentInvalidException {
try { try {
final var streamDetails = Utils.createStreamDetails(attachment); final var streamDetails = Utils.createStreamDetails(attachment);
return createAttachmentStream(streamDetails.first(), streamDetails.second()); return createAttachmentStream(streamDetails.first(), streamDetails.second(), resumableUpload);
} catch (IOException e) { } catch (IOException e) {
throw new AttachmentInvalidException(attachment, e); throw new AttachmentInvalidException(attachment, e);
} }
} }
public static SignalServiceAttachmentStream createAttachmentStream( public static SignalServiceAttachmentStream createAttachmentStream(
StreamDetails streamDetails, Optional<String> name StreamDetails streamDetails, Optional<String> name, ResumableUpload resumableUpload
) { ) throws ResumeLocationInvalidException {
// TODO maybe add a parameter to set the voiceNote, borderless, preview, width, height and caption option // TODO maybe add a parameter to set the voiceNote, borderless, preview, width, height and caption option
final var uploadTimestamp = System.currentTimeMillis(); final var uploadTimestamp = System.currentTimeMillis();
Optional<byte[]> preview = Optional.empty(); final var resumableUploadSpec = ResumableUploadSpec.from(resumableUpload);
Optional<String> caption = Optional.empty(); return SignalServiceAttachmentStream.newStreamBuilder()
Optional<String> blurHash = Optional.empty(); .withStream(streamDetails.getStream())
final Optional<ResumableUploadSpec> resumableUploadSpec = Optional.empty(); .withContentType(streamDetails.getContentType())
return new SignalServiceAttachmentStream(streamDetails.getStream(), .withLength(streamDetails.getLength())
streamDetails.getContentType(), .withFileName(name.orElse(null))
streamDetails.getLength(), .withUploadTimestamp(uploadTimestamp)
name, .withResumableUploadSpec(resumableUploadSpec)
false, .withUuid(UUID.randomUUID())
false, .build();
false,
false,
preview,
0,
0,
uploadTimestamp,
caption,
blurHash,
null,
null,
resumableUploadSpec,
UUID.randomUUID());
} }
} }