/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.ingest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.TreeMap;
import java.util.logging.Level;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.examples.SampleExecutableIngestModuleFactory;
import org.sleuthkit.autopsy.examples.SampleIngestModuleFactory;
import org.sleuthkit.autopsy.modules.android.AndroidModuleFactory;
import org.sleuthkit.autopsy.modules.e01verify.E01VerifierModuleFactory;
import org.sleuthkit.autopsy.modules.exif.ExifParserModuleFactory;
import org.sleuthkit.autopsy.modules.fileextmismatch.FileExtMismatchDetectorModuleFactory;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory;
import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory;
import org.sleuthkit.autopsy.modules.interestingitems.InterestingItemsIngestModuleFactory;
import org.sleuthkit.autopsy.modules.photoreccarver.PhotoRecCarverIngestModuleFactory;
import org.sleuthkit.autopsy.modules.embeddedfileextractor.EmbeddedFileExtractorModuleFactory;
import org.sleuthkit.autopsy.python.JythonModuleLoader;
/**
* Discovers and instantiates ingest module factories.
*/
final class IngestModuleFactoryLoader {
private static final Logger logger = Logger.getLogger(IngestModuleFactoryLoader.class.getName());
private static final String SAMPLE_MODULE_FACTORY_CLASS_NAME = SampleIngestModuleFactory.class.getCanonicalName();
private static final String SAMPLE_EXECUTABLE_MODULE_FACTORY_CLASS_NAME = SampleExecutableIngestModuleFactory.class.getCanonicalName();
private static final ArrayList<String> coreModuleOrdering = new ArrayList<String>() {
{
// The ordering of the core ingest module factories implemented
// using Java is hard-coded.
add("org.sleuthkit.autopsy.recentactivity.RecentActivityExtracterModuleFactory"); //NON-NLS
add(HashLookupModuleFactory.class.getCanonicalName());
add(FileTypeIdModuleFactory.class.getCanonicalName());
add(EmbeddedFileExtractorModuleFactory.class.getCanonicalName());
add(ExifParserModuleFactory.class.getCanonicalName());
add("org.sleuthkit.autopsy.keywordsearch.KeywordSearchModuleFactory"); //NON-NLS
add("org.sleuthkit.autopsy.thunderbirdparser.EmailParserModuleFactory"); //NON-NLS
add(FileExtMismatchDetectorModuleFactory.class.getCanonicalName());
add(E01VerifierModuleFactory.class.getCanonicalName());
add(AndroidModuleFactory.class.getCanonicalName());
add(InterestingItemsIngestModuleFactory.class.getCanonicalName());
add(PhotoRecCarverIngestModuleFactory.class.getCanonicalName());
}
};
/**
* Gets the currently available set of ingest module factories. The
* factories are not cached between calls since NetBeans modules with
* classes annotated as IngestModuleFactory service providers and/or Python
* scripts defining classes derived from IngestModuleFactory may be added or
* removed between invocations.
*
* @return A list of objects that implement the IngestModuleFactory
* interface.
*/
static List<IngestModuleFactory> getIngestModuleFactories() {
// A hash set of display names and a hash map of class names to
// discovered factories are used to de-duplicate and order the
// factories.
HashSet<String> moduleDisplayNames = new HashSet<>();
HashMap<String, IngestModuleFactory> javaFactoriesByClass = new HashMap<>();
// Discover the ingest module factories implemented using Java with a
// service provider annotation for the IngestModuleFactory interface.
for (IngestModuleFactory factory : Lookup.getDefault().lookupAll(IngestModuleFactory.class)) {
IngestModuleFactoryLoader.addFactory(factory, moduleDisplayNames, javaFactoriesByClass);
}
// Discover the ingest module factories implemented using Java with a
// service provider annotation for the IngestModuleFactoryAdapter
// abstract base class.
for (IngestModuleFactory factory : Lookup.getDefault().lookupAll(IngestModuleFactoryAdapter.class)) {
if (!javaFactoriesByClass.containsValue(factory)) {
IngestModuleFactoryLoader.addFactory(factory, moduleDisplayNames, javaFactoriesByClass);
}
}
// Add the core ingest module factories in the desired order, removing
// the core factories from the map so that the map will only contain
// non-core modules after this loop.
List<IngestModuleFactory> factories = new ArrayList<>();
for (String className : coreModuleOrdering) {
IngestModuleFactory coreFactory = javaFactoriesByClass.remove(className);
if (coreFactory != null) {
factories.add(coreFactory);
} else {
logger.log(Level.SEVERE, "Core factory {0} not loaded", className); //NON-NLS
}
}
// Add any remaining non-core factories discovered. Order with an
// alphabetical sort by module display name.
TreeMap<String, IngestModuleFactory> javaFactoriesSortedByName = new TreeMap<>();
for (IngestModuleFactory factory : javaFactoriesByClass.values()) {
javaFactoriesSortedByName.put(factory.getModuleDisplayName(), factory);
}
factories.addAll(javaFactoriesSortedByName.values());
// Add any ingest module factories implemented using Jython. Order is
// not guaranteed!
for (IngestModuleFactory factory : JythonModuleLoader.getIngestModuleFactories()) {
if (!moduleDisplayNames.contains(factory.getModuleDisplayName())) {
moduleDisplayNames.add(factory.getModuleDisplayName());
factories.add(factory);
logger.log(Level.INFO, "Found ingest module factory: name = {0}, version = {1}", new Object[]{factory.getModuleDisplayName(), factory.getModuleVersionNumber()}); //NON-NLS
} else {
logger.log(Level.SEVERE, "Found duplicate ingest module display name (name = {0})", factory.getModuleDisplayName()); //NON-NLS
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
NbBundle.getMessage(IngestModuleFactoryLoader.class, "IngestModuleFactoryLoader.errorMessages.duplicateDisplayName", factory.getModuleDisplayName()),
NotifyDescriptor.ERROR_MESSAGE));
}
}
return factories;
}
private static void addFactory(IngestModuleFactory factory, HashSet<String> moduleDisplayNames, HashMap<String, IngestModuleFactory> javaFactoriesByClass) {
// Ignore the sample ingest module factories implemented in Java.
String className = factory.getClass().getCanonicalName();
if (className.equals(IngestModuleFactoryLoader.SAMPLE_MODULE_FACTORY_CLASS_NAME)
|| className.equals(IngestModuleFactoryLoader.SAMPLE_EXECUTABLE_MODULE_FACTORY_CLASS_NAME)) {
return;
}
if (!moduleDisplayNames.contains(factory.getModuleDisplayName())) {
moduleDisplayNames.add(factory.getModuleDisplayName());
javaFactoriesByClass.put(factory.getClass().getCanonicalName(), factory);
logger.log(Level.INFO, "Found ingest module factory: name = {0}, version = {1}", new Object[]{factory.getModuleDisplayName(), factory.getModuleVersionNumber()}); //NON-NLS
} else {
logger.log(Level.SEVERE, "Found duplicate ingest module display name (name = {0})", factory.getModuleDisplayName()); //NON-NLS
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
NbBundle.getMessage(IngestModuleFactoryLoader.class, "IngestModuleFactoryLoader.errorMessages.duplicateDisplayName", factory.getModuleDisplayName()),
NotifyDescriptor.ERROR_MESSAGE));
}
}
}