Strip EXIF metadata from all JPEG images.

Strip all EXIF metadata from all JPEGs by re-encoding the JPEG. This
will keep all of the necessary visual effects of the tags (by encoding
them directly in the image data) while stripped the EXIF tags
themselves.
This commit is contained in:
Greyson Parrelli 2018-03-19 11:22:39 -07:00 committed by Moxie Marlinspike
parent 10e5b24cfd
commit 7e1e666172
5 changed files with 20 additions and 6 deletions

View File

@ -175,7 +175,7 @@ public class MmsSendJob extends SendJob {
String lineNumber = getMyNumber(context); String lineNumber = getMyNumber(context);
Address destination = message.getRecipient().getAddress(); Address destination = message.getRecipient().getAddress();
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId()); MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
List<Attachment> scaledAttachments = scaleAttachments(mediaConstraints, message.getAttachments()); List<Attachment> scaledAttachments = scaleAndStripExifFromAttachments(mediaConstraints, message.getAttachments());
if (!TextUtils.isEmpty(lineNumber)) { if (!TextUtils.isEmpty(lineNumber)) {
req.setFrom(new EncodedStringValue(lineNumber)); req.setFrom(new EncodedStringValue(lineNumber));

View File

@ -141,7 +141,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
Optional<byte[]> profileKey = getProfileKey(message.getRecipient()); Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
List<Address> recipients = getGroupMessageRecipients(groupId, messageId); List<Address> recipients = getGroupMessageRecipients(groupId, messageId);
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(mediaConstraints, message.getAttachments()); List<Attachment> scaledAttachments = scaleAndStripExifFromAttachments(mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(scaledAttachments); List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(scaledAttachments);
List<SignalServiceAddress> addresses; List<SignalServiceAddress> addresses;

View File

@ -107,7 +107,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
try { try {
SignalServiceAddress address = getPushAddress(message.getRecipient().getAddress()); SignalServiceAddress address = getPushAddress(message.getRecipient().getAddress());
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(mediaConstraints, message.getAttachments()); List<Attachment> scaledAttachments = scaleAndStripExifFromAttachments(mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(scaledAttachments); List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(scaledAttachments);
Optional<byte[]> profileKey = getProfileKey(message.getRecipient()); Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder()

View File

@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.MediaStream; import org.thoughtcrime.securesms.mms.MediaStream;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
@ -50,8 +51,8 @@ public abstract class SendJob extends MasterSecretJob {
} }
} }
protected List<Attachment> scaleAttachments(@NonNull MediaConstraints constraints, protected List<Attachment> scaleAndStripExifFromAttachments(@NonNull MediaConstraints constraints,
@NonNull List<Attachment> attachments) @NonNull List<Attachment> attachments)
throws UndeliverableMessageException throws UndeliverableMessageException
{ {
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context); AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
@ -60,7 +61,12 @@ public abstract class SendJob extends MasterSecretJob {
for (Attachment attachment : attachments) { for (Attachment attachment : attachments) {
try { try {
if (constraints.isSatisfied(context, attachment)) { if (constraints.isSatisfied(context, attachment)) {
results.add(attachment); if (MediaUtil.isJpeg(attachment)) {
MediaStream stripped = constraints.getResizedMedia(context, attachment);
results.add(attachmentDatabase.updateAttachmentData(attachment, stripped));
} else {
results.add(attachment);
}
} else if (constraints.canResize(attachment)) { } else if (constraints.canResize(attachment)) {
MediaStream resized = constraints.getResizedMedia(context, attachment); MediaStream resized = constraints.getResizedMedia(context, attachment);
results.add(attachmentDatabase.updateAttachmentData(attachment, resized)); results.add(attachmentDatabase.updateAttachmentData(attachment, resized));

View File

@ -149,6 +149,10 @@ public class MediaUtil {
return isGif(attachment.getContentType()); return isGif(attachment.getContentType());
} }
public static boolean isJpeg(Attachment attachment) {
return isJpegType(attachment.getContentType());
}
public static boolean isImage(Attachment attachment) { public static boolean isImage(Attachment attachment) {
return isImageType(attachment.getContentType()); return isImageType(attachment.getContentType());
} }
@ -169,6 +173,10 @@ public class MediaUtil {
return !TextUtils.isEmpty(contentType) && contentType.trim().equals("image/gif"); return !TextUtils.isEmpty(contentType) && contentType.trim().equals("image/gif");
} }
public static boolean isJpegType(String contentType) {
return !TextUtils.isEmpty(contentType) && contentType.trim().equals(IMAGE_JPEG);
}
public static boolean isFile(Attachment attachment) { public static boolean isFile(Attachment attachment) {
return !isGif(attachment) && !isImage(attachment) && !isAudio(attachment) && !isVideo(attachment); return !isGif(attachment) && !isImage(attachment) && !isAudio(attachment) && !isVideo(attachment);
} }