Merge branch 'develop' of github.com:sleuthkit/autopsy into 6896-testing

This commit is contained in:
Greg DiCristofaro 2020-10-28 10:05:38 -04:00
commit 1866fd461c
56 changed files with 3556 additions and 118 deletions

View File

@ -232,7 +232,8 @@
<build-prerequisite/> <build-prerequisite/>
<compile-dependency/> <compile-dependency/>
<run-dependency> <run-dependency>
<specification-version>1.0</specification-version> <release-version>1</release-version>
<specification-version>23</specification-version>
</run-dependency> </run-dependency>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -62,6 +62,7 @@ public final class UserPreferences {
private static final String MESSAGE_SERVICE_HOST = "MessageServiceHost"; //NON-NLS private static final String MESSAGE_SERVICE_HOST = "MessageServiceHost"; //NON-NLS
private static final String MESSAGE_SERVICE_PORT = "MessageServicePort"; //NON-NLS private static final String MESSAGE_SERVICE_PORT = "MessageServicePort"; //NON-NLS
public static final String TEXT_TRANSLATOR_NAME = "TextTranslatorName"; public static final String TEXT_TRANSLATOR_NAME = "TextTranslatorName";
public static final String OCR_TRANSLATION_ENABLED = "OcrTranslationEnabled";
public static final String PROCESS_TIME_OUT_ENABLED = "ProcessTimeOutEnabled"; //NON-NLS public static final String PROCESS_TIME_OUT_ENABLED = "ProcessTimeOutEnabled"; //NON-NLS
public static final String PROCESS_TIME_OUT_HOURS = "ProcessTimeOutHours"; //NON-NLS public static final String PROCESS_TIME_OUT_HOURS = "ProcessTimeOutHours"; //NON-NLS
private static final int DEFAULT_PROCESS_TIMEOUT_HR = 60; private static final int DEFAULT_PROCESS_TIMEOUT_HR = 60;
@ -347,6 +348,14 @@ public final class UserPreferences {
public static String getTextTranslatorName() { public static String getTextTranslatorName() {
return preferences.get(TEXT_TRANSLATOR_NAME, null); return preferences.get(TEXT_TRANSLATOR_NAME, null);
} }
public static void setUseOcrInTranslation(boolean enableOcr) {
preferences.putBoolean(OCR_TRANSLATION_ENABLED, enableOcr);
}
public static boolean getUseOcrInTranslation() {
return preferences.getBoolean(OCR_TRANSLATION_ENABLED, true);
}
/** /**
* Persists message service connection info. * Persists message service connection info.
@ -623,4 +632,4 @@ public final class UserPreferences {
return Paths.get(UserMachinePreferences.getBaseTempDirectory(), getAppName()) return Paths.get(UserMachinePreferences.getBaseTempDirectory(), getAppName())
.toAbsolutePath().toString(); .toAbsolutePath().toString();
} }
} }

View File

@ -27,6 +27,7 @@ import javax.swing.text.ViewFactory;
import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.InlineView; import javax.swing.text.html.InlineView;
import javax.swing.text.html.ParagraphView; import javax.swing.text.html.ParagraphView;
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
/** /**
* JTextPane extension that auto wraps input text using an HTMLEditorKit trick. * JTextPane extension that auto wraps input text using an HTMLEditorKit trick.
@ -98,6 +99,6 @@ public class AutoWrappingJTextPane extends JTextPane {
@Override @Override
public void setText(String text) { public void setText(String text) {
super.setText("<pre>" + text + "</pre>"); super.setText("<pre>" + EscapeUtil.escapeHtml(text) + "</pre>");
} }
} }

View File

@ -20,7 +20,12 @@ package org.sleuthkit.autopsy.modules.embeddedfileextractor;
import java.io.File; import java.io.File;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import javax.imageio.ImageIO;
import javax.imageio.spi.IIORegistry;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
@ -122,8 +127,53 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
throw new IngestModuleException(Bundle.EmbeddedFileExtractorIngestModule_UnableToGetMSOfficeExtractor_errMsg(), 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<Class<?>> categories = pluginRegistry.getCategories();
while(categories.hasNext()) {
sortPluginsInCategory(pluginRegistry, categories.next());
}
}
/**
* Sorts all ImageIO SPI providers by their class name.
*/
private <T> void sortPluginsInCategory(IIORegistry pluginRegistry, Class<T> category) {
Iterator<T> serviceProviderIter = pluginRegistry.getServiceProviders(category, false);
ArrayList<T> 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 @Override
public ProcessResult process(AbstractFile abstractFile) { public ProcessResult process(AbstractFile abstractFile) {

View File

@ -123,7 +123,7 @@ final class PDFAttachmentExtractor {
@Override @Override
public void parseEmbedded(InputStream in, ContentHandler ch, Metadata mtdt, boolean bln) throws SAXException, IOException { public void parseEmbedded(InputStream in, ContentHandler ch, Metadata mtdt, boolean bln) throws SAXException, IOException {
//Resource naming scheme is used internally in autopsy, therefore we can guarentee uniqueness. //Resource naming scheme is used internally in autopsy, therefore we can guarentee uniqueness.
String uniqueExtractedName = parentID + "_attch_" + attachmentCount++; //NON-NLS String uniqueExtractedName = "extract_" + attachmentCount++; //NON-NLS
String name = mtdt.get(Metadata.RESOURCE_NAME_KEY); String name = mtdt.get(Metadata.RESOURCE_NAME_KEY);
String ext = FilenameUtils.getExtension(name); String ext = FilenameUtils.getExtension(name);

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2019 Basis Technology Corp. * Copyright 2011-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -46,7 +46,6 @@ import org.apache.tika.Tika;
import org.apache.tika.exception.TikaException; import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.Metadata; import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.AutoDetectParser; import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.EmptyParser;
import org.apache.tika.parser.ParseContext; import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.Parser; import org.apache.tika.parser.Parser;
import org.apache.tika.parser.ParsingReader; import org.apache.tika.parser.ParsingReader;
@ -72,6 +71,9 @@ import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import org.apache.tika.parser.pdf.PDFParserConfig.OCR_STRATEGY;
/** /**
* Extracts text from Tika supported content. Protects against Tika parser hangs * Extracts text from Tika supported content. Protects against Tika parser hangs
@ -126,16 +128,6 @@ final class TikaTextExtractor implements TextExtractor {
"application/x-z", //NON-NLS "application/x-z", //NON-NLS
"application/x-compress"); //NON-NLS "application/x-compress"); //NON-NLS
//Tika should ignore types with embedded files that can be handled by the unpacking modules
private static final List<String> EMBEDDED_FILE_MIME_TYPES
= ImmutableList.of("application/msword", //NON-NLS
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", //NON-NLS
"application/vnd.ms-powerpoint", //NON-NLS
"application/vnd.openxmlformats-officedocument.presentationml.presentation", //NON-NLS
"application/vnd.ms-excel", //NON-NLS
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //NON-NLS
"application/pdf"); //NON-NLS
// Used to log to the tika file that is why it uses the java.util.logging.logger class instead of the Autopsy one // Used to log to the tika file that is why it uses the java.util.logging.logger class instead of the Autopsy one
private static final java.util.logging.Logger TIKA_LOGGER = java.util.logging.Logger.getLogger("Tika"); //NON-NLS private static final java.util.logging.Logger TIKA_LOGGER = java.util.logging.Logger.getLogger("Tika"); //NON-NLS
private static final Logger AUTOPSY_LOGGER = Logger.getLogger(TikaTextExtractor.class.getName()); private static final Logger AUTOPSY_LOGGER = Logger.getLogger(TikaTextExtractor.class.getName());
@ -193,52 +185,31 @@ final class TikaTextExtractor implements TextExtractor {
*/ */
@Override @Override
public Reader getReader() throws InitReaderException { public Reader getReader() throws InitReaderException {
InputStream stream = null; if (!this.isSupported()) {
throw new InitReaderException("Content is not supported");
ParseContext parseContext = new ParseContext();
//Disable appending embedded file text to output for EFE supported types
//JIRA-4975
if(content instanceof AbstractFile && EMBEDDED_FILE_MIME_TYPES.contains(((AbstractFile)content).getMIMEType())) {
parseContext.set(Parser.class, new EmptyParser());
} else {
parseContext.set(Parser.class, parser);
} }
if (ocrEnabled() && content instanceof AbstractFile) { // Only abstract files are supported, see isSupported()
AbstractFile file = ((AbstractFile) content); final AbstractFile file = ((AbstractFile) content);
//Run OCR on images with Tesseract directly. // This mime type must be non-null, see isSupported()
if (file.getMIMEType().toLowerCase().startsWith("image/")) { final String mimeType = file.getMIMEType();
stream = performOCR(file);
} else {
//Otherwise, go through Tika for PDFs so that it can
//extract images and run Tesseract on them.
PDFParserConfig pdfConfig = new PDFParserConfig();
// Extracting the inline images and letting Tesseract run on each inline image. // Handle images seperately so the OCR task can be cancelled.
// https://wiki.apache.org/tika/PDFParser%20%28Apache%20PDFBox%29 // See JIRA-4519 for the need to have cancellation in the UI and ingest.
// https://tika.apache.org/1.7/api/org/apache/tika/parser/pdf/PDFParserConfig.html if (ocrEnabled() && mimeType.toLowerCase().startsWith("image/")) {
pdfConfig.setExtractInlineImages(true); InputStream imageOcrStream = performOCR(file);
// Multiple pages within a PDF file might refer to the same underlying image. return new InputStreamReader(imageOcrStream, Charset.forName("UTF-8"));
pdfConfig.setExtractUniqueInlineImagesOnly(true);
parseContext.set(PDFParserConfig.class, pdfConfig);
// Configure Tesseract parser to perform OCR
TesseractOCRConfig ocrConfig = new TesseractOCRConfig();
String tesseractFolder = TESSERACT_PATH.getParent();
ocrConfig.setTesseractPath(tesseractFolder);
ocrConfig.setLanguage(languagePacks);
ocrConfig.setTessdataPath(PlatformUtil.getOcrLanguagePacksPath());
parseContext.set(TesseractOCRConfig.class, ocrConfig);
stream = new ReadContentInputStream(content);
}
} else {
stream = new ReadContentInputStream(content);
} }
Metadata metadata = new Metadata(); // Set up Tika
final InputStream stream = new ReadContentInputStream(content);
final ParseContext parseContext = new ParseContext();
// Documents can contain other documents. By adding
// the parser back into the context, Tika will recursively
// parse embedded documents.
parseContext.set(Parser.class, parser);
// Use the more memory efficient Tika SAX parsers for DOCX and // Use the more memory efficient Tika SAX parsers for DOCX and
// PPTX files (it already uses SAX for XLSX). // PPTX files (it already uses SAX for XLSX).
OfficeParserConfig officeParserConfig = new OfficeParserConfig(); OfficeParserConfig officeParserConfig = new OfficeParserConfig();
@ -246,6 +217,30 @@ final class TikaTextExtractor implements TextExtractor {
officeParserConfig.setUseSAXDocxExtractor(true); officeParserConfig.setUseSAXDocxExtractor(true);
parseContext.set(OfficeParserConfig.class, officeParserConfig); parseContext.set(OfficeParserConfig.class, officeParserConfig);
if (ocrEnabled()) {
// Configure OCR for Tika if it chooses to run OCR
// during extraction
TesseractOCRConfig ocrConfig = new TesseractOCRConfig();
String tesseractFolder = TESSERACT_PATH.getParent();
ocrConfig.setTesseractPath(tesseractFolder);
ocrConfig.setLanguage(languagePacks);
ocrConfig.setTessdataPath(PlatformUtil.getOcrLanguagePacksPath());
parseContext.set(TesseractOCRConfig.class, ocrConfig);
// Configure how Tika handles OCRing PDFs
PDFParserConfig pdfConfig = new PDFParserConfig();
// This stategy tries to pick between OCRing a page in the
// PDF and doing text extraction. It makes this choice by
// first running text extraction and then counting characters.
// If there are too few characters or too many unmapped
// unicode characters, it'll run the entire page through OCR
// and take that output instead. See JIRA-6938
pdfConfig.setOcrStrategy(OCR_STRATEGY.AUTO);
parseContext.set(PDFParserConfig.class, pdfConfig);
}
Metadata metadata = new Metadata();
//Make the creation of a TikaReader a cancellable future in case it takes too long //Make the creation of a TikaReader a cancellable future in case it takes too long
Future<Reader> future = executorService.submit( Future<Reader> future = executorService.submit(
new GetTikaReader(parser, stream, metadata, parseContext)); new GetTikaReader(parser, stream, metadata, parseContext));
@ -568,4 +563,4 @@ final class TikaTextExtractor implements TextExtractor {
return reader; return reader;
} }
} }
} }

View File

@ -6,3 +6,4 @@ TranslationOptionsPanelController.moduleErr.msg=A module caused an error listeni
TranslationContentPanel.showLabel.text=Show: TranslationContentPanel.showLabel.text=Show:
TranslationOptionsPanel.translationServiceLabel.text=Text translator: TranslationOptionsPanel.translationServiceLabel.text=Text translator:
TranslationOptionsPanel.translationOptionsDescription.text=Configure a 3rd party text translation service to enable text and file name translation. TranslationOptionsPanel.translationOptionsDescription.text=Configure a 3rd party text translation service to enable text and file name translation.
TranslationOptionsPanel.enableOcrCheckBox.text=Enable Optical Character Recognition (OCR) in the translation content viewer

View File

@ -10,3 +10,4 @@ TranslationOptionsPanelController.moduleErr.msg=A module caused an error listeni
TranslationContentPanel.showLabel.text=Show: TranslationContentPanel.showLabel.text=Show:
TranslationOptionsPanel.translationServiceLabel.text=Text translator: TranslationOptionsPanel.translationServiceLabel.text=Text translator:
TranslationOptionsPanel.translationOptionsDescription.text=Configure a 3rd party text translation service to enable text and file name translation. TranslationOptionsPanel.translationOptionsDescription.text=Configure a 3rd party text translation service to enable text and file name translation.
TranslationOptionsPanel.enableOcrCheckBox.text=Enable Optical Character Recognition (OCR) in the translation content viewer

View File

@ -16,17 +16,23 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jSeparator1" alignment="0" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="translationServicePanel" max="32767" attributes="0"/> <Component id="translationServicePanel" max="32767" attributes="0"/>
<Component id="translationOptionsDescription" alignment="0" pref="462" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Component id="translationServiceLabel" min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/> <Group type="102" attributes="0">
<Component id="translatorComboBox" min="-2" pref="214" max="-2" attributes="0"/> <Component id="translationServiceLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Component id="translatorComboBox" min="-2" pref="214" max="-2" attributes="0"/>
</Group>
<Component id="enableOcrCheckBox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group> </Group>
<Component id="translationOptionsDescription" alignment="0" pref="462" max="32767" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -42,9 +48,13 @@
<Component id="translatorComboBox" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="translatorComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="translationServiceLabel" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="translationServiceLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="translationServicePanel" max="32767" attributes="0"/> <Component id="translationServicePanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="jSeparator1" min="-2" pref="10" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="enableOcrCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -76,5 +86,17 @@
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JSeparator" name="jSeparator1">
</Component>
<Component class="javax.swing.JCheckBox" name="enableOcrCheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/Bundle.properties" key="TranslationOptionsPanel.enableOcrCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="enableOcrCheckBoxActionPerformed"/>
</Events>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -111,6 +111,7 @@ final class TranslationOptionsPanel extends javax.swing.JPanel {
} }
translatorComboBox.setSelectedItem(currentSelection); translatorComboBox.setSelectedItem(currentSelection);
loadSelectedPanelSettings(); loadSelectedPanelSettings();
enableOcrCheckBox.setSelected(UserPreferences.getUseOcrInTranslation());
} }
/** /**
@ -128,6 +129,8 @@ final class TranslationOptionsPanel extends javax.swing.JPanel {
logger.log(Level.WARNING, "Unable to save settings for TextTranslator named: " + currentSelection, ex); logger.log(Level.WARNING, "Unable to save settings for TextTranslator named: " + currentSelection, ex);
} }
} }
// Save whether OCR is enabled in the content viewer
UserPreferences.setUseOcrInTranslation(enableOcrCheckBox.isSelected());
} }
@ -144,6 +147,8 @@ final class TranslationOptionsPanel extends javax.swing.JPanel {
translationServiceLabel = new javax.swing.JLabel(); translationServiceLabel = new javax.swing.JLabel();
translationServicePanel = new javax.swing.JPanel(); translationServicePanel = new javax.swing.JPanel();
translationOptionsDescription = new javax.swing.JLabel(); translationOptionsDescription = new javax.swing.JLabel();
jSeparator1 = new javax.swing.JSeparator();
enableOcrCheckBox = new javax.swing.JCheckBox();
translatorComboBox.addActionListener(new java.awt.event.ActionListener() { translatorComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
@ -157,20 +162,31 @@ final class TranslationOptionsPanel extends javax.swing.JPanel {
org.openide.awt.Mnemonics.setLocalizedText(translationOptionsDescription, org.openide.util.NbBundle.getMessage(TranslationOptionsPanel.class, "TranslationOptionsPanel.translationOptionsDescription.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(translationOptionsDescription, org.openide.util.NbBundle.getMessage(TranslationOptionsPanel.class, "TranslationOptionsPanel.translationOptionsDescription.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(enableOcrCheckBox, org.openide.util.NbBundle.getMessage(TranslationOptionsPanel.class, "TranslationOptionsPanel.enableOcrCheckBox.text")); // NOI18N
enableOcrCheckBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
enableOcrCheckBoxActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSeparator1)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(translationServicePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(translationServicePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(translationOptionsDescription, javax.swing.GroupLayout.DEFAULT_SIZE, 462, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(translationServiceLabel) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(10, 10, 10) .addGroup(layout.createSequentialGroup()
.addComponent(translatorComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 214, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(translationServiceLabel)
.addGap(0, 0, Short.MAX_VALUE)) .addGap(10, 10, 10)
.addComponent(translationOptionsDescription, javax.swing.GroupLayout.PREFERRED_SIZE, 462, Short.MAX_VALUE)) .addComponent(translatorComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 214, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(enableOcrCheckBox))
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap()) .addContainerGap())
); );
layout.setVerticalGroup( layout.setVerticalGroup(
@ -183,8 +199,12 @@ final class TranslationOptionsPanel extends javax.swing.JPanel {
.addComponent(translatorComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(translatorComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(translationServiceLabel)) .addComponent(translationServiceLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(translationServicePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(translationServicePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap()) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(enableOcrCheckBox)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -192,12 +212,18 @@ final class TranslationOptionsPanel extends javax.swing.JPanel {
updatePanel(); updatePanel();
}//GEN-LAST:event_translatorComboBoxActionPerformed }//GEN-LAST:event_translatorComboBoxActionPerformed
private void enableOcrCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_enableOcrCheckBoxActionPerformed
controller.changed();
}//GEN-LAST:event_enableOcrCheckBoxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox enableOcrCheckBox;
private javax.swing.JSeparator jSeparator1;
private javax.swing.JLabel translationOptionsDescription; private javax.swing.JLabel translationOptionsDescription;
private javax.swing.JLabel translationServiceLabel; private javax.swing.JLabel translationServiceLabel;
private javax.swing.JPanel translationServicePanel; private javax.swing.JPanel translationServicePanel;
private javax.swing.JComboBox<String> translatorComboBox; private javax.swing.JComboBox<String> translatorComboBox;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -8,6 +8,7 @@ TranslatedContentViewer.errorExtractingText=An error occurred while extracting t
TranslatedContentViewer.extractingText=Extracting text, please wait... TranslatedContentViewer.extractingText=Extracting text, please wait...
TranslatedContentViewer.fileHasNoText=File has no text. TranslatedContentViewer.fileHasNoText=File has no text.
TranslatedContentViewer.noServiceProvider=The machine translation software was not found. TranslatedContentViewer.noServiceProvider=The machine translation software was not found.
TranslatedContentViewer.ocrNotEnabled=OCR is not enabled. To change, go to Tools->Options->Machine Translation
TranslatedContentViewer.translatingText=Translating text, please wait... TranslatedContentViewer.translatingText=Translating text, please wait...
# {0} - exception message # {0} - exception message
TranslatedContentViewer.translationException=An error occurred while translating the text ({0}). TranslatedContentViewer.translationException=An error occurred while translating the text ({0}).

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -38,16 +38,15 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.corecomponents.DataContentViewerUtility;
import org.sleuthkit.autopsy.coreutils.ExecUtil.ProcessTerminator; import org.sleuthkit.autopsy.coreutils.ExecUtil.ProcessTerminator;
import org.sleuthkit.autopsy.textextractors.TextExtractor; import org.sleuthkit.autopsy.textextractors.TextExtractor;
import org.sleuthkit.autopsy.textextractors.TextExtractorFactory; import org.sleuthkit.autopsy.textextractors.TextExtractorFactory;
import org.sleuthkit.autopsy.textextractors.configs.ImageConfig; import org.sleuthkit.autopsy.textextractors.configs.ImageConfig;
import org.sleuthkit.autopsy.texttranslation.TextTranslationService; import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
import org.sleuthkit.datamodel.Content;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.texttranslation.ui.TranslationContentPanel.DisplayDropdownOptions; import org.sleuthkit.autopsy.texttranslation.ui.TranslationContentPanel.DisplayDropdownOptions;
@ -60,8 +59,6 @@ public final class TranslatedTextViewer implements TextViewer {
private static final Logger logger = Logger.getLogger(TranslatedTextViewer.class.getName()); private static final Logger logger = Logger.getLogger(TranslatedTextViewer.class.getName());
private static final boolean OCR_ENABLED = true;
private static final boolean OCR_DISABLED = false;
private static final int MAX_EXTRACT_SIZE_BYTES = 25600; private static final int MAX_EXTRACT_SIZE_BYTES = 25600;
private static final List<String> INSTALLED_LANGUAGE_PACKS = PlatformUtil.getOcrLanguagePacks(); private static final List<String> INSTALLED_LANGUAGE_PACKS = PlatformUtil.getOcrLanguagePacks();
private final TranslationContentPanel panel = new TranslationContentPanel(); private final TranslationContentPanel panel = new TranslationContentPanel();
@ -81,15 +78,10 @@ public final class TranslatedTextViewer implements TextViewer {
SelectionChangeListener displayDropDownListener = new DisplayDropDownChangeListener(); SelectionChangeListener displayDropDownListener = new DisplayDropDownChangeListener();
panel.addDisplayTextActionListener(displayDropDownListener); panel.addDisplayTextActionListener(displayDropDownListener);
panel.addOcrDropDownActionListener(new OCRDropdownChangeListener()); panel.addOcrDropDownActionListener(new OCRDropdownChangeListener());
Content source = DataContentViewerUtility.getDefaultContent(node); if (UserPreferences.getUseOcrInTranslation()) {
panel.addLanguagePackNames(INSTALLED_LANGUAGE_PACKS);
if (source instanceof AbstractFile) {
boolean isImage = ((AbstractFile) source).getMIMEType().toLowerCase().startsWith("image/");
if (isImage) {
panel.enableOCRSelection(OCR_ENABLED);
panel.addLanguagePackNames(INSTALLED_LANGUAGE_PACKS);
}
} }
panel.enableOCRSelection(UserPreferences.getUseOcrInTranslation());
int payloadMaxInKB = TextTranslationService.getInstance().getMaxTextChars() / 1000; int payloadMaxInKB = TextTranslationService.getInstance().getMaxTextChars() / 1000;
panel.setWarningLabelMsg(String.format(Bundle.TranslatedTextViewer_maxPayloadSize(), payloadMaxInKB)); panel.setWarningLabelMsg(String.format(Bundle.TranslatedTextViewer_maxPayloadSize(), payloadMaxInKB));
@ -201,14 +193,16 @@ public final class TranslatedTextViewer implements TextViewer {
* @throws * @throws
* org.sleuthkit.autopsy.textextractors.TextExtractor.InitReaderException * org.sleuthkit.autopsy.textextractors.TextExtractor.InitReaderException
*/ */
@NbBundle.Messages({
"TranslatedContentViewer.ocrNotEnabled=OCR is not enabled. To change, go to Tools->Options->Machine Translation",
})
private String getFileText(AbstractFile file) throws IOException, InterruptedException, TextExtractor.InitReaderException { private String getFileText(AbstractFile file) throws IOException, InterruptedException, TextExtractor.InitReaderException {
final boolean isImage = file.getMIMEType().toLowerCase().startsWith("image/"); // NON-NLS final boolean isImage = file.getMIMEType().toLowerCase().startsWith("image/"); // NON-NLS
String result; if (isImage && ! UserPreferences.getUseOcrInTranslation()) {
if (isImage) { return Bundle.TranslatedContentViewer_ocrNotEnabled();
result = extractText(file, OCR_ENABLED);
} else {
result = extractText(file, OCR_DISABLED);
} }
String result = extractText(file, UserPreferences.getUseOcrInTranslation());
//Correct for UTF-8 //Correct for UTF-8
byte[] resultInUTF8Bytes = result.getBytes("UTF8"); byte[] resultInUTF8Bytes = result.getBytes("UTF8");
@ -363,4 +357,4 @@ public final class TranslatedTextViewer implements TextViewer {
return panel.getSelectedOcrLanguagePack(); return panel.getSelectedOcrLanguagePack();
} }
} }
} }

View File

@ -0,0 +1 @@
Netbeans platform does not properly scope classloaders while running qa-functional test code. The result is that NoClassDefError's occur in instances where an external jar (i.e. importing a class from common-io) is referenced in test code and the same external jar is referenced in multiple NBM's. Importing from external jars in qa-functional should be avoided. See jira issue 6954 for more information.

View File

@ -25,11 +25,10 @@ import java.nio.file.Paths;
import junit.framework.Assert; import junit.framework.Assert;
import junit.framework.TestCase; import junit.framework.TestCase;
import junit.framework.Test; import junit.framework.Test;
import org.apache.commons.io.FileUtils;
import org.netbeans.junit.NbModuleSuite; import org.netbeans.junit.NbModuleSuite;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.InvalidAccountIDException; import org.sleuthkit.datamodel.InvalidAccountIDException;
@ -95,7 +94,8 @@ public class CentralRepoAccountsTest extends TestCase {
if (CentralRepository.isEnabled()) { if (CentralRepository.isEnabled()) {
CentralRepository.getInstance().shutdownConnections(); CentralRepository.getInstance().shutdownConnections();
} }
FileUtils.deleteDirectory(testDirectory.toFile());
FileUtil.deleteDir(testDirectory.toFile());
} }
public void testPredefinedAccountTypes() { public void testPredefinedAccountTypes() {

View File

@ -35,7 +35,6 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import junit.framework.Test; import junit.framework.Test;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.commons.io.FileUtils;
import org.netbeans.junit.NbModuleSuite; import org.netbeans.junit.NbModuleSuite;
import org.openide.util.Exceptions; import org.openide.util.Exceptions;
import junit.framework.Assert; import junit.framework.Assert;
@ -47,6 +46,7 @@ import org.sleuthkit.datamodel.TskData;
import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.FileUtil;
/** /**
* Functional tests for the Central Repository data model. * Functional tests for the Central Repository data model.
@ -100,8 +100,8 @@ public class CentralRepoDatamodelTest extends TestCase {
if (CentralRepository.isEnabled()) { if (CentralRepository.isEnabled()) {
CentralRepository.getInstance().shutdownConnections(); CentralRepository.getInstance().shutdownConnections();
} }
FileUtils.deleteDirectory(testDirectory.toFile()); FileUtil.deleteDir(testDirectory.toFile());
} catch (IOException | CentralRepoException ex) { } catch (CentralRepoException ex) {
Assert.fail(ex.getMessage()); Assert.fail(ex.getMessage());
} }
} }
@ -194,8 +194,8 @@ public class CentralRepoDatamodelTest extends TestCase {
if (CentralRepository.isEnabled()) { if (CentralRepository.isEnabled()) {
CentralRepository.getInstance().shutdownConnections(); CentralRepository.getInstance().shutdownConnections();
} }
FileUtils.deleteDirectory(testDirectory.toFile()); FileUtil.deleteDir(testDirectory.toFile());
} catch (CentralRepoException | IOException ex) { } catch (CentralRepoException ex) {
Exceptions.printStackTrace(ex); Exceptions.printStackTrace(ex);
Assert.fail(ex.getMessage()); Assert.fail(ex.getMessage());
} }

View File

@ -28,10 +28,10 @@ import junit.framework.Assert;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
import junit.framework.TestCase; import junit.framework.TestCase;
import junit.framework.Test; import junit.framework.Test;
import org.apache.commons.io.FileUtils;
import org.netbeans.junit.NbModuleSuite; import org.netbeans.junit.NbModuleSuite;
import org.openide.util.Exceptions; import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.InvalidAccountIDException; import org.sleuthkit.datamodel.InvalidAccountIDException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
@ -257,12 +257,12 @@ public class CentralRepoPersonasTest extends TestCase {
// This function is run after every test, NOT after the entire collection of // This function is run after every test, NOT after the entire collection of
// tests defined in the class are run. // tests defined in the class are run.
@Override @Override
public void tearDown() throws CentralRepoException, IOException { public void tearDown() throws CentralRepoException {
// Close and delete the test case and central repo db // Close and delete the test case and central repo db
if (CentralRepository.isEnabled()) { if (CentralRepository.isEnabled()) {
CentralRepository.getInstance().shutdownConnections(); CentralRepository.getInstance().shutdownConnections();
} }
FileUtils.deleteDirectory(testDirectory.toFile()); FileUtil.deleteDir(testDirectory.toFile());
} }

View File

@ -28,7 +28,6 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.netbeans.junit.NbTestCase; import org.netbeans.junit.NbTestCase;
import org.openide.util.Exceptions; import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
@ -63,6 +62,7 @@ import org.sleuthkit.autopsy.modules.vmextractor.VMExtractorIngestModuleFactory;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepoFactory; import org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepoFactory;
import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.autopsy.modules.pictureanalyzer.PictureAnalyzerIngestModuleFactory; import org.sleuthkit.autopsy.modules.pictureanalyzer.PictureAnalyzerIngestModuleFactory;
/** /**
@ -247,8 +247,8 @@ class InterCaseTestUtils {
if (CentralRepository.isEnabled()) { if (CentralRepository.isEnabled()) {
CentralRepository.getInstance().shutdownConnections(); CentralRepository.getInstance().shutdownConnections();
} }
FileUtils.deleteDirectory(CENTRAL_REPO_DIRECTORY_PATH.toFile()); FileUtil.deleteDir(CENTRAL_REPO_DIRECTORY_PATH.toFile());
} catch (IOException | CentralRepoException ex) { } catch (CentralRepoException ex) {
Exceptions.printStackTrace(ex); Exceptions.printStackTrace(ex);
Assert.fail(ex.getMessage()); Assert.fail(ex.getMessage());
} }

View File

@ -134,7 +134,8 @@
<build-prerequisite/> <build-prerequisite/>
<compile-dependency/> <compile-dependency/>
<run-dependency> <run-dependency>
<specification-version>1.0</specification-version> <release-version>1</release-version>
<specification-version>23</specification-version>
</run-dependency> </run-dependency>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -118,7 +118,8 @@
<build-prerequisite/> <build-prerequisite/>
<compile-dependency/> <compile-dependency/>
<run-dependency> <run-dependency>
<specification-version>1.0</specification-version> <release-version>1</release-version>
<specification-version>23</specification-version>
</run-dependency> </run-dependency>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -1,6 +1,7 @@
Manifest-Version: 1.0 Manifest-Version: 1.0
AutoUpdate-Show-In-Client: true AutoUpdate-Show-In-Client: true
OpenIDE-Module: org.sleuthkit.autopsy.Tika OpenIDE-Module: org.sleuthkit.autopsy.Tika/1
OpenIDE-Module-Implementation-Version: 1
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/Tika/Bundle.properties OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/Tika/Bundle.properties
OpenIDE-Module-Specification-Version: 1.0 OpenIDE-Module-Specification-Version: 23

3
ZookeeperNodeMigration/.gitignore vendored Executable file
View File

@ -0,0 +1,3 @@
/nbproject/private/
/build/

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="ZookeeperNodeMigration" default="default" basedir=".">
<description>Builds, tests, and runs the project ZookeeperNodeMigration.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="ZookeeperNodeMigration-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>

32
ZookeeperNodeMigration/dist/README.TXT vendored Executable file
View File

@ -0,0 +1,32 @@
========================
BUILD OUTPUT DESCRIPTION
========================
When you build an Java application project that has a main class, the IDE
automatically copies all of the JAR
files on the projects classpath to your projects dist/lib folder. The IDE
also adds each of the JAR files to the Class-Path element in the application
JAR files manifest file (MANIFEST.MF).
To run the project from the command line, go to the dist folder and
type the following:
java -jar "ZookeeperNodeMigration.jar"
To distribute this project, zip up the dist folder (including the lib folder)
and distribute the ZIP file.
Notes:
* If two JAR files on the project classpath have the same name, only the first
JAR file is copied to the lib folder.
* Only JAR files are copied to the lib folder.
If the classpath contains other types of files or folders, these files (folders)
are not copied.
* If a library on the projects classpath also has a Class-Path element
specified in the manifest,the content of the Class-Path element has to be on
the projects runtime path.
* To set a main class in a standard Java project, right-click the project node
in the Projects window and choose Properties. Then click Run and enter the
class name in the Main Class field. Alternatively, you can manually type the
class name in the manifest Main-Class element.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
ZookeeperNodeMigration/dist/lib/guava-17.0.jar vendored Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,23 @@
To run the project from the command line, go to the folder that contains "ZookeeperNodeMigration.jar" and
type the following:
java -jar ZookeeperNodeMigration.jar
To distribute this project, zip up the dist folder (including the lib folder)
and distribute the ZIP file.
Usage:
ZookeeperNodeMigration input needs to be:
[Input Zookeeper IP Address or Hostname] [Input Zookeeper Port Number] [Output Zookeeper IP Address or Hostname] [Output Zookeeper Port Number]
For example, if you execute the following command from command line line, the Zookeeper
nodes will get copied from Zookeeper server on localhost:9983 to Zookeeper server on localhost:19983 :
java -jar ZookeeperNodeMigration.jar localhost 9983 localhost 19983
If you do not have Java installed on the machine, you can use the packaged version of Java that is distributed along
with Autopsy. For example:
"C:\Program Files\Autopsy-4.16.0\jre\bin\java.exe" -jar ZookeeperNodeMigration.jar localhost 9983 localhost 19983

View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,107 @@
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
application.title=ZookeeperNodeMigration
application.vendor=elivis
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
build.dir=build
build.generated.dir=${build.dir}/generated
build.generated.sources.dir=${build.dir}/generated-sources
# Only compile against the classpath explicitly listed here:
build.sysclasspath=ignore
build.test.classes.dir=${build.dir}/test/classes
build.test.results.dir=${build.dir}/test/results
# Uncomment to specify the preferred debugger connection transport:
#debug.transport=dt_socket
debug.classpath=\
${run.classpath}
debug.modulepath=\
${run.modulepath}
debug.test.classpath=\
${run.test.classpath}
debug.test.modulepath=\
${run.test.modulepath}
# Files in build.classes.dir which should be excluded from distribution jar
dist.archive.excludes=
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=${dist.dir}/ZookeeperNodeMigration.jar
dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
file.reference.curator-client-2.8.0.jar=release/curator-client-2.8.0.jar
file.reference.curator-framework-2.8.0.jar=release/curator-framework-2.8.0.jar
file.reference.curator-recipes-2.8.0.jar=release/curator-recipes-2.8.0.jar
file.reference.guava-17.0.jar=release/guava-17.0.jar
file.reference.log4j-1.2.17.jar=release/log4j-1.2.17.jar
file.reference.slf4j-api-1.7.24.jar=release/slf4j-api-1.7.24.jar
file.reference.slf4j-log4j12-1.7.6.jar=release/slf4j-log4j12-1.7.6.jar
file.reference.zookeeper-3.4.6.jar=release/zookeeper-3.4.6.jar
includes=**
jar.compress=false
javac.classpath=\
${file.reference.curator-client-2.8.0.jar}:\
${file.reference.curator-framework-2.8.0.jar}:\
${file.reference.curator-recipes-2.8.0.jar}:\
${file.reference.zookeeper-3.4.6.jar}:\
${file.reference.slf4j-api-1.7.24.jar}:\
${file.reference.slf4j-log4j12-1.7.6.jar}:\
${file.reference.log4j-1.2.17.jar}:\
${file.reference.guava-17.0.jar}
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
javac.external.vm=true
javac.modulepath=
javac.processormodulepath=
javac.processorpath=\
${javac.classpath}
javac.source=1.8
javac.target=1.8
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}
javac.test.modulepath=\
${javac.modulepath}
javac.test.processorpath=\
${javac.test.classpath}
javadoc.additionalparam=
javadoc.author=false
javadoc.encoding=${source.encoding}
javadoc.html5=false
javadoc.noindex=false
javadoc.nonavbar=false
javadoc.notree=false
javadoc.private=false
javadoc.splitindex=true
javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
jlink.launcher=false
jlink.launcher.name=ZookeeperNodeMigration
main.class=zookeepernodemigration.ZookeeperNodeMigration
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=false
platform.active=default_platform
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
# Space-separated list of JVM arguments used when running the project.
# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
# To set system properties for unit tests define test-sys-prop.name=value:
run.jvmargs=
run.modulepath=\
${javac.modulepath}
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}
run.test.modulepath=\
${javac.test.modulepath}
source.encoding=UTF-8
src.dir=src
test.src.dir=test

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>ZookeeperNodeMigration</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
</configuration>
</project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,687 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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 zookeepernodemigration;
import java.io.Serializable;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import javax.lang.model.type.TypeKind;
/**
* An object that converts auto ingest job data for an auto ingest job
* coordination service node to and from byte arrays.
*/
final class AutoIngestJobNodeData {
private static final int CURRENT_VERSION = 2;
private static final int DEFAULT_PRIORITY = 0;
/*
* This number is the sum of each piece of data, based on it's type. For the
* types boolean, int, and long, values 1, 4, and 8 will be added
* respectively. For String objects, the length of the string, plus either a
* byte or short respesenting the length of the string, will be added.
*
* This field is used to set the size of the buffer during the byte array
* creation in the 'toArray()' method. Since the final size of the array
* isn't immediately known at the time of creation, this number is used to
* create an array as large as could possibly be needed to store all the
* data. This avoids the need to continuously enlarge the buffer. Once the
* buffer has all the necessary data, it will be resized as appropriate.
*/
private static final int MAX_POSSIBLE_NODE_DATA_SIZE = 131637;
/*
* Version 0 fields.
*/
private int processingStatus;
private int priority;
private int numberOfCrashes;
private long completedDate;
private boolean errorsOccurred;
/*
* Version 1 fields.
*/
private int version;
private String manifestFilePath; // 'short' length used in byte array
private long manifestFileDate;
private String caseName; // 'byte' length used in byte array
private String deviceId; // 'byte' length used in byte array
private String dataSourcePath; // 'short' length used in byte array
private String caseDirectoryPath; // 'short' length used in byte array
private String processingHostName; // 'short' length used in byte array
private byte processingStage;
private long processingStageStartDate;
private String processingStageDetailsDescription; // 'byte' length used in byte array
private long processingStageDetailsStartDate;
/*
* Version 2 fields.
*/
private long dataSourceSize;
/**
* Processing statuses for an auto ingest job.
*/
enum ProcessingStatus {
PENDING,
PROCESSING,
COMPLETED,
DELETED
}
/**
* Processing stages for an auto ingest job.
*/
enum Stage {
PENDING("Pending"),
STARTING("Starting"),
UPDATING_SHARED_CONFIG("Updating shared configuration"),
CHECKING_SERVICES("Checking services"),
OPENING_CASE("Opening case"),
IDENTIFYING_DATA_SOURCE("Identifying data source type"),
ADDING_DATA_SOURCE("Adding data source"),
ANALYZING_DATA_SOURCE("Analyzing data source"),
ANALYZING_FILES("Analyzing files"),
EXPORTING_FILES("Exporting files"),
CANCELLING_MODULE("Cancelling module"),
CANCELLING("Cancelling"),
COMPLETED("Completed");
private final String displayText;
private Stage(String displayText) {
this.displayText = displayText;
}
String getDisplayText() {
return displayText;
}
}
/**
* Processing stage details for an auto ingest job.
*/
static final class StageDetails implements Serializable {
private static final long serialVersionUID = 1L;
private final String description;
private final Date startDate;
StageDetails(String description, Date startDate) {
this.description = description;
this.startDate = startDate;
}
String getDescription() {
return this.description;
}
Date getStartDate() {
return new Date(this.startDate.getTime());
}
}
/**
* Gets the current version of the auto ingest job coordination service node
* data.
*
* @return The version number.
*/
static int getCurrentVersion() {
return AutoIngestJobNodeData.CURRENT_VERSION;
}
/**
* Uses a coordination service node data to construct an object that
* converts auto ingest job data for an auto ingest job coordination service
* node to and from byte arrays.
*
* @param nodeData The raw bytes received from the coordination service.
*/
AutoIngestJobNodeData(byte[] nodeData) throws InvalidDataException {
if (null == nodeData || nodeData.length == 0) {
throw new InvalidDataException(null == nodeData ? "Null nodeData byte array" : "Zero-length nodeData byte array");
}
/*
* Set default values for all fields.
*/
this.processingStatus = ProcessingStatus.PENDING.ordinal();
this.priority = DEFAULT_PRIORITY;
this.numberOfCrashes = 0;
this.completedDate = 0L;
this.errorsOccurred = false;
this.version = 0;
this.manifestFilePath = "";
this.manifestFileDate = 0L;
this.caseName = "";
this.deviceId = "";
this.dataSourcePath = "";
this.caseDirectoryPath = "";
this.processingHostName = "";
this.processingStage = (byte) Stage.PENDING.ordinal();
this.processingStageStartDate = 0L;
this.processingStageDetailsDescription = "";
this.processingStageDetailsStartDate = 0L;
this.dataSourceSize = 0L;
/*
* Get fields from node data.
*/
ByteBuffer buffer = ByteBuffer.wrap(nodeData);
try {
if (buffer.hasRemaining()) {
/*
* Get version 0 fields.
*/
this.processingStatus = buffer.getInt();
this.priority = buffer.getInt();
this.numberOfCrashes = buffer.getInt();
this.completedDate = buffer.getLong();
int errorFlag = buffer.getInt();
this.errorsOccurred = (1 == errorFlag);
}
if (buffer.hasRemaining()) {
/*
* Get version 1 fields.
*/
this.version = buffer.getInt();
this.deviceId = getStringFromBuffer(buffer, TypeKind.BYTE);
this.caseName = getStringFromBuffer(buffer, TypeKind.BYTE);
this.caseDirectoryPath = getStringFromBuffer(buffer, TypeKind.SHORT);
this.manifestFileDate = buffer.getLong();
this.manifestFilePath = getStringFromBuffer(buffer, TypeKind.SHORT);
this.dataSourcePath = getStringFromBuffer(buffer, TypeKind.SHORT);
this.processingStage = buffer.get();
this.processingStageStartDate = buffer.getLong();
this.processingStageDetailsDescription = getStringFromBuffer(buffer, TypeKind.BYTE);
this.processingStageDetailsStartDate = buffer.getLong();
this.processingHostName = getStringFromBuffer(buffer, TypeKind.SHORT);
}
if (buffer.hasRemaining()) {
/*
* Get version 2 fields.
*/
this.dataSourceSize = buffer.getLong();
}
} catch (BufferUnderflowException ex) {
throw new InvalidDataException("Node data is incomplete", ex);
}
}
/**
* Gets the processing status of the job.
*
* @return The processing status.
*/
ProcessingStatus getProcessingStatus() {
return ProcessingStatus.values()[this.processingStatus];
}
/**
* Sets the processing status of the job.
*
* @param processingSatus The processing status.
*/
void setProcessingStatus(ProcessingStatus processingStatus) {
this.processingStatus = processingStatus.ordinal();
}
/**
* Gets the priority of the job.
*
* @return The priority.
*/
int getPriority() {
return this.priority;
}
/**
* Sets the priority of the job. A higher number indicates a higheer
* priority.
*
* @param priority The priority.
*/
void setPriority(int priority) {
this.priority = priority;
}
/**
* Gets the number of times the job has crashed during processing.
*
* @return The number of crashes.
*/
int getNumberOfCrashes() {
return this.numberOfCrashes;
}
/**
* Sets the number of times the job has crashed during processing.
*
* @param numberOfCrashes The number of crashes.
*/
void setNumberOfCrashes(int numberOfCrashes) {
this.numberOfCrashes = numberOfCrashes;
}
/**
* Gets the date the job was completed. A completion date equal to the epoch
* (January 1, 1970, 00:00:00 GMT), i.e., Date.getTime() returns 0L,
* indicates the job has not been completed.
*
* @return The job completion date.
*/
Date getCompletedDate() {
return new Date(this.completedDate);
}
/**
* Sets the date the job was completed. A completion date equal to the epoch
* (January 1, 1970, 00:00:00 GMT), i.e., Date.getTime() returns 0L,
* indicates the job has not been completed.
*
* @param completedDate The job completion date.
*/
void setCompletedDate(Date completedDate) {
this.completedDate = completedDate.getTime();
}
/**
* Gets whether or not any errors occurred during the processing of the job.
*
* @return True or false.
*/
boolean getErrorsOccurred() {
return this.errorsOccurred;
}
/**
* Sets whether or not any errors occurred during the processing of job.
*
* @param errorsOccurred True or false.
*/
void setErrorsOccurred(boolean errorsOccurred) {
this.errorsOccurred = errorsOccurred;
}
/**
* Gets the node data version number.
*
* @return The version number.
*/
int getVersion() {
return this.version;
}
/**
* Gets the device ID of the device associated with the data source for the
* job.
*
* @return The device ID.
*/
String getDeviceId() {
return this.deviceId;
}
/**
* Sets the device ID of the device associated with the data source for the
* job.
*
* @param deviceId The device ID.
*/
void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
/**
* Gets the case name.
*
* @return The case name.
*/
String getCaseName() {
return this.caseName;
}
/**
* Sets the case name.
*
* @param caseName The case name.
*/
void setCaseName(String caseName) {
this.caseName = caseName;
}
/**
* Sets the path to the case directory of the case associated with the job.
*
* @param caseDirectoryPath The path to the case directory.
*/
synchronized void setCaseDirectoryPath(Path caseDirectoryPath) {
if (caseDirectoryPath == null) {
this.caseDirectoryPath = "";
} else {
this.caseDirectoryPath = caseDirectoryPath.toString();
}
}
/**
* Gets the path to the case directory of the case associated with the job.
*
* @return The case directory path or an empty string path if the case
* directory has not been created yet.
*/
synchronized Path getCaseDirectoryPath() {
if (!caseDirectoryPath.isEmpty()) {
return Paths.get(caseDirectoryPath);
} else {
return Paths.get("");
}
}
/**
* Gets the date the manifest was created.
*
* @return The date the manifest was created.
*/
Date getManifestFileDate() {
return new Date(this.manifestFileDate);
}
/**
* Sets the date the manifest was created.
*
* @param manifestFileDate The date the manifest was created.
*/
void setManifestFileDate(Date manifestFileDate) {
this.manifestFileDate = manifestFileDate.getTime();
}
/**
* Gets the manifest file path.
*
* @return The manifest file path.
*/
Path getManifestFilePath() {
return Paths.get(this.manifestFilePath);
}
/**
* Sets the manifest file path.
*
* @param manifestFilePath The manifest file path.
*/
void setManifestFilePath(Path manifestFilePath) {
if (manifestFilePath != null) {
this.manifestFilePath = manifestFilePath.toString();
} else {
this.manifestFilePath = "";
}
}
/**
* Gets the path of the data source for the job.
*
* @return The data source path.
*/
Path getDataSourcePath() {
return Paths.get(dataSourcePath);
}
/**
* Get the file name portion of the path of the data source for the job.
*
* @return The data source file name.
*/
public String getDataSourceFileName() {
return Paths.get(dataSourcePath).getFileName().toString();
}
/**
* Sets the path of the data source for the job.
*
* @param dataSourcePath The data source path.
*/
void setDataSourcePath(Path dataSourcePath) {
if (dataSourcePath != null) {
this.dataSourcePath = dataSourcePath.toString();
} else {
this.dataSourcePath = "";
}
}
/**
* Get the processing stage of the job.
*
* @return The processing stage.
*/
Stage getProcessingStage() {
return Stage.values()[this.processingStage];
}
/**
* Sets the processing stage job.
*
* @param processingStage The processing stage.
*/
void setProcessingStage(Stage processingStage) {
this.processingStage = (byte) processingStage.ordinal();
}
/**
* Gets the processing stage start date.
*
* @return The processing stage start date.
*/
Date getProcessingStageStartDate() {
return new Date(this.processingStageStartDate);
}
/**
* Sets the processing stage start date.
*
* @param processingStageStartDate The processing stage start date.
*/
void setProcessingStageStartDate(Date processingStageStartDate) {
this.processingStageStartDate = processingStageStartDate.getTime();
}
/**
* Get the processing stage details.
*
* @return A processing stage details object.
*/
StageDetails getProcessingStageDetails() {
return new StageDetails(this.processingStageDetailsDescription, new Date(this.processingStageDetailsStartDate));
}
/**
* Sets the details of the current processing stage.
*
* @param stageDetails A stage details object.
*/
void setProcessingStageDetails(StageDetails stageDetails) {
this.processingStageDetailsDescription = stageDetails.getDescription();
this.processingStageDetailsStartDate = stageDetails.getStartDate().getTime();
}
/**
* Gets the processing host name, may be the empty string.
*
* @return The processing host. The empty string if the job is not currently
* being processed.
*/
String getProcessingHostName() {
return this.processingHostName;
}
/**
* Sets the processing host name. May be the empty string.
*
* @param processingHost The processing host name. The empty string if the
* job is not currently being processed.
*/
void setProcessingHostName(String processingHost) {
this.processingHostName = processingHost;
}
/**
* Gets the total size of the data source.
*
* @return The data source size.
*/
long getDataSourceSize() {
return this.dataSourceSize;
}
/**
* Sets the total size of the data source.
*
* @param dataSourceSize The data source size.
*/
void setDataSourceSize(long dataSourceSize) {
this.dataSourceSize = dataSourceSize;
}
/**
* Gets the node data as a byte array that can be sent to the coordination
* service.
*
* @return The node data as a byte array.
*/
byte[] toArray() {
ByteBuffer buffer = ByteBuffer.allocate(MAX_POSSIBLE_NODE_DATA_SIZE);
// Write data (compatible with version 0)
buffer.putInt(this.processingStatus);
buffer.putInt(this.priority);
buffer.putInt(this.numberOfCrashes);
buffer.putLong(this.completedDate);
buffer.putInt(this.errorsOccurred ? 1 : 0);
if (this.version >= 1) {
// Write version
buffer.putInt(this.version);
// Write data
putStringIntoBuffer(deviceId, buffer, TypeKind.BYTE);
putStringIntoBuffer(caseName, buffer, TypeKind.BYTE);
putStringIntoBuffer(caseDirectoryPath, buffer, TypeKind.SHORT);
buffer.putLong(this.manifestFileDate);
putStringIntoBuffer(manifestFilePath, buffer, TypeKind.SHORT);
putStringIntoBuffer(dataSourcePath, buffer, TypeKind.SHORT);
buffer.put(this.processingStage);
buffer.putLong(this.processingStageStartDate);
putStringIntoBuffer(this.processingStageDetailsDescription, buffer, TypeKind.BYTE);
buffer.putLong(this.processingStageDetailsStartDate);
putStringIntoBuffer(processingHostName, buffer, TypeKind.SHORT);
if (this.version >= 2) {
buffer.putLong(this.dataSourceSize);
}
}
// Prepare the array
byte[] array = new byte[buffer.position()];
buffer.rewind();
buffer.get(array, 0, array.length);
return array;
}
/**
* This method retrieves a string from a given buffer. Depending on the type
* specified, either a 'byte' or a 'short' will first be read out of the
* buffer which gives the length of the string so it can be properly parsed.
*
* @param buffer The buffer from which the string will be read.
* @param lengthType The size of the length data.
*
* @return The string read from the buffer.
*/
private String getStringFromBuffer(ByteBuffer buffer, TypeKind lengthType) {
int length = 0;
String output = "";
switch (lengthType) {
case BYTE:
length = buffer.get();
break;
case SHORT:
length = buffer.getShort();
break;
}
if (length > 0) {
byte[] array = new byte[length];
buffer.get(array, 0, length);
output = new String(array);
}
return output;
}
/**
* This method puts a given string into a given buffer. Depending on the
* type specified, either a 'byte' or a 'short' will be inserted prior to
* the string which gives the length of the string so it can be properly
* parsed.
*
* @param stringValue The string to write to the buffer.
* @param buffer The buffer to which the string will be written.
* @param lengthType The size of the length data.
*/
private void putStringIntoBuffer(String stringValue, ByteBuffer buffer, TypeKind lengthType) {
switch (lengthType) {
case BYTE:
buffer.put((byte) stringValue.length());
break;
case SHORT:
buffer.putShort((short) stringValue.length());
break;
}
buffer.put(stringValue.getBytes());
}
final static class InvalidDataException extends Exception {
private static final long serialVersionUID = 1L;
private InvalidDataException(String message) {
super(message);
}
private InvalidDataException(String message, Throwable cause) {
super(message, cause);
}
}
}

View File

@ -0,0 +1,93 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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 zookeepernodemigration;
import java.text.SimpleDateFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility methods for working with time stamps of the form
* 'yyyy_MM_dd_HH_mm_ss'.
*/
final class TimeStampUtils {
/*
* Sample time stamp suffix: 2015_02_02_12_10_31
*/
private static final Pattern TIME_STAMP_PATTERN = Pattern.compile("\\d{4}_\\d{2}_\\d{2}_\\d{2}_\\d{2}_\\d{2}$");
private static final int LENGTH_OF_DATE_TIME_STAMP = 20; // length of the above time stamp
/**
* Checks whether a string ends with a time stamp.
*
* @param inputString The string to check.
*
* @return True or false.
*/
static boolean endsWithTimeStamp(String inputString) {
Matcher m = TIME_STAMP_PATTERN.matcher(inputString);
return m.find();
}
/**
* Gets the fixed length of the time stamp suffix.
*
* @return The length.
*/
static int getTimeStampLength() {
return LENGTH_OF_DATE_TIME_STAMP;
}
/**
* Removes the time stamp suffix from a string, if present.
*
* @param inputString The string to trim.
*
* @return The trimmed string.
*/
static String removeTimeStamp(String inputString) {
String trimmedString = inputString;
if (inputString != null && endsWithTimeStamp(inputString)) {
trimmedString = inputString.substring(0, inputString.length() - getTimeStampLength());
}
return trimmedString;
}
/**
* Gets the time stamp suffix from a string, if present.
*
* @param inputString the name to check for a timestamp
*
* @return The time stamp, may be the empty.
*/
static String getTimeStampOnly(String inputString) {
String timeStamp = "";
if (inputString != null && endsWithTimeStamp(inputString)) {
timeStamp = inputString.substring(inputString.length() - getTimeStampLength(), inputString.length());
}
return timeStamp;
}
/*
* Private contructor to prevent instantiation.
*/
private TimeStampUtils() {
}
}

View File

@ -0,0 +1,464 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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 zookeepernodemigration;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.KeeperException.NoNodeException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
/**
* Utility to migrate Autopsy coordination service data from one ZK database to
* another.
*/
public class ZookeeperNodeMigration {
private static final Logger LOGGER = Logger.getLogger(ZookeeperNodeMigration.class.getName());
private static final int SESSION_TIMEOUT_MILLISECONDS = 300000;
private static final int CONNECTION_TIMEOUT_MILLISECONDS = 300000;
private static final int ZOOKEEPER_SESSION_TIMEOUT_MILLIS = 3000;
private static final int ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS = 15000;
private static final String DEFAULT_NAMESPACE_ROOT = "autopsy";
private static CuratorFramework inputCurator;
private static CuratorFramework outputCurator;
private static Map<String, String> categoryNodeToPath;
private ZookeeperNodeMigration(){
}
/**
* Main method.
*
* @param args the command line arguments
*/
public static void main(String[] args) {
String inputZkIpAddr, inputZkPort, outputZkIpAddr, outputZkPort;
if (args.length == 4) {
inputZkIpAddr = args[0];
inputZkPort = args[1];
outputZkIpAddr = args[2];
outputZkPort = args[3];
} else {
System.out.println("Input needs to be [Input Zookeeper IP Address] [Input Zookeeper Port Number] [Output Zookeeper IP Address] [Output Zookeeper Port Number]");
LOGGER.log(Level.SEVERE, "Input needs to be [Input Zookeeper IP Address] [Input Zookeeper Port Number] [Output Zookeeper IP Address] [Output Zookeeper Port Number]");
return;
}
if (inputZkIpAddr.isEmpty() || inputZkPort.isEmpty() || outputZkIpAddr.isEmpty() || outputZkPort.isEmpty()) {
System.out.println("Input needs to be [Input Zookeeper IP Address] [Input Zookeeper Port Number] [Output Zookeeper IP Address] [Output Zookeeper Port Number]");
LOGGER.log(Level.SEVERE, "Input needs to be [Input Zookeeper IP Address] [Input Zookeeper Port Number] [Output Zookeeper IP Address] [Output Zookeeper Port Number]");
return;
}
inputCurator = initializeCurator(inputZkIpAddr, inputZkPort);
if (inputCurator == null) {
System.out.println("Unable to initialize Zookeeper or Curator: " + inputZkIpAddr + ":" + inputZkPort);
LOGGER.log(Level.SEVERE, "Unable to initialize Zookeeper or Curator: {0}:{1}", new Object[]{inputZkIpAddr, inputZkPort});
return;
}
try {
categoryNodeToPath = populateCategoryNodes(inputCurator);
} catch (KeeperException | CoordinationServiceException ex) {
System.out.println("Unable to initialize Curator: " + inputZkIpAddr + ":" + inputZkPort);
LOGGER.log(Level.SEVERE, "Unable to initialize Curator: {0}:{1}", new Object[]{inputZkIpAddr, inputZkPort});
return;
}
outputCurator = initializeCurator(outputZkIpAddr, outputZkPort);
if (outputCurator == null) {
System.out.println("Unable to initialize Zookeeper or Curator: " + outputZkIpAddr + ":" + outputZkPort);
LOGGER.log(Level.SEVERE, "Unable to initialize Zookeeper or Curator: {0}:{1}", new Object[]{outputZkIpAddr, outputZkPort});
return;
}
try {
// if output ZK database is new, we may have to ceate root "autopsy" node and it's sub-nodes
populateCategoryNodes(outputCurator);
} catch (KeeperException | CoordinationServiceException ex) {
System.out.println("Unable to initialize Curator: " + outputZkIpAddr + ":" + outputZkPort);
LOGGER.log(Level.SEVERE, "Unable to initialize Curator: {0}:{1}", new Object[]{outputZkIpAddr, outputZkPort});
return;
}
copyAllCategoryNodes();
System.out.println("Done...");
}
/**
* Copy all Autopsy coordination service nodes from one ZK database to
* another.
*/
private static void copyAllCategoryNodes() {
for (CategoryNode category : CategoryNode.values()) {
List<String> inputNodeList = Collections.EMPTY_LIST;
try {
inputNodeList = getNodeList(category);
} catch (CoordinationServiceException ex) {
System.out.println("Unable to get ZK nodes for category: " + category.getDisplayName());
LOGGER.log(Level.SEVERE, "Unable to get ZK nodes for category: " + category.getDisplayName(), ex);
continue;
}
for (String zkNode : inputNodeList) {
try {
final byte[] nodeBytes = getNodeData(category, zkNode);
try (Lock manifestLock = tryGetExclusiveLock(outputCurator, category, zkNode)) {
setNodeData(outputCurator, category, zkNode, nodeBytes);
}
} catch (CoordinationServiceException | InterruptedException ex) {
System.out.println("Unable to write ZK node data for node: " + zkNode);
LOGGER.log(Level.SEVERE, "Unable to write ZK node data for node: " + zkNode, ex);
continue;
}
}
}
}
/**
* Initialize Curator framework.
*
* @param zkIpAddr Zookeeper server IP address.
* @param zookeeperPort Zookeeper server port number.
*
* @return CuratorFramework object
*/
private static CuratorFramework initializeCurator(String zkIpAddr, String zookeeperPort) {
try {
if (!isZooKeeperAccessible(zkIpAddr, zookeeperPort)) {
System.out.println("Unable to connect to Zookeeper");
LOGGER.log(Level.SEVERE, "Unable to connect to Zookeeper");
return null;
}
} catch (InterruptedException | IOException ex) {
System.out.println("Unable to connect to Zookeeper");
LOGGER.log(Level.SEVERE, "Unable to connect to Zookeeper", ex);
return null;
}
/*
* Connect to ZooKeeper via Curator.
*/
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
String connectString = zkIpAddr + ":" + zookeeperPort;
CuratorFramework curator = CuratorFrameworkFactory.newClient(connectString, SESSION_TIMEOUT_MILLISECONDS, CONNECTION_TIMEOUT_MILLISECONDS, retryPolicy);
curator.start();
return curator;
}
/*
* Creates Autopsy coordination service root ZK nodes.
*/
private static Map<String, String> populateCategoryNodes(CuratorFramework curator) throws KeeperException, CoordinationServiceException {
/*
* Create the top-level root and category nodes.
*/
String rootNodeName = DEFAULT_NAMESPACE_ROOT;
String rootNode = rootNodeName;
if (!rootNode.startsWith("/")) {
rootNode = "/" + rootNode;
}
Map<String, String> categoryPaths = new ConcurrentHashMap<>();
for (CategoryNode node : CategoryNode.values()) {
String nodePath = rootNode + "/" + node.getDisplayName();
try {
curator.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE).forPath(nodePath);
} catch (KeeperException ex) {
if (ex.code() != KeeperException.Code.NODEEXISTS) {
throw ex;
}
} catch (Exception ex) {
throw new CoordinationServiceException("Curator experienced an error", ex);
}
categoryPaths.put(node.getDisplayName(), nodePath);
}
return categoryPaths;
}
/**
* Determines if ZooKeeper is accessible with the current settings. Closes
* the connection prior to returning.
*
* @return true if a connection was achieved, false otherwise
*
* @throws InterruptedException
* @throws IOException
*/
private static boolean isZooKeeperAccessible(String solrIpAddr, String zookeeperPort) throws InterruptedException, IOException {
boolean result = false;
Object workerThreadWaitNotifyLock = new Object();
String connectString = solrIpAddr + ":" + zookeeperPort;
ZooKeeper zooKeeper = new ZooKeeper(connectString, ZOOKEEPER_SESSION_TIMEOUT_MILLIS,
(WatchedEvent event) -> {
synchronized (workerThreadWaitNotifyLock) {
workerThreadWaitNotifyLock.notifyAll();
}
});
synchronized (workerThreadWaitNotifyLock) {
workerThreadWaitNotifyLock.wait(ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS);
}
ZooKeeper.States state = zooKeeper.getState();
if (state == ZooKeeper.States.CONNECTED || state == ZooKeeper.States.CONNECTEDREADONLY) {
result = true;
}
zooKeeper.close();
return result;
}
/**
* Tries to get an exclusive lock on a node path appended to a category path
* in the namespace managed by this coordination service. Returns
* immediately if the lock can not be acquired.
*
* IMPORTANT: The lock needs to be released in the same thread in which it
* is acquired.
*
* @param category The desired category in the namespace.
* @param nodePath The node path to use as the basis for the lock.
*
* @return The lock, or null if the lock could not be obtained.
*
* @throws CoordinationServiceException If there is an error during lock
* acquisition.
*/
private static Lock tryGetExclusiveLock(CuratorFramework curator, CategoryNode category, String nodePath) throws CoordinationServiceException {
String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
try {
InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
if (!lock.writeLock().acquire(0, TimeUnit.SECONDS)) {
return null;
}
return new Lock(nodePath, lock.writeLock());
} catch (Exception ex) {
throw new CoordinationServiceException(String.format("Failed to get exclusive lock for %s", fullNodePath), ex);
}
}
/**
* Retrieve the data associated with the specified node.
*
* @param category The desired category in the namespace.
* @param nodePath The node to retrieve the data for.
*
* @return The data associated with the node, if any, or null if the node
* has not been created yet.
*
* @throws CoordinationServiceException If there is an error setting the
* node data.
* @throws InterruptedException If interrupted while blocked during
* setting of node data.
*/
private static byte[] getNodeData(CategoryNode category, String nodePath) throws CoordinationServiceException, InterruptedException {
String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
try {
return inputCurator.getData().forPath(fullNodePath);
} catch (NoNodeException ex) {
return null;
} catch (Exception ex) {
if (ex instanceof InterruptedException) {
throw (InterruptedException) ex;
} else {
throw new CoordinationServiceException(String.format("Failed to get data for %s", fullNodePath), ex);
}
}
}
/**
* Store the given data with the specified node.
*
* @param category The desired category in the namespace.
* @param nodePath The node to associate the data with.
* @param data The data to store with the node.
*
* @throws CoordinationServiceException If there is an error setting the
* node data.
* @throws InterruptedException If interrupted while blocked during
* setting of node data.
*/
private static void setNodeData(CuratorFramework curator, CategoryNode category, String nodePath, byte[] data) throws CoordinationServiceException, InterruptedException {
String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
try {
curator.setData().forPath(fullNodePath, data);
} catch (Exception ex) {
if (ex instanceof InterruptedException) {
throw (InterruptedException) ex;
} else {
throw new CoordinationServiceException(String.format("Failed to set data for %s", fullNodePath), ex);
}
}
}
/**
* Delete the specified node.
*
* @param category The desired category in the namespace.
* @param nodePath The node to delete.
*
* @throws CoordinationServiceException If there is an error setting the
* node data.
* @throws InterruptedException If interrupted while blocked during
* setting of node data.
*/
private static void deleteNode(CategoryNode category, String nodePath) throws CoordinationServiceException, InterruptedException {
String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
try {
inputCurator.delete().forPath(fullNodePath);
} catch (Exception ex) {
if (ex instanceof InterruptedException) {
throw (InterruptedException) ex;
} else {
throw new CoordinationServiceException(String.format("Failed to set data for %s", fullNodePath), ex);
}
}
}
/**
* Gets a list of the child nodes of a category in the namespace.
*
* @param category The desired category in the namespace.
*
* @return A list of child node names.
*
* @throws CoordinationServiceException If there is an error getting the
* node list.
*/
private static List<String> getNodeList(CategoryNode category) throws CoordinationServiceException {
try {
List<String> list = inputCurator.getChildren().forPath(categoryNodeToPath.get(category.getDisplayName()));
return list;
} catch (Exception ex) {
throw new CoordinationServiceException(String.format("Failed to get node list for %s", category.getDisplayName()), ex);
}
}
/**
* Creates a node path within a given category.
*
* @param category A category node.
* @param nodePath A node path relative to a category node path.
*
* @return
*/
private static String getFullyQualifiedNodePath(CategoryNode category, String nodePath) {
// nodePath on Unix systems starts with a "/" and ZooKeeper doesn't like two slashes in a row
if (nodePath.startsWith("/")) {
return categoryNodeToPath.get(category.getDisplayName()) + nodePath.toUpperCase();
} else {
return categoryNodeToPath.get(category.getDisplayName()) + "/" + nodePath.toUpperCase();
}
}
/**
* Exception type thrown by the coordination service.
*/
private final static class CoordinationServiceException extends Exception {
private static final long serialVersionUID = 1L;
private CoordinationServiceException(String message) {
super(message);
}
private CoordinationServiceException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* An opaque encapsulation of a lock for use in distributed synchronization.
* Instances are obtained by calling a get lock method and must be passed to
* a release lock method.
*/
private static class Lock implements AutoCloseable {
/**
* This implementation uses the Curator read/write lock. see
* http://curator.apache.org/curator-recipes/shared-reentrant-read-write-lock.html
*/
private final InterProcessMutex interProcessLock;
private final String nodePath;
private Lock(String nodePath, InterProcessMutex lock) {
this.nodePath = nodePath;
this.interProcessLock = lock;
}
public String getNodePath() {
return nodePath;
}
public void release() throws CoordinationServiceException {
try {
this.interProcessLock.release();
} catch (Exception ex) {
throw new CoordinationServiceException(String.format("Failed to release the lock on %s", nodePath), ex);
}
}
@Override
public void close() throws CoordinationServiceException {
release();
}
}
/**
* Category nodes are the immediate children of the root node of a shared
* hierarchical namespace managed by a coordination service.
*/
public enum CategoryNode {
CASES("cases"),
MANIFESTS("manifests"),
CONFIG("config"),
CENTRAL_REPO("centralRepository"),
HEALTH_MONITOR("healthMonitor");
private final String displayName;
private CategoryNode(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}
}

View File

@ -106,8 +106,7 @@
<copy file="${basedir}/NEWS.txt" tofile="${zip-tmp}/${app.name}/NEWS.txt"/> <copy file="${basedir}/NEWS.txt" tofile="${zip-tmp}/${app.name}/NEWS.txt"/>
<copy file="${basedir}/Running_Linux_OSX.txt" tofile="${zip-tmp}/${app.name}/Running_Linux_OSX.txt"/> <copy file="${basedir}/Running_Linux_OSX.txt" tofile="${zip-tmp}/${app.name}/Running_Linux_OSX.txt"/>
<copy file="${basedir}/unix_setup.sh" tofile="${zip-tmp}/${app.name}/unix_setup.sh"/> <copy file="${basedir}/unix_setup.sh" tofile="${zip-tmp}/${app.name}/unix_setup.sh"/>
<copy file="${basedir}/ManifestTool/ManifestTool.exe" todir="${zip-tmp}/${app.name}/bin"/> <copy file="${basedir}/ManifestTool/ManifestTool.exe" todir="${zip-tmp}/${app.name}/bin"/>
<copy file="${basedir}/icons/icon.ico" tofile="${zip-tmp}/${app.name}/icon.ico" overwrite="true"/> <copy file="${basedir}/icons/icon.ico" tofile="${zip-tmp}/${app.name}/icon.ico" overwrite="true"/>
@ -115,6 +114,14 @@
<copy flatten="true" todir="${zip-tmp}/${app.name}/docs"> <copy flatten="true" todir="${zip-tmp}/${app.name}/docs">
<fileset dir="${basedir}/docs/doxygen-user/user-docs"/> <fileset dir="${basedir}/docs/doxygen-user/user-docs"/>
</copy> </copy>
<!-- Copy the ZooKeeper migration tool, it's JAR files, and documentation -->
<copy flatten="false" todir="${zip-tmp}/${app.name}/autopsy/ZookeeperNodeMigration">
<fileset dir="${basedir}/ZookeeperNodeMigration/dist"/>
</copy>
<copy flatten="false" todir="${zip-tmp}/${app.name}/autopsy/ZookeeperNodeMigration" overwrite="true">
<fileset dir="${basedir}/ZookeeperNodeMigration/docs"/>
</copy>
<property name="app.property.file" value="${zip-tmp}/${app.name}/etc/${app.name}.conf" /> <property name="app.property.file" value="${zip-tmp}/${app.name}/etc/${app.name}.conf" />
<var name="jvm-value" value="--branding ${app.name} -J-Xms24m -J-Xmx4G -J-Xverify:none -J-XX:+UseG1GC -J-XX:+UseStringDeduplication"/> <var name="jvm-value" value="--branding ${app.name} -J-Xms24m -J-Xmx4G -J-Xverify:none -J-XX:+UseG1GC -J-XX:+UseStringDeduplication"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -57,6 +57,7 @@ The "Indexed Text" tab shows the results when running the keyword search module
\image html keyword-search-ocr-indexed-text.png \image html keyword-search-ocr-indexed-text.png
\anchor keyword_search_ocr_config
By default, OCR is only configured for English text. Its configuration depends on the presence of language files (called "traineddata" files) By default, OCR is only configured for English text. Its configuration depends on the presence of language files (called "traineddata" files)
that exist in a location that Autopsy can understand. To add support for more languages, you will need to download additional "traineddata" that exist in a location that Autopsy can understand. To add support for more languages, you will need to download additional "traineddata"
and move them to the right location. The following steps breakdown this process for you: and move them to the right location. The following steps breakdown this process for you:

View File

@ -17,6 +17,8 @@ To set up a machine translation service, go to Options->Tools and then select th
Each service will require slightly different configuration steps. After setting everything up, you can run a quick check that the service is set up correctly using the "Test" button. Each service will require slightly different configuration steps. After setting everything up, you can run a quick check that the service is set up correctly using the "Test" button.
The checkbox at the bottom allows you to enable or disable optical character recognition (OCR). When enabled, if you select an image in the \ref mt_content_viewer "content viewer" Autopsy will use OCR to attempt to extract text to be translated. Instructions for installing OCR packages for different languages can be found on the \ref keyword_search_ocr_config "Keyword Search page".
\section mt_file_names Translating File Names \section mt_file_names Translating File Names
You can use machine translation to automatically translate file and folder names, such as the ones seen below: You can use machine translation to automatically translate file and folder names, such as the ones seen below:
@ -49,4 +51,12 @@ Then use the drop-down menu on the right to change from "Original Text" to "Tran
\image html mt_message_translated.png \image html mt_message_translated.png
If you've enabled OCR as described in the \ref mt_config section above, you can extract and translate text from images. Here is an image containing the beginning of a French poem:
\image html mt_ocr_image.png
If you go to the Text tab and then the Translation viewer it will use OCR to read text from the image and then display the translation.
\image html mt_ocr_result.png
*/ */

View File

@ -44,7 +44,8 @@
<build-prerequisite/> <build-prerequisite/>
<compile-dependency/> <compile-dependency/>
<run-dependency> <run-dependency>
<specification-version>1.0</specification-version> <release-version>1</release-version>
<specification-version>23</specification-version>
</run-dependency> </run-dependency>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -79,7 +79,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
private static final int MBOX_SIZE_TO_SPLIT = 1048576000; private static final int MBOX_SIZE_TO_SPLIT = 1048576000;
private Case currentCase; private Case currentCase;
/** /**
* Empty constructor. * Empty constructor.
*/ */
@ -139,6 +139,10 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
boolean isPstFile = PstParser.isPstFile(abstractFile); boolean isPstFile = PstParser.isPstFile(abstractFile);
boolean isVcardFile = VcardParser.isVcardFile(abstractFile); boolean isVcardFile = VcardParser.isVcardFile(abstractFile);
if (context.fileIngestIsCancelled()) {
return ProcessResult.OK;
}
if (isMbox || isEMLFile || isPstFile || isVcardFile ) { if (isMbox || isEMLFile || isPstFile || isVcardFile ) {
try { try {
communicationArtifactsHelper = new CommunicationArtifactsHelper(currentCase.getSleuthkitCase(), communicationArtifactsHelper = new CommunicationArtifactsHelper(currentCase.getSleuthkitCase(),
@ -213,6 +217,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
Iterator<EmailMessage> pstMsgIterator = parser.getEmailMessageIterator(); Iterator<EmailMessage> pstMsgIterator = parser.getEmailMessageIterator();
if (pstMsgIterator != null) { if (pstMsgIterator != null) {
processEmails(parser.getPartialEmailMessages(), pstMsgIterator , abstractFile); processEmails(parser.getPartialEmailMessages(), pstMsgIterator , abstractFile);
if (context.fileIngestIsCancelled()) {
return ProcessResult.OK;
}
} else { } else {
// sometimes parser returns ParseResult=OK but there are no messages // sometimes parser returns ParseResult=OK but there are no messages
postErrorMessage( postErrorMessage(
@ -321,6 +328,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
} }
processMboxFile(file, abstractFile, emailFolder); processMboxFile(file, abstractFile, emailFolder);
if (context.fileIngestIsCancelled()) {
return ProcessResult.OK;
}
if (file.delete() == false) { if (file.delete() == false) {
logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS
@ -349,7 +359,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
if (splitFile.delete() == false) { if (splitFile.delete() == false) {
logger.log(Level.INFO, "Failed to delete temp file: {0}", splitFile); //NON-NLS logger.log(Level.INFO, "Failed to delete temp file: {0}", splitFile); //NON-NLS
} }
if (context.fileIngestIsCancelled()) {
return ProcessResult.OK;
}
} }
} }
@ -385,6 +397,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
List<EmailMessage> emails = new ArrayList<>(); List<EmailMessage> emails = new ArrayList<>();
if(emailIterator != null) { if(emailIterator != null) {
while(emailIterator.hasNext()) { while(emailIterator.hasNext()) {
if (context.fileIngestIsCancelled()) {
return;
}
EmailMessage emailMessage = emailIterator.next(); EmailMessage emailMessage = emailIterator.next();
if(emailMessage != null) { if(emailMessage != null) {
emails.add(emailMessage); emails.add(emailMessage);
@ -526,6 +541,10 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
int msgCnt = 0; int msgCnt = 0;
while(fullMessageIterator.hasNext()) { while(fullMessageIterator.hasNext()) {
if (context.fileIngestIsCancelled()) {
return;
}
EmailMessage current = fullMessageIterator.next(); EmailMessage current = fullMessageIterator.next();
if(current == null) { if(current == null) {
@ -550,6 +569,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
if (derivedFiles.isEmpty() == false) { if (derivedFiles.isEmpty() == false) {
for (AbstractFile derived : derivedFiles) { for (AbstractFile derived : derivedFiles) {
if (context.fileIngestIsCancelled()) {
return;
}
services.fireModuleContentEvent(new ModuleContentEvent(derived)); services.fireModuleContentEvent(new ModuleContentEvent(derived));
} }
} }
@ -675,6 +697,10 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
String senderAddress; String senderAddress;
senderAddressList.addAll(findEmailAddresess(from)); senderAddressList.addAll(findEmailAddresess(from));
if (context.fileIngestIsCancelled()) {
return null;
}
AccountFileInstance senderAccountInstance = null; AccountFileInstance senderAccountInstance = null;
if (senderAddressList.size() == 1) { if (senderAddressList.size() == 1) {
@ -690,13 +716,20 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
logger.log(Level.WARNING, "Failed to find sender address, from = {0}", from); //NON-NLS logger.log(Level.WARNING, "Failed to find sender address, from = {0}", from); //NON-NLS
} }
if (context.fileIngestIsCancelled()) {
return null;
}
List<String> recipientAddresses = new ArrayList<>(); List<String> recipientAddresses = new ArrayList<>();
recipientAddresses.addAll(findEmailAddresess(to)); recipientAddresses.addAll(findEmailAddresess(to));
recipientAddresses.addAll(findEmailAddresess(cc)); recipientAddresses.addAll(findEmailAddresess(cc));
recipientAddresses.addAll(findEmailAddresess(bcc)); recipientAddresses.addAll(findEmailAddresess(bcc));
List<AccountFileInstance> recipientAccountInstances = new ArrayList<>(); List<AccountFileInstance> recipientAccountInstances = new ArrayList<>();
recipientAddresses.forEach((addr) -> { for (String addr : recipientAddresses) {
if (context.fileIngestIsCancelled()) {
return null;
}
try { try {
AccountFileInstance recipientAccountInstance = AccountFileInstance recipientAccountInstance =
currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, addr, currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, addr,
@ -706,7 +739,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
catch(TskCoreException ex) { catch(TskCoreException ex) {
logger.log(Level.WARNING, "Failed to create account for email address " + addr, ex); //NON-NLS logger.log(Level.WARNING, "Failed to create account for email address " + addr, ex); //NON-NLS
} }
}); }
addArtifactAttribute(headers, ATTRIBUTE_TYPE.TSK_HEADERS, bbattributes); addArtifactAttribute(headers, ATTRIBUTE_TYPE.TSK_HEADERS, bbattributes);
addArtifactAttribute(from, ATTRIBUTE_TYPE.TSK_EMAIL_FROM, bbattributes); addArtifactAttribute(from, ATTRIBUTE_TYPE.TSK_EMAIL_FROM, bbattributes);
@ -731,12 +764,23 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
try { try {
if (context.fileIngestIsCancelled()) {
return null;
}
bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG); bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG);
bbart.addAttributes(bbattributes); bbart.addAttributes(bbattributes);
if (context.fileIngestIsCancelled()) {
return null;
}
// Add account relationships // Add account relationships
currentCase.getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart,Relationship.Type.MESSAGE, dateL); currentCase.getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart,Relationship.Type.MESSAGE, dateL);
if (context.fileIngestIsCancelled()) {
return null;
}
try { try {
// index the artifact for keyword search // index the artifact for keyword search