19 package org.sleuthkit.autopsy.keywordsearch;
21 import com.google.common.eventbus.Subscribe;
23 import java.io.IOException;
24 import java.io.Reader;
25 import java.lang.reflect.InvocationTargetException;
26 import java.net.InetAddress;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.MissingResourceException;
30 import java.util.logging.Level;
31 import javax.swing.JDialog;
32 import javax.swing.JOptionPane;
33 import javax.swing.SwingUtilities;
34 import org.apache.commons.io.FileUtils;
35 import org.apache.commons.lang.math.NumberUtils;
36 import org.apache.solr.client.solrj.SolrServerException;
37 import org.apache.solr.client.solrj.impl.HttpSolrServer;
38 import org.openide.util.NbBundle;
39 import org.openide.util.lookup.ServiceProvider;
40 import org.openide.util.lookup.ServiceProviders;
62 @ServiceProviders(value = {
63 @ServiceProvider(service = KeywordSearchService.class),
64 @ServiceProvider(service = AutopsyService.class)
68 private static final String BAD_IP_ADDRESS_FORMAT =
"ioexception occurred when talking to server";
69 private static final String SERVER_REFUSED_CONNECTION =
"server refused connection";
70 private static final int IS_REACHABLE_TIMEOUT_MS = 1000;
71 private static final int LARGE_INDEX_SIZE_GB = 50;
72 private static final int GIANT_INDEX_SIZE_GB = 500;
91 public void index(Content content)
throws TskCoreException {
105 if (content == null) {
108 final Ingester ingester = Ingester.getDefault();
109 if (content instanceof BlackboardArtifact) {
110 BlackboardArtifact artifact = (BlackboardArtifact) content;
111 if (artifact.getArtifactID() > 0) {
121 Reader blackboardExtractedTextReader = blackboardExtractor.
getReader();
122 String sourceName = artifact.getDisplayName() +
"_" + artifact.getArtifactID();
123 ingester.indexMetaDataOnly(artifact, sourceName);
124 ingester.indexText(blackboardExtractedTextReader, artifact.getArtifactID(), sourceName, content, null);
126 throw new TskCoreException(
"Error indexing artifact", ex);
131 Reader contentExtractedTextReader = contentExtractor.
getReader();
132 ingester.indexText(contentExtractedTextReader, content.getId(), content.getName(), content, null);
137 Reader stringsExtractedTextReader = stringsExtractor.
getReader();
138 ingester.indexText(stringsExtractedTextReader, content.getId(), content.getName(), content, null);
140 throw new TskCoreException(
"Error indexing content", ex1);
157 HttpSolrServer solrServer = null;
158 if (host == null || host.isEmpty()) {
162 solrServer =
new HttpSolrServer(
"http://" + host +
":" + Integer.toString(port) +
"/solr");
164 }
catch (SolrServerException ex) {
166 }
catch (IOException ex) {
167 String result = NbBundle.getMessage(
SolrSearchService.class,
"SolrConnectionCheck.HostnameOrPort");
168 String message = ex.getCause().getMessage().toLowerCase();
169 if (message.startsWith(SERVER_REFUSED_CONNECTION)) {
171 if (InetAddress.getByName(host).isReachable(IS_REACHABLE_TIMEOUT_MS)) {
173 result = Bundle.SolrConnectionCheck_Port();
175 result = NbBundle.getMessage(
SolrSearchService.class,
"SolrConnectionCheck.HostnameOrPort");
177 }
catch (IOException | MissingResourceException any) {
179 result = NbBundle.getMessage(
SolrSearchService.class,
"SolrConnectionCheck.HostnameOrPort");
181 }
else if (message.startsWith(BAD_IP_ADDRESS_FORMAT)) {
182 result = NbBundle.getMessage(
SolrSearchService.class,
"SolrConnectionCheck.Hostname");
185 }
catch (NumberFormatException ex) {
187 }
catch (IllegalArgumentException ex) {
190 if (null != solrServer) {
191 solrServer.shutdown();
209 ddsServer.deleteDataSource(dataSourceId);
211 logger.log(Level.WARNING, NbBundle.getMessage(
SolrSearchService.class,
"SolrSearchService.DeleteDataSource.msg", dataSourceId), ex);
222 "# {0} - case directory",
"SolrSearchService.exceptionMessage.noIndexMetadata=Unable to create IndexMetaData from case directory: {0}",
223 "SolrSearchService.exceptionMessage.noCurrentSolrCore=IndexMetadata did not contain a current Solr core so could not delete the case",
224 "# {0} - index folder path",
"SolrSearchService.exceptionMessage.failedToDeleteIndexFiles=Failed to delete text index files at {0}"
229 IndexMetadata indexMetadata;
231 indexMetadata =
new IndexMetadata(caseDirectory);
232 }
catch (IndexMetadata.TextIndexMetadataException ex) {
233 logger.log(Level.WARNING, NbBundle.getMessage(
SolrSearchService.class,
"SolrSearchService.exceptionMessage.noIndexMetadata", caseDirectory), ex);
237 String currentSchema = IndexFinder.getCurrentSchemaVersion();
238 String currentSolr = IndexFinder.getCurrentSolrVersion();
239 for (Index index : indexMetadata.getIndexes()) {
240 if (index.getSolrVersion().equals(currentSolr) && index.getSchemaVersion().equals(currentSchema)) {
255 "SolrSearchService.exceptionMessage.noCurrentSolrCore"));
257 "SolrSearchService.exceptionMessage.noCurrentSolrCore"));
262 return NbBundle.getMessage(this.getClass(),
"SolrSearchService.ServiceName");
275 "SolrSearch.lookingForMetadata.msg=Looking for text index metadata file",
276 "SolrSearch.readingIndexes.msg=Reading text index metadata file",
277 "SolrSearch.findingIndexes.msg=Looking for existing text index directories",
278 "SolrSearch.creatingNewIndex.msg=Creating new text index",
279 "SolrSearch.checkingForLatestIndex.msg=Looking for text index with latest Solr and schema version",
280 "SolrSearch.indentifyingIndex.msg=Identifying text index to use",
281 "SolrSearch.openCore.msg=Opening text index",
282 "SolrSearch.openLargeCore.msg=Opening text index. This may take several minutes.",
283 "SolrSearch.openGiantCore.msg=Opening text index. Text index for this case is very large and may take long time to load.",
284 "SolrSearch.complete.msg=Text index successfully opened"})
291 int totalNumProgressUnits = 7;
292 int progressUnitsCompleted = 0;
296 List<Index> indexes =
new ArrayList<>();
297 progress.
progress(Bundle.SolrSearch_lookingForMetadata_msg(), totalNumProgressUnits);
298 if (IndexMetadata.isMetadataFilePresent(caseDirPath)) {
301 progressUnitsCompleted++;
302 progress.
progress(Bundle.SolrSearch_findingIndexes_msg(), progressUnitsCompleted);
303 IndexMetadata indexMetadata =
new IndexMetadata(caseDirPath);
304 indexes = indexMetadata.getIndexes();
305 }
catch (IndexMetadata.TextIndexMetadataException ex) {
306 logger.log(Level.SEVERE, String.format(
"Unable to read text index metadata file"), ex);
312 progressUnitsCompleted++;
313 progress.
progress(Bundle.SolrSearch_findingIndexes_msg(), progressUnitsCompleted);
314 Index oldIndex = IndexFinder.findOldIndexDir(theCase);
315 if (oldIndex != null) {
317 indexes.add(oldIndex);
326 Index currentVersionIndex = null;
327 if (indexes.isEmpty()) {
329 progressUnitsCompleted++;
330 progress.
progress(Bundle.SolrSearch_creatingNewIndex_msg(), progressUnitsCompleted);
331 currentVersionIndex = IndexFinder.createLatestVersionIndexDir(theCase);
333 indexes.add(currentVersionIndex);
336 progressUnitsCompleted++;
337 progress.
progress(Bundle.SolrSearch_checkingForLatestIndex_msg(), progressUnitsCompleted);
338 currentVersionIndex = IndexFinder.findLatestVersionIndexDir(indexes);
339 if (currentVersionIndex == null) {
341 progressUnitsCompleted++;
342 progress.
progress(Bundle.SolrSearch_indentifyingIndex_msg(), progressUnitsCompleted);
343 Index indexToUse = IndexFinder.identifyIndexToUse(indexes);
344 if (indexToUse == null) {
353 double currentSolrVersion = NumberUtils.toDouble(IndexFinder.getCurrentSolrVersion());
354 double indexSolrVersion = NumberUtils.toDouble(indexToUse.getSolrVersion());
355 if (indexSolrVersion == currentSolrVersion) {
359 JOptionPane optionPane =
new JOptionPane(
360 NbBundle.getMessage(
this.getClass(),
"SolrSearchService.IndexReadOnlyDialog.msg"),
361 JOptionPane.WARNING_MESSAGE,
362 JOptionPane.DEFAULT_OPTION);
364 SwingUtilities.invokeAndWait(() -> {
365 JDialog dialog = optionPane.createDialog(NbBundle.getMessage(
this.getClass(),
"SolrSearchService.IndexReadOnlyDialog.title"));
366 dialog.setVisible(
true);
368 }
catch (InterruptedException ex) {
371 }
catch (InvocationTargetException ex) {
376 currentVersionIndex = indexToUse;
386 if (!indexes.isEmpty()) {
387 IndexMetadata indexMetadata =
new IndexMetadata(caseDirPath, indexes);
389 }
catch (IndexMetadata.TextIndexMetadataException ex) {
396 long indexSizeInBytes = FileUtils.sizeOfDirectory(
new File(currentVersionIndex.getIndexPath()));
397 long sizeInGb = indexSizeInBytes / 1000000000;
398 if (sizeInGb < LARGE_INDEX_SIZE_GB) {
399 progress.
progress(Bundle.SolrSearch_openCore_msg(), totalNumProgressUnits - 1);
400 }
else if (sizeInGb >= LARGE_INDEX_SIZE_GB && sizeInGb < GIANT_INDEX_SIZE_GB) {
416 progress.
progress(Bundle.SolrSearch_complete_msg(), totalNumProgressUnits);
435 AdHocSearchChildFactory.BlackboardResultWriter.stopAllWriters();
438 }
catch (InterruptedException ex) {
439 logger.log(Level.SEVERE,
"Unexpected interrupt while waiting for BlackboardResultWriters to terminate", ex);
456 @NbBundle.Messages(
"SolrSearchService.indexingError=Unable to index blackboard artifact.")
458 void handleNewArtifacts(Blackboard.ArtifactsPostedEvent event) {
459 for (BlackboardArtifact artifact : event.getArtifacts()) {
460 if ((artifact.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) &&
461 (artifact.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID())){
464 }
catch (TskCoreException ex) {
466 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex);
467 MessageNotifyUtil.Notify.error(Bundle.SolrSearchService_indexingError(), artifact.getDisplayName());
484 public void indexArtifact(BlackboardArtifact artifact)
throws TskCoreException {
485 if (artifact == null) {
491 if (artifact.getArtifactID() > 0) {
494 final Ingester ingester = Ingester.getDefault();
497 String sourceName = artifact.getDisplayName() +
"_" + artifact.getArtifactID();
499 Reader blackboardExtractedTextReader = blackboardExtractor.
getReader();
500 ingester.indexMetaDataOnly(artifact, sourceName);
501 ingester.indexText(blackboardExtractedTextReader, artifact.getId(), sourceName, artifact, null);
503 throw new TskCoreException(ex.getCause().getMessage(), ex);
void index(Content content)
void indexArtifact(BlackboardArtifact artifact)
static boolean runningWithGUI
void openCaseResources(CaseContext context)
String getCaseDirectory()
static synchronized Server getServer()
ProgressIndicator getProgressIndicator()
volatile boolean cancelRequested
void closeCaseResources(CaseContext context)
void deleteTextIndex(CaseMetadata metadata)
SleuthkitCase getSleuthkitCase()
void deleteDataSource(Long dataSourceId)
synchronized static Logger getLogger(String name)
static boolean deleteDir(File dirPath)
void progress(String message)
void tryConnect(String host, int port)
void switchToIndeterminate(String message)