diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java index fd833b59a7..dc9e7c6d1d 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java @@ -20,7 +20,12 @@ package org.sleuthkit.autopsy.modules.embeddedfileextractor; import java.io.File; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; +import javax.imageio.ImageIO; +import javax.imageio.spi.IIORegistry; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.AbstractFile; @@ -122,8 +127,53 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda } catch (NoCurrentCaseException ex) { throw new IngestModuleException(Bundle.EmbeddedFileExtractorIngestModule_UnableToGetMSOfficeExtractor_errMsg(), ex); } + + /** + * Initialize Java's Image I/O API so that image reading and writing + * (needed for image extraction) happens consistently through the + * same providers. See JIRA-6951 for more details. + */ + initializeImageIO(); } + + /** + * Initializes the ImageIO API and sorts the providers for + * deterministic image reading and writing. + */ + private void initializeImageIO() { + ImageIO.scanForPlugins(); + + // Sift through each registry category and sort category providers by + // their canonical class name. + IIORegistry pluginRegistry = IIORegistry.getDefaultInstance(); + Iterator> categories = pluginRegistry.getCategories(); + while(categories.hasNext()) { + sortPluginsInCategory(pluginRegistry, categories.next()); + } + } + + /** + * Sorts all ImageIO SPI providers by their class name. + */ + private void sortPluginsInCategory(IIORegistry pluginRegistry, Class category) { + Iterator serviceProviderIter = pluginRegistry.getServiceProviders(category, false); + ArrayList providers = new ArrayList<>(); + while (serviceProviderIter.hasNext()) { + providers.add(serviceProviderIter.next()); + } + Collections.sort(providers, (first, second) -> { + return first.getClass().getCanonicalName().compareToIgnoreCase(second.getClass().getCanonicalName()); + }); + for(int i = 0; i < providers.size() - 1; i++) { + for(int j = i + 1; j < providers.size(); j++) { + // The registry only accepts pairwise orderings. To guarantee a + // total order, all pairs need to be exhausted. + pluginRegistry.setOrdering(category, providers.get(i), + providers.get(j)); + } + } + } @Override public ProcessResult process(AbstractFile abstractFile) {