23 package org.sleuthkit.autopsy.report.modules.html;
27 import java.awt.image.BufferedImage;
28 import java.io.BufferedWriter;
30 import java.io.FileNotFoundException;
31 import java.io.FileOutputStream;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.OutputStream;
35 import java.io.OutputStreamWriter;
36 import java.io.UnsupportedEncodingException;
37 import java.io.Writer;
38 import java.nio.file.Files;
39 import java.nio.file.Path;
40 import java.nio.file.Paths;
41 import java.text.DateFormat;
42 import java.text.SimpleDateFormat;
43 import java.util.ArrayList;
44 import java.util.Date;
45 import java.util.HashMap;
46 import java.util.List;
49 import java.util.TreeMap;
50 import java.util.concurrent.ExecutionException;
51 import java.util.logging.Level;
52 import javax.imageio.ImageIO;
53 import javax.swing.JPanel;
54 import org.apache.commons.io.FilenameUtils;
55 import org.apache.commons.lang3.StringEscapeUtils;
56 import org.openide.filesystems.FileUtil;
57 import org.openide.util.NbBundle;
58 import org.openide.util.NbBundle.Messages;
76 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
85 import org.
sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
111 if (instance == null) {
129 if (configPanel == null) {
130 configPanel =
new HTMLReportConfigurationPanel();
141 return new HTMLReportModuleSettings();
152 return configPanel.getConfiguration();
168 if (settings instanceof HTMLReportModuleSettings) {
169 configPanel.setConfiguration((HTMLReportModuleSettings) settings);
173 throw new IllegalArgumentException(
"Expected settings argument to be an instance of HTMLReportModuleSettings");
180 dataTypes =
new TreeMap<>();
185 currentDataType =
"";
191 }
catch (IOException ex) {
207 fileName = fileName.replaceAll(
" ",
"_");
216 @SuppressWarnings(
"deprecation" )
221 OutputStream output = null;
223 logger.log(Level.INFO,
"useDataTypeIcon: dataType = {0}", dataType);
226 BlackboardArtifact.ARTIFACT_TYPE artifactType = null;
227 for (ARTIFACT_TYPE v : ARTIFACT_TYPE.values()) {
228 if (v.getDisplayName().equals(dataType)) {
233 if (null != artifactType) {
236 iconFilePath = subPath + File.separator + iconFileName;
239 switch (artifactType) {
240 case TSK_WEB_BOOKMARK:
241 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/bookmarks.png");
244 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/cookies.png");
246 case TSK_WEB_HISTORY:
247 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/history.png");
249 case TSK_WEB_DOWNLOAD:
250 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/downloads.png");
252 case TSK_RECENT_OBJECT:
253 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/recent.png");
255 case TSK_INSTALLED_PROG:
256 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/installed.png");
258 case TSK_KEYWORD_HIT:
259 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/keywords.png");
261 case TSK_HASHSET_HIT:
262 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/hash.png");
264 case TSK_DEVICE_ATTACHED:
265 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/devices.png");
267 case TSK_WEB_SEARCH_QUERY:
268 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/search.png");
270 case TSK_METADATA_EXIF:
271 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/exif.png");
274 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/userbookmarks.png");
276 case TSK_TAG_ARTIFACT:
277 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/userbookmarks.png");
279 case TSK_SERVICE_ACCOUNT:
280 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/account-icon-16.png");
283 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/contact.png");
286 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/message.png");
289 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/calllog.png");
291 case TSK_CALENDAR_ENTRY:
292 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/calendar.png");
294 case TSK_SPEED_DIAL_ENTRY:
295 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/speeddialentry.png");
297 case TSK_BLUETOOTH_PAIRING:
298 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/bluetooth.png");
300 case TSK_GPS_BOOKMARK:
301 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gpsfav.png");
303 case TSK_GPS_LAST_KNOWN_LOCATION:
304 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gps-lastlocation.png");
307 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gps-search.png");
310 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/computer.png");
312 case TSK_GPS_TRACKPOINT:
313 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gps_trackpoint.png");
316 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gps_trackpoint.png");
319 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/mail-icon-16.png");
321 case TSK_ENCRYPTION_SUSPECTED:
322 case TSK_ENCRYPTION_DETECTED:
323 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/encrypted-file.png");
325 case TSK_EXT_MISMATCH_DETECTED:
326 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/mismatch-16.png");
328 case TSK_INTERESTING_ARTIFACT_HIT:
329 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/interesting_item.png");
331 case TSK_INTERESTING_FILE_HIT:
332 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/interesting_item.png");
335 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/installed.png");
337 case TSK_REMOTE_DRIVE:
338 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/drive_network.png");
341 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/os-account.png");
343 case TSK_OBJECT_DETECTED:
344 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/objects.png");
346 case TSK_WEB_FORM_AUTOFILL:
347 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/web-form.png");
350 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/cache.png");
352 case TSK_USER_CONTENT_SUSPECTED:
353 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/user-content.png");
356 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/metadata.png");
358 case TSK_CLIPBOARD_CONTENT:
359 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/clipboard.png");
362 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/accounts.png");
364 case TSK_WIFI_NETWORK:
365 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/network-wifi.png");
367 case TSK_WIFI_NETWORK_ADAPTER:
368 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/network-wifi.png");
370 case TSK_SIM_ATTACHED:
371 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/sim_card.png");
373 case TSK_BLUETOOTH_ADAPTER:
374 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/Bluetooth.png");
376 case TSK_DEVICE_INFO:
377 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/devices.png");
379 case TSK_VERIFICATION_FAILED:
380 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/validationFailed.png");
382 case TSK_WEB_ACCOUNT_TYPE:
383 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/web-account-type.png");
385 case TSK_WEB_FORM_ADDRESS:
386 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/web-form-address.png");
389 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/gps-area.png");
391 case TSK_WEB_CATEGORIZATION:
392 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/domain-16.png");
395 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/yara_16.png");
397 case TSK_PREVIOUSLY_SEEN:
398 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/previously-seen.png");
400 case TSK_PREVIOUSLY_UNSEEN:
401 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/previously-unseen.png");
403 case TSK_PREVIOUSLY_NOTABLE:
404 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/red-circle-exclamation.png");
407 logger.log(Level.WARNING,
"useDataTypeIcon: unhandled artifact type = {0}", dataType);
408 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/star.png");
409 iconFileName =
"star.png";
410 iconFilePath = subPath + File.separator + iconFileName;
413 }
else if (dataType.startsWith(ARTIFACT_TYPE.TSK_ACCOUNT.getDisplayName())) {
421 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/accounts.png");
422 iconFileName =
"accounts.png";
423 iconFilePath = subPath + File.separator + iconFileName;
425 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/star.png");
426 iconFileName =
"star.png";
427 iconFilePath = subPath + File.separator + iconFileName;
431 output =
new FileOutputStream(iconFilePath);
432 FileUtil.copy(in, output);
435 }
catch (IOException ex) {
436 logger.log(Level.SEVERE,
"Failed to extract images for HTML report.", ex);
438 if (output != null) {
442 }
catch (IOException ex) {
448 }
catch (IOException ex) {
469 logger.log(Level.SEVERE,
"Exception while getting open case.");
473 this.path = baseReportDir;
474 this.subPath = this.path + HTML_SUBDIR + File.separator;
477 FileUtil.createFolder(
new File(this.subPath));
478 FileUtil.createFolder(
new File(this.thumbsPath));
479 }
catch (IOException ex) {
480 logger.log(Level.SEVERE,
"Unable to make HTML report folder.");
498 }
catch (IOException ex) {
499 logger.log(Level.WARNING,
"Could not close the output writer when ending report.", ex);
516 out =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(subPath + title +
".html"),
"UTF-8"));
517 }
catch (FileNotFoundException ex) {
518 logger.log(Level.SEVERE,
"File not found: {0}", ex);
519 }
catch (UnsupportedEncodingException ex) {
520 logger.log(Level.SEVERE,
"Unrecognized encoding");
524 StringBuilder page =
new StringBuilder();
525 page.append(
"<html>\n<head>\n\t<title>").append(name).append(
"</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n</head>\n<body>\n")
527 .append(
"<div id=\"header\">").append(name).append(
"</div>\n")
528 .append(
"<div id=\"content\">\n");
529 if (!description.isEmpty()) {
530 page.append(
"<p><strong>");
531 page.append(description);
532 page.append(
"</strong></p>\n");
534 out.write(page.toString());
535 currentDataType = name;
537 }
catch (IOException ex) {
538 logger.log(Level.SEVERE,
"Failed to write page head: {0}", ex);
548 dataTypes.put(currentDataType, rowCount);
550 StringBuilder builder =
new StringBuilder();
552 builder.append(
"</div>\n</body>\n</html>\n");
553 out.write(builder.toString());
554 }
catch (IOException ex) {
555 logger.log(Level.SEVERE,
"Failed to write end of HTML report.", ex);
561 }
catch (IOException ex) {
562 logger.log(Level.WARNING,
"Could not close the output writer when ending data type.", ex);
576 StringBuilder output =
new StringBuilder();
577 String pageHeader = configPanel.getHeader();
578 if (pageHeader.isEmpty() ==
false) {
579 output.append(
"<div id=\"pageHeaderFooter\">")
580 .append(StringEscapeUtils.escapeHtml4(pageHeader))
583 return output.toString();
593 StringBuilder output =
new StringBuilder();
594 String pageFooter = configPanel.getFooter();
595 if (pageFooter.isEmpty() ==
false) {
596 output.append(
"<br/><div id=\"pageHeaderFooter\">")
597 .append(StringEscapeUtils.escapeHtml4(pageFooter))
600 return output.toString();
610 StringBuilder set =
new StringBuilder();
611 set.append(
"<h1><a name=\"").append(setName).append(
"\">").append(setName).append(
"</a></h1>\n");
612 set.append(
"<div class=\"keyword_list\">\n");
615 out.write(set.toString());
616 }
catch (IOException ex) {
617 logger.log(Level.SEVERE,
"Failed to write set: {0}", ex);
627 out.write(
"</div>\n");
628 }
catch (IOException ex) {
629 logger.log(Level.SEVERE,
"Failed to write end of set: {0}", ex);
640 StringBuilder index =
new StringBuilder();
641 index.append(
"<ul>\n");
642 for (String set : sets) {
643 index.append(
"\t<li><a href=\"#").append(set).append(
"\">").append(set).append(
"</a></li>\n");
645 index.append(
"</ul>\n");
647 out.write(index.toString());
648 }
catch (IOException ex) {
649 logger.log(Level.SEVERE,
"Failed to add set index: {0}", ex);
661 out.write(
"<h4>" + elementName +
"</h4>\n");
662 }
catch (IOException ex) {
663 logger.log(Level.SEVERE,
"Failed to write set element: {0}", ex);
674 StringBuilder ele =
new StringBuilder();
675 ele.append(
"<table>\n<thead>\n\t<tr>\n");
676 for (String title : titles) {
677 ele.append(
"\t\t<th>").append(title).append(
"</th>\n");
679 ele.append(
"\t</tr>\n</thead>\n");
682 out.write(ele.toString());
683 }
catch (IOException ex) {
684 logger.log(Level.SEVERE,
"Failed to write table start: {0}", ex);
695 StringBuilder htmlOutput =
new StringBuilder();
696 htmlOutput.append(
"<table>\n<thead>\n\t<tr>\n");
699 for (String columnHeader : columnHeaders) {
700 htmlOutput.append(
"\t\t<th>").append(columnHeader).append(
"</th>\n");
704 htmlOutput.append(
"\t\t<th></th>\n");
706 htmlOutput.append(
"\t</tr>\n</thead>\n");
709 out.write(htmlOutput.toString());
710 }
catch (IOException ex) {
711 logger.log(Level.SEVERE,
"Failed to write table start: {0}", ex);
721 out.write(
"</table>\n");
722 }
catch (IOException ex) {
723 logger.log(Level.SEVERE,
"Failed to write end of table: {0}", ex);
745 private void addRow(List<String> row,
boolean escapeText) {
746 StringBuilder builder =
new StringBuilder();
747 builder.append(
"\t<tr>\n");
748 for (String cell : row) {
750 builder.append(
"\t\t<td>").append(cellText).append(
"</td>\n");
752 builder.append(
"\t</tr>\n");
756 out.write(builder.toString());
757 }
catch (IOException ex) {
758 logger.log(Level.SEVERE,
"Failed to write row to out.", ex);
759 }
catch (NullPointerException ex) {
760 logger.log(Level.SEVERE,
"Output writer is null. Page was not initialized before writing.", ex);
772 Content content = contentTag.getContent();
773 if (content instanceof AbstractFile ==
false) {
777 AbstractFile file = (AbstractFile) content;
779 StringBuilder localFileLink =
new StringBuilder();
782 || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
783 || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)) {
784 localFileLink.append(
"<a href=\"");
786 String localFilePath =
saveContent(file, contentTag.getName().getDisplayName());
787 localFileLink.append(localFilePath);
788 localFileLink.append(
"\" target=\"_top\">");
791 StringBuilder builder =
new StringBuilder();
792 builder.append(
"\t<tr>\n");
793 int positionCounter = 0;
794 for (String cell : row) {
796 switch (positionCounter) {
799 builder.append(
"\t\t<td class=\"left_align_cell\">").append(localFileLink.toString()).append(cell).append(
"</a></td>\n");
803 builder.append(
"\t\t<td class=\"right_align_cell\">").append(cell).append(
"</td>\n");
807 builder.append(
"\t\t<td>").append(cell).append(
"</td>\n");
812 builder.append(
"\t</tr>\n");
816 out.write(builder.toString());
817 }
catch (IOException ex) {
818 logger.log(Level.SEVERE,
"Failed to write row to out.", ex);
819 }
catch (NullPointerException ex) {
820 logger.log(Level.SEVERE,
"Output writer is null. Page was not initialized before writing.", ex);
831 ArrayList<ImageTagRegion> tagRegions =
new ArrayList<>();
832 contentTags.forEach((contentTag) -> {
836 if (contentViewerTag != null) {
837 tagRegions.add(contentViewerTag.
getDetails());
840 logger.log(Level.WARNING,
"Could not get content viewer tag "
841 +
"from case db for content_tag with id %d", contentTag.getId());
853 List<String> currentRow =
new ArrayList<>();
856 for (Content content : images) {
858 addRow(currentRow,
false);
862 if (totalCount == MAX_THUMBS_PER_PAGE) {
866 rowCount = totalCount;
871 startDataType(NbBundle.getMessage(
this.getClass(),
"ReportHTML.addThumbRows.dataType.title", pages),
872 NbBundle.getMessage(
this.getClass(),
"ReportHTML.addThumbRows.dataType.msg"));
873 List<String> emptyHeaders =
new ArrayList<>();
875 emptyHeaders.add(
"");
884 AbstractFile file = (AbstractFile) content;
885 List<ContentTag> contentTags =
new ArrayList<>();
887 String thumbnailPath = null;
888 String imageWithTagsFullPath = null;
895 if (!imageTags.isEmpty()) {
905 File thumbnailImageWithTagsFile = Paths.get(thumbsPath, FilenameUtils.removeExtension(fileName) +
".png").toFile();
907 fullImageWithTagsPath = FilenameUtils.removeExtension(fullImageWithTagsPath) +
".png";
908 File fullImageWithTagsFile = Paths.get(fullImageWithTagsPath).toFile();
911 ImageIO.write(thumbnailWithTags,
"png", thumbnailImageWithTagsFile);
912 ImageIO.write(fullImageWithTags,
"png", fullImageWithTagsFile);
914 thumbnailPath = THUMBS_REL_PATH + thumbnailImageWithTagsFile.getName();
916 imageWithTagsFullPath = fullImageWithTagsPath.substring(subPath.length());
918 }
catch (TskCoreException ex) {
919 logger.log(Level.WARNING,
"Could not get tags for file.", ex);
920 }
catch (IOException | InterruptedException | ExecutionException ex) {
921 logger.log(Level.WARNING,
"Could make marked up thumbnail.", ex);
925 if (thumbnailPath == null) {
929 if (thumbnailPath == null) {
932 String contentPath =
saveContent(file,
"original");
935 nameInImage = file.getUniquePath();
936 }
catch (TskCoreException ex) {
937 nameInImage = file.getName();
940 StringBuilder linkToThumbnail =
new StringBuilder();
941 linkToThumbnail.append(
"<div id='thumbnail_link'><a href=\"")
942 .append((imageWithTagsFullPath != null) ? imageWithTagsFullPath : contentPath)
943 .append(
"\" target=\"_top\"><img src=\"")
944 .append(thumbnailPath).append(
"\" title=\"").append(nameInImage).append(
"\"/></a><br>")
945 .append(file.getName()).append(
"<br>");
946 if (imageWithTagsFullPath != null) {
947 linkToThumbnail.append(
"<a href=\"").append(contentPath).append(
"\" target=\"_top\">View Original</a><br>");
950 if (!contentTags.isEmpty()) {
951 linkToThumbnail.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.thumbLink.tags"));
953 for (
int i = 0; i < contentTags.size(); i++) {
954 ContentTag tag = contentTags.get(i);
956 linkToThumbnail.append(tag.getName().getDisplayName()).append(notableString);
957 if (i != contentTags.size() - 1) {
958 linkToThumbnail.append(
", ");
962 linkToThumbnail.append(
"</div>");
963 currentRow.add(linkToThumbnail.toString());
968 if (currentRow.isEmpty() ==
false) {
969 int extraCells = THUMBNAIL_COLUMNS - currentRow.size();
970 for (
int i = 0; i < extraCells; i++) {
974 addRow(currentRow,
false);
978 rowCount = totalCount;
982 if (c instanceof AbstractFile ==
false) {
985 AbstractFile file = (AbstractFile) c;
987 || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
988 || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS;
996 StringBuilder localFilePath =
new StringBuilder();
998 localFilePath.append(subPath);
999 localFilePath.append(dirName2);
1000 File localFileFolder =
new File(localFilePath.toString());
1001 if (!localFileFolder.exists()) {
1002 localFileFolder.mkdirs();
1013 String objectIdSuffix =
"_" + file.getId();
1014 int lastDotIndex = fileName.lastIndexOf(
".");
1015 if (lastDotIndex != -1 && lastDotIndex != 0) {
1017 fileName = fileName.substring(0, lastDotIndex) + objectIdSuffix + fileName.substring(lastDotIndex, fileName.length());
1021 fileName += objectIdSuffix;
1023 localFilePath.append(File.separator);
1024 localFilePath.append(fileName);
1026 return localFilePath.toString();
1044 File localFile =
new File(localFilePath);
1045 if (!localFile.exists()) {
1050 return localFilePath.substring(subPath.length());
1062 SimpleDateFormat sdf =
new java.text.SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss");
1063 return sdf.format(
new java.util.Date(date * 1000));
1068 return "report.html";
1073 return NbBundle.getMessage(this.getClass(),
"ReportHTML.getName.text");
1078 return NbBundle.getMessage(this.getClass(),
"ReportHTML.getDesc.text");
1085 Writer cssOut = null;
1087 cssOut =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(subPath +
"index.css"),
"UTF-8"));
1088 String css =
"body {margin: 0px; padding: 0px; background: #FFFFFF; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353;}\n"
1090 "#content {padding: 30px;}\n"
1092 "#header {width:100%; padding: 10px; line-height: 25px; background: #07A; color: #FFF; font-size: 20px;}\n"
1094 "#pageHeaderFooter {width: 100%; padding: 10px; line-height: 25px; text-align: center; font-size: 20px;}\n"
1096 "h1 {font-size: 20px; font-weight: normal; color: #07A; padding: 0 0 7px 0; margin-top: 25px; border-bottom: 1px solid #D6D6D6;}\n"
1098 "h2 {font-size: 20px; font-weight: bolder; color: #07A;}\n"
1100 "h3 {font-size: 16px; color: #07A;}\n"
1102 "h4 {background: #07A; color: #FFF; font-size: 16px; margin: 0 0 0 25px; padding: 0; padding-left: 15px;}\n"
1104 "ul.nav {list-style-type: none; line-height: 35px; padding: 0px; margin-left: 15px;}\n"
1106 "ul li a {font-size: 14px; color: #444; text-decoration: none; padding-left: 25px;}\n"
1108 "ul li a:hover {text-decoration: underline;}\n"
1110 "p {margin: 0 0 20px 0;}\n"
1112 "table {white-space:nowrap; min-width: 700px; padding: 2; margin: 0; border-collapse: collapse; border-bottom: 2px solid #e5e5e5;}\n"
1114 ".keyword_list table {margin: 0 0 25px 25px; border-bottom: 2px solid #dedede;}\n"
1116 "table th {white-space:nowrap; display: table-cell; text-align: center; padding: 2px 4px; background: #e5e5e5; color: #777; font-size: 11px; text-shadow: #e9f9fd 0 1px 0; border-top: 1px solid #dedede; border-bottom: 2px solid #e5e5e5;}\n"
1118 "table .left_align_cell{display: table-cell; padding: 2px 4px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align: left; }\n"
1120 "table .right_align_cell{display: table-cell; padding: 2px 4px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align: right; }\n"
1122 "table td {white-space:nowrap; display: table-cell; padding: 2px 3px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align:left; vertical-align: text-top;}\n"
1124 "table tr:nth-child(even) td {background: #f3f3f3;}\n"
1126 "div#thumbnail_link {max-width: 200px; white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; word-wrap: break-word;}";
1128 }
catch (FileNotFoundException ex) {
1129 logger.log(Level.SEVERE,
"Could not find index.css file to write to.", ex);
1130 }
catch (UnsupportedEncodingException ex) {
1131 logger.log(Level.SEVERE,
"Did not recognize encoding when writing index.css.", ex);
1132 }
catch (IOException ex) {
1133 logger.log(Level.SEVERE,
"Error creating Writer for index.css.", ex);
1136 if (cssOut != null) {
1140 }
catch (IOException ex) {
1149 Writer indexOut = null;
1150 String indexFilePath = path +
"report.html";
1155 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
1159 indexOut =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(indexFilePath),
"UTF-8"));
1160 StringBuilder index =
new StringBuilder();
1163 if (iconPath == null) {
1165 iconPath = HTML_SUBDIR +
"favicon.ico";
1167 iconPath = Paths.get(reportBranding.
getAgencyLogoPath()).getFileName().toString();
1169 index.append(
"<head>\n<title>").append(reportTitle).append(
" ").append(
1170 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.title", currentCase.
getDisplayName())).append(
1172 index.append(
"<link rel=\"icon\" type=\"image/ico\" href=\"")
1173 .append(iconPath).append(
"\" />\n");
1174 index.append(
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n");
1175 index.append(
"</head>\n");
1176 index.append(
"<frameset cols=\"350px,*\">\n");
1177 index.append(
"<frame src=\"" + HTML_SUBDIR).append(File.separator).append(
"nav.html\" name=\"nav\">\n");
1178 index.append(
"<frame src=\"" + HTML_SUBDIR).append(File.separator).append(
"summary.html\" name=\"content\">\n");
1179 index.append(
"<noframes>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.noFrames.msg")).append(
"<br />\n");
1180 index.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.noFrames.seeNav")).append(
"<br />\n");
1181 index.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.seeSum")).append(
"</noframes>\n");
1182 index.append(
"</frameset>\n");
1183 index.append(
"</html>");
1184 indexOut.write(index.toString());
1185 openCase.
addReport(indexFilePath, NbBundle.getMessage(
this.getClass(),
1186 "ReportHTML.writeIndex.srcModuleName.text"),
"");
1187 }
catch (IOException ex) {
1188 logger.log(Level.SEVERE,
"Error creating Writer for report.html: {0}", ex);
1189 }
catch (TskCoreException ex) {
1190 String errorMessage = String.format(
"Error adding %s to case as a report", indexFilePath);
1191 logger.log(Level.SEVERE, errorMessage, ex);
1194 if (indexOut != null) {
1198 }
catch (IOException ex) {
1207 Writer navOut = null;
1209 navOut =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(subPath +
"nav.html"),
"UTF-8"));
1210 StringBuilder nav =
new StringBuilder();
1211 nav.append(
"<html>\n<head>\n\t<title>").append(
1212 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeNav.title"))
1213 .append(
"</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n");
1214 nav.append(
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n</head>\n<body>\n");
1215 nav.append(
"<div id=\"content\">\n<h1>").append(
1216 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeNav.h1")).append(
"</h1>\n");
1217 nav.append(
"<ul class=\"nav\">\n");
1218 nav.append(
"<li style=\"background: url(summary.png) left center no-repeat;\"><a href=\"summary.html\" target=\"content\">")
1219 .append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeNav.summary")).append(
"</a></li>\n");
1221 for (String dataType : dataTypes.keySet()) {
1224 nav.append(
"<li style=\"background: url('").append(iconFileName)
1225 .append(
"') left center no-repeat;\"><a href=\"")
1226 .append(dataTypeEsc).append(
".html\" target=\"content\">")
1227 .append(dataType).append(
" (").append(dataTypes.get(dataType))
1228 .append(
")</a></li>\n");
1230 nav.append(
"</ul>\n");
1231 nav.append(
"</div>\n</body>\n</html>");
1232 navOut.write(nav.toString());
1233 }
catch (IOException ex) {
1234 logger.log(Level.SEVERE,
"Failed to write end of report navigation menu: {0}", ex);
1236 if (navOut != null) {
1240 }
catch (IOException ex) {
1241 logger.log(Level.WARNING,
"Could not close navigation out writer.");
1246 InputStream in = null;
1247 OutputStream output = null;
1252 if (generatorLogoPath != null && !generatorLogoPath.isEmpty()) {
1253 File from =
new File(generatorLogoPath);
1254 File to =
new File(subPath);
1255 FileUtil.copyFile(FileUtil.toFileObject(from), FileUtil.toFileObject(to),
"generator_logo");
1259 if (agencyLogoPath != null && !agencyLogoPath.isEmpty()) {
1260 Path destinationPath = Paths.get(subPath);
1261 Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), destinationPath.resolve(Paths.get(agencyLogoPath).getFileName()));
1264 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/favicon.ico");
1265 output =
new FileOutputStream(
new File(subPath +
"favicon.ico"));
1266 FileUtil.copy(in, output);
1270 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/summary.png");
1271 output =
new FileOutputStream(
new File(subPath +
"summary.png"));
1272 FileUtil.copy(in, output);
1276 }
catch (IOException ex) {
1277 logger.log(Level.SEVERE,
"Failed to extract images for HTML report.", ex);
1279 if (output != null) {
1283 }
catch (IOException ex) {
1289 }
catch (IOException ex) {
1299 Writer output = null;
1301 output =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(subPath +
"summary.html"),
"UTF-8"));
1302 StringBuilder head =
new StringBuilder();
1303 head.append(
"<html>\n<head>\n<title>").append(
1304 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.title")).append(
"</title>\n");
1305 head.append(
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n");
1306 head.append(
"<style type=\"text/css\">\n");
1307 head.append(
"#pageHeaderFooter {width: 100%; padding: 10px; line-height: 25px; text-align: center; font-size: 20px;}\n");
1308 head.append(
"body { padding: 0px; margin: 0px; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353; }\n");
1309 head.append(
"#wrapper { width: 90%; margin: 0px auto; margin-top: 35px; }\n");
1310 head.append(
"h1 { color: #07A; font-size: 36px; line-height: 42px; font-weight: normal; margin: 0px; border-bottom: 1px solid #81B9DB; }\n");
1311 head.append(
"h1 span { color: #F00; display: block; font-size: 16px; font-weight: bold; line-height: 22px;}\n");
1312 head.append(
"h2 { padding: 0 0 3px 0; margin: 0px; color: #07A; font-weight: normal; border-bottom: 1px dotted #81B9DB; }\n");
1313 head.append(
"h3 { padding: 5 0 3px 0; margin: 0px; color: #07A; font-weight: normal; }\n");
1314 head.append(
"table td { padding: 5px 25px 5px 0px; vertical-align:top;}\n");
1315 head.append(
"p.subheadding { padding: 0px; margin: 0px; font-size: 11px; color: #B5B5B5; }\n");
1316 head.append(
".title { width: 660px; margin-bottom: 50px; }\n");
1317 head.append(
".left { float: left; width: 250px; margin-top: 20px; text-align: center; }\n");
1318 head.append(
".left img { max-width: 250px; max-height: 250px; min-width: 200px; min-height: 200px; }\n");
1319 head.append(
".right { float: right; width: 385px; margin-top: 25px; font-size: 14px; }\n");
1320 head.append(
".clear { clear: both; }\n");
1321 head.append(
".info { padding: 10px 0;}\n");
1322 head.append(
".info p { padding: 3px 10px; background: #e5e5e5; color: #777; font-size: 12px; font-weight: bold; text-shadow: #e9f9fd 0 1px 0; border-top: 1px solid #dedede; border-bottom: 2px solid #dedede; }\n");
1323 head.append(
".info table { margin: 10px 25px 10px 25px; }\n");
1324 head.append(
"ul {padding: 0;margin: 0;list-style-type: none;}");
1325 head.append(
"li {padding-bottom: 5px;}");
1326 head.append(
"</style>\n");
1327 head.append(
"</head>\n<body>\n");
1328 output.write(head.toString());
1330 DateFormat datetimeFormat =
new SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss");
1331 Date date =
new Date();
1332 String datetime = datetimeFormat.format(date);
1334 StringBuilder summary =
new StringBuilder();
1335 boolean running =
false;
1340 List<IngestJobInfo> ingestJobs = skCase.getIngestJobs();
1345 summary.append(
"<div id=\"wrapper\">\n");
1347 summary.append(
"<h1>").append(reportTitle)
1348 .append(running ? NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.warningMsg") :
"")
1350 summary.append(
"<p class=\"subheadding\">").append(
1351 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.reportGenOn.text", datetime)).append(
"</p>\n");
1352 summary.append(
"<div class=\"title\">\n");
1357 if (generatorLogoSet) {
1358 summary.append(
"<div class=\"left\">\n");
1359 summary.append(
"<img src=\"generator_logo.png\" />\n");
1360 summary.append(
"</div>\n");
1362 summary.append(
"<div class=\"clear\"></div>\n");
1363 if (reportFooter != null) {
1364 summary.append(
"<p class=\"subheadding\">").append(reportFooter).append(
"</p>\n");
1366 summary.append(
"</div>\n");
1368 summary.append(
"</body></html>");
1369 output.write(summary.toString());
1370 }
catch (FileNotFoundException ex) {
1371 logger.log(Level.SEVERE,
"Could not find summary.html file to write to.");
1372 }
catch (UnsupportedEncodingException ex) {
1373 logger.log(Level.SEVERE,
"Did not recognize encoding when writing summary.hmtl.");
1374 }
catch (IOException ex) {
1375 logger.log(Level.SEVERE,
"Error creating Writer for summary.html.");
1377 logger.log(Level.WARNING,
"Unable to get current sleuthkit Case for the HTML report.");
1380 if (output != null) {
1384 }
catch (IOException ex) {
1390 "ReportHTML.writeSum.case=Case:",
1391 "ReportHTML.writeSum.caseNumber=Case Number:",
1392 "ReportHTML.writeSum.caseNumImages=Number of data sources in case:",
1393 "ReportHTML.writeSum.caseNotes=Notes:",
1394 "ReportHTML.writeSum.examiner=Examiner:"
1402 StringBuilder summary =
new StringBuilder();
1408 String caseNumber = currentCase.
getNumber();
1412 }
catch (TskCoreException ex) {
1421 summary.append(
"<div class=\"title\">\n");
1422 if (agencyLogoSet) {
1423 summary.append(
"<div class=\"left\">\n");
1424 summary.append(
"<img src=\"");
1425 summary.append(Paths.get(reportBranding.
getAgencyLogoPath()).getFileName().toString());
1426 summary.append(
"\" />\n");
1427 summary.append(
"</div>\n");
1429 final String align = agencyLogoSet ?
"right" :
"left";
1430 summary.append(
"<div class=\"").append(align).append(
"\">\n");
1431 summary.append(
"<table>\n");
1434 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_case()).append(
"</td><td>")
1437 if (!caseNumber.isEmpty()) {
1438 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_caseNumber()).append(
"</td><td>")
1442 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_caseNumImages()).append(
"</td><td>")
1443 .append(imagecount).append(
"</td></tr>\n");
1445 if (!caseNotes.isEmpty()) {
1446 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_caseNotes()).append(
"</td><td>")
1451 if (!examinerName.isEmpty()) {
1452 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_examiner()).append(
"</td><td>")
1457 summary.append(
"</table>\n");
1458 summary.append(
"</div>\n");
1459 summary.append(
"<div class=\"clear\"></div>\n");
1460 summary.append(
"</div>\n");
1470 StringBuilder summary =
new StringBuilder();
1471 summary.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.imageInfoHeading"));
1472 summary.append(
"<div class=\"info\">\n");
1475 summary.append(
"<p>").append(c.getName()).append(
"</p>\n");
1476 if (c instanceof Image) {
1477 Image img = (Image) c;
1479 summary.append(
"<table>\n");
1480 summary.append(
"<tr><td>").append(
1481 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.timezone"))
1482 .append(
"</td><td>").append(img.getTimeZone()).append(
"</td></tr>\n");
1483 for (String imgPath : img.getPaths()) {
1484 summary.append(
"<tr><td>").append(
1485 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.path"))
1486 .append(
"</td><td>").append(imgPath).append(
"</td></tr>\n");
1488 summary.append(
"</table>\n");
1491 }
catch (TskCoreException ex) {
1492 logger.log(Level.WARNING,
"Unable to get image information for the HTML report.");
1494 summary.append(
"</div>\n");
1504 StringBuilder summary =
new StringBuilder();
1505 summary.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.softwareInfoHeading"));
1506 summary.append(
"<div class=\"info\">\n");
1507 summary.append(
"<table>\n");
1508 summary.append(
"<tr><td>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.autopsyVersion"))
1510 Map<Long, IngestModuleInfo> moduleInfoHashMap =
new HashMap<>();
1511 for (IngestJobInfo ingestJob : ingestJobs) {
1512 List<IngestModuleInfo> ingestModules = ingestJob.getIngestModuleInfo();
1513 for (IngestModuleInfo ingestModule : ingestModules) {
1514 if (!moduleInfoHashMap.containsKey(ingestModule.getIngestModuleId())) {
1515 moduleInfoHashMap.put(ingestModule.getIngestModuleId(), ingestModule);
1519 TreeMap<String, String> modules =
new TreeMap<>();
1520 for (IngestModuleInfo moduleinfo : moduleInfoHashMap.values()) {
1521 modules.put(moduleinfo.getDisplayName(), moduleinfo.getVersion());
1523 for (Map.Entry<String, String> module : modules.entrySet()) {
1524 summary.append(
"<tr><td>").append(module.getKey()).append(
" Module:")
1525 .append(
"</td><td>").append(module.getValue()).append(
"</td></tr>\n");
1527 summary.append(
"</table>\n");
1528 summary.append(
"</div>\n");
1529 summary.append(
"<div class=\"clear\"></div>\n");
1539 StringBuilder summary =
new StringBuilder();
1541 summary.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.ingestHistoryHeading"));
1542 summary.append(
"<div class=\"info\">\n");
1545 for (IngestJobInfo ingestJob : ingestJobs) {
1546 summary.append(
"<h3>Job ").append(jobnumber).append(
":</h3>\n");
1547 summary.append(
"<table>\n");
1548 summary.append(
"<tr><td>").append(
"Data Source:")
1549 .append(
"</td><td>").append(skCase.getContentById(ingestJob.getObjectId()).
getName()).append(
"</td></tr>\n");
1550 summary.append(
"<tr><td>").append(
"Status:")
1551 .append(
"</td><td>").append(ingestJob.getStatus()).append(
"</td></tr>\n");
1552 summary.append(
"<tr><td>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.modulesEnabledHeading"))
1553 .append(
"</td><td>");
1554 List<IngestModuleInfo> ingestModules = ingestJob.getIngestModuleInfo();
1555 summary.append(
"<ul>\n");
1556 for (IngestModuleInfo ingestModule : ingestModules) {
1557 summary.append(
"<li>").append(ingestModule.getDisplayName()).append(
"</li>");
1559 summary.append(
"</ul>\n");
1561 summary.append(
"</td></tr>\n");
1562 summary.append(
"</table>\n");
1564 summary.append(
"</div>\n");
1565 }
catch (TskCoreException ex) {
1566 logger.log(Level.WARNING,
"Unable to get ingest jobs for the HTML report.");
1588 File thumbFile = Paths.get(thumbsPath, fileName +
".png").toFile();
1589 if (bufferedThumb == null) {
1593 ImageIO.write(bufferedThumb,
"png", thumbFile);
1594 }
catch (IOException ex) {
1595 logger.log(Level.WARNING,
"Failed to write thumb file to report directory.", ex);
1598 if (thumbFile.exists()
1602 return THUMBS_REL_PATH
1603 + thumbFile.getName();
1615 String formattedString = StringEscapeUtils.escapeHtml4(text);
1616 return formattedString.replaceAll(
"(\r\n|\r|\n|\n\r)",
"<br>");
List< Content > getDataSources()
static String escapeHtml(String toEscape)
String prepareThumbnail(AbstractFile file)
void startContentTagsTable(List< String > columnHeaders)
ReportModuleSettings getConfiguration()
String getAgencyLogoPath()
List< ImageTagRegion > getTaggedRegions(List< ContentTag > contentTags)
String getGeneratorLogoPath()
static synchronized IngestManager getInstance()
void addRow(List< String > row, boolean escapeText)
static HTMLReport instance
String useDataTypeIcon(String dataType)
static final String HTML_SUBDIR
JPanel getConfigurationPanel()
void addSetIndex(List< String > sets)
void setConfiguration(ReportModuleSettings settings)
String getRelativeFilePath()
boolean isIngestRunning()
StringBuilder writeSummaryImageInfo()
void addReport(String localPath, String srcModuleName, String reportName)
static BufferedImage getImageWithTags(AbstractFile file, Collection< ImageTagRegion > tagRegions)
static final String THUMBS_REL_PATH
void addThumbnailRows(Set< Content > images)
static final Logger logger
HTMLReportConfigurationPanel configPanel
void addRow(List< String > row)
static< T > ContentViewerTag< T > getTag(ContentTag contentTag, Class< T > clazz)
static BufferedImage getThumbnailWithTags(AbstractFile file, Collection< ImageTagRegion > tagRegions, IconSize iconSize)
Map< String, Integer > dataTypes
void startDataType(String name, String description)
void addRowWithTaggedContentHyperlink(List< String > row, ContentTag contentTag)
static final int ICON_SIZE_MEDIUM
static synchronized HTMLReport getDefault()
TagsManager getTagsManager()
String dataTypeToFileName(String dataType)
StringBuilder writeSummaryIngestHistoryInfo(SleuthkitCase skCase, List< IngestJobInfo > ingestJobs)
ReportModuleSettings getDefaultConfiguration()
StringBuilder writeSummaryCaseDetails()
String dateToString(long date)
void addSetElement(String elementName)
String formatHtmlString(String text)
boolean failsContentCheck(Content c)
void close(ProgressIndicator progressIndicator)
SleuthkitCase getSleuthkitCase()
static Integer THUMBNAIL_COLUMNS
static final int MAX_THUMBS_PER_PAGE
final ReportBranding reportBranding
static String escapeFileName(String fileName)
StringBuilder writeSummarySoftwareInfo(SleuthkitCase skCase, List< IngestJobInfo > ingestJobs)
void startTable(List< String > titles)
static Case getCurrentCase()
synchronized static Logger getLogger(String name)
String makeCustomUniqueFilePath(AbstractFile file, String dirName)
void startReport(String baseReportDir)
static Case getCurrentCaseThrows()
String saveContent(AbstractFile file, String dirName)
static BufferedImage getThumbnail(Content content, int iconSize)
static String getVersion()
void startSet(String setName)