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)
65 @ServiceProvider(service = AutopsyService.class)
69 private static final String BAD_IP_ADDRESS_FORMAT =
"ioexception occurred when talking to server";
70 private static final String SERVER_REFUSED_CONNECTION =
"server refused connection";
71 private static final int IS_REACHABLE_TIMEOUT_MS = 1000;
72 private static final int LARGE_INDEX_SIZE_GB = 50;
73 private static final int GIANT_INDEX_SIZE_GB = 500;
92 public void index(Content content)
throws TskCoreException {
106 if (content == null) {
109 final Ingester ingester = Ingester.getDefault();
110 if (content instanceof BlackboardArtifact) {
111 BlackboardArtifact artifact = (BlackboardArtifact) content;
112 if (artifact.getArtifactID() > 0) {
122 Reader blackboardExtractedTextReader = blackboardExtractor.
getReader();
123 String sourceName = artifact.getDisplayName() +
"_" + artifact.getArtifactID();
124 ingester.indexMetaDataOnly(artifact, sourceName);
125 ingester.indexText(blackboardExtractedTextReader, artifact.getArtifactID(), sourceName, content, null);
127 throw new TskCoreException(
"Error indexing artifact", ex);
132 Reader contentExtractedTextReader = contentExtractor.
getReader();
133 ingester.indexText(contentExtractedTextReader, content.getId(), content.getName(), content, null);
138 Reader stringsExtractedTextReader = stringsExtractor.
getReader();
139 ingester.indexText(stringsExtractedTextReader, content.getId(), content.getName(), content, null);
141 throw new TskCoreException(
"Error indexing content", ex1);
158 HttpSolrServer solrServer = null;
159 if (host == null || host.isEmpty()) {
163 solrServer =
new HttpSolrServer(
"http://" + host +
":" + Integer.toString(port) +
"/solr");
165 }
catch (SolrServerException ex) {
167 }
catch (IOException ex) {
168 String result = NbBundle.getMessage(
SolrSearchService.class,
"SolrConnectionCheck.HostnameOrPort");
169 String message = ex.getCause().getMessage().toLowerCase();
170 if (message.startsWith(SERVER_REFUSED_CONNECTION)) {
172 if (InetAddress.getByName(host).isReachable(IS_REACHABLE_TIMEOUT_MS)) {
174 result = Bundle.SolrConnectionCheck_Port();
176 result = NbBundle.getMessage(
SolrSearchService.class,
"SolrConnectionCheck.HostnameOrPort");
178 }
catch (IOException | MissingResourceException any) {
180 result = NbBundle.getMessage(
SolrSearchService.class,
"SolrConnectionCheck.HostnameOrPort");
182 }
else if (message.startsWith(BAD_IP_ADDRESS_FORMAT)) {
183 result = NbBundle.getMessage(
SolrSearchService.class,
"SolrConnectionCheck.Hostname");
186 }
catch (NumberFormatException ex) {
188 }
catch (IllegalArgumentException ex) {
191 if (null != solrServer) {
192 solrServer.shutdown();
203 "# {0} - case directory",
"SolrSearchService.exceptionMessage.noIndexMetadata=Unable to create IndexMetaData from case directory: {0}",
204 "SolrSearchService.exceptionMessage.noCurrentSolrCore=IndexMetadata did not contain a current Solr core so could not delete the case",
205 "# {0} - index folder path",
"SolrSearchService.exceptionMessage.failedToDeleteIndexFiles=Failed to delete text index files at {0}"
210 IndexMetadata indexMetadata;
212 indexMetadata =
new IndexMetadata(caseDirectory);
213 }
catch (IndexMetadata.TextIndexMetadataException ex) {
214 logger.log(Level.WARNING, NbBundle.getMessage(
SolrSearchService.class,
"SolrSearchService.exceptionMessage.noIndexMetadata", caseDirectory), ex);
218 String currentSchema = IndexFinder.getCurrentSchemaVersion();
219 String currentSolr = IndexFinder.getCurrentSolrVersion();
220 for (Index index : indexMetadata.getIndexes()) {
221 if (index.getSolrVersion().equals(currentSolr) && index.getSchemaVersion().equals(currentSchema)) {
236 "SolrSearchService.exceptionMessage.noCurrentSolrCore"));
238 "SolrSearchService.exceptionMessage.noCurrentSolrCore"));
243 return NbBundle.getMessage(this.getClass(),
"SolrSearchService.ServiceName");
256 "SolrSearch.lookingForMetadata.msg=Looking for text index metadata file",
257 "SolrSearch.readingIndexes.msg=Reading text index metadata file",
258 "SolrSearch.findingIndexes.msg=Looking for existing text index directories",
259 "SolrSearch.creatingNewIndex.msg=Creating new text index",
260 "SolrSearch.checkingForLatestIndex.msg=Looking for text index with latest Solr and schema version",
261 "SolrSearch.indentifyingIndex.msg=Identifying text index to use",
262 "SolrSearch.openCore.msg=Opening text index",
263 "SolrSearch.openLargeCore.msg=Opening text index. This may take several minutes.",
264 "SolrSearch.openGiantCore.msg=Opening text index. Text index for this case is very large and may take long time to load.",
265 "SolrSearch.complete.msg=Text index successfully opened"})
272 int totalNumProgressUnits = 7;
273 int progressUnitsCompleted = 0;
277 List<Index> indexes =
new ArrayList<>();
278 progress.
start(Bundle.SolrSearch_lookingForMetadata_msg(), totalNumProgressUnits);
279 if (IndexMetadata.isMetadataFilePresent(caseDirPath)) {
282 progressUnitsCompleted++;
283 progress.
progress(Bundle.SolrSearch_findingIndexes_msg(), progressUnitsCompleted);
284 IndexMetadata indexMetadata =
new IndexMetadata(caseDirPath);
285 indexes = indexMetadata.getIndexes();
286 }
catch (IndexMetadata.TextIndexMetadataException ex) {
287 logger.log(Level.SEVERE, String.format(
"Unable to read text index metadata file"), ex);
293 progressUnitsCompleted++;
294 progress.
progress(Bundle.SolrSearch_findingIndexes_msg(), progressUnitsCompleted);
295 Index oldIndex = IndexFinder.findOldIndexDir(theCase);
296 if (oldIndex != null) {
298 indexes.add(oldIndex);
307 Index currentVersionIndex = null;
308 if (indexes.isEmpty()) {
310 progressUnitsCompleted++;
311 progress.
progress(Bundle.SolrSearch_creatingNewIndex_msg(), progressUnitsCompleted);
312 currentVersionIndex = IndexFinder.createLatestVersionIndexDir(theCase);
314 indexes.add(currentVersionIndex);
317 progressUnitsCompleted++;
318 progress.
progress(Bundle.SolrSearch_checkingForLatestIndex_msg(), progressUnitsCompleted);
319 currentVersionIndex = IndexFinder.findLatestVersionIndexDir(indexes);
320 if (currentVersionIndex == null) {
322 progressUnitsCompleted++;
323 progress.
progress(Bundle.SolrSearch_indentifyingIndex_msg(), progressUnitsCompleted);
324 Index indexToUse = IndexFinder.identifyIndexToUse(indexes);
325 if (indexToUse == null) {
334 double currentSolrVersion = NumberUtils.toDouble(IndexFinder.getCurrentSolrVersion());
335 double indexSolrVersion = NumberUtils.toDouble(indexToUse.getSolrVersion());
336 if (indexSolrVersion == currentSolrVersion) {
340 JOptionPane optionPane =
new JOptionPane(
341 NbBundle.getMessage(
this.getClass(),
"SolrSearchService.IndexReadOnlyDialog.msg"),
342 JOptionPane.WARNING_MESSAGE,
343 JOptionPane.DEFAULT_OPTION);
345 SwingUtilities.invokeAndWait(() -> {
346 JDialog dialog = optionPane.createDialog(NbBundle.getMessage(
this.getClass(),
"SolrSearchService.IndexReadOnlyDialog.title"));
347 dialog.setVisible(
true);
349 }
catch (InterruptedException ex) {
352 }
catch (InvocationTargetException ex) {
357 currentVersionIndex = indexToUse;
367 if (!indexes.isEmpty()) {
368 IndexMetadata indexMetadata =
new IndexMetadata(caseDirPath, indexes);
370 }
catch (IndexMetadata.TextIndexMetadataException ex) {
377 long indexSizeInBytes = FileUtils.sizeOfDirectory(
new File(currentVersionIndex.getIndexPath()));
378 long sizeInGb = indexSizeInBytes / 1000000000;
379 if (sizeInGb < LARGE_INDEX_SIZE_GB) {
380 progress.
progress(Bundle.SolrSearch_openCore_msg(), totalNumProgressUnits - 1);
381 }
else if (sizeInGb >= LARGE_INDEX_SIZE_GB && sizeInGb < GIANT_INDEX_SIZE_GB) {
397 progress.
progress(Bundle.SolrSearch_complete_msg(), totalNumProgressUnits);
416 AdHocSearchChildFactory.BlackboardResultWriter.stopAllWriters();
419 }
catch (InterruptedException ex) {
420 logger.log(Level.SEVERE,
"Unexpected interrupt while waiting for BlackboardResultWriters to terminate", ex);
437 @NbBundle.Messages(
"SolrSearchService.indexingError=Unable to index blackboard artifact.")
439 void handleNewArtifacts(Blackboard.ArtifactsPostedEvent event) {
440 for (BlackboardArtifact artifact : event.getArtifacts()) {
441 if (artifact.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
444 }
catch (TskCoreException ex) {
446 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex);
447 MessageNotifyUtil.Notify.error(Bundle.SolrSearchService_indexingError(), artifact.getDisplayName());
464 public void indexArtifact(BlackboardArtifact artifact)
throws TskCoreException {
465 if (artifact == null) {
471 if (artifact.getArtifactID() > 0) {
474 final Ingester ingester = Ingester.getDefault();
477 String sourceName = artifact.getDisplayName() +
"_" + artifact.getArtifactID();
479 Reader blackboardExtractedTextReader = blackboardExtractor.
getReader();
480 ingester.indexMetaDataOnly(artifact, sourceName);
481 ingester.indexText(blackboardExtractedTextReader, artifact.getId(), sourceName, artifact, null);
483 throw new TskCoreException(ex.getCause().getMessage(), ex);
void index(Content content)
void indexArtifact(BlackboardArtifact artifact)
static boolean runningWithGUI
void start(String message, int totalWorkUnits)
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()
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)