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.text.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:
330 case TSK_INTERESTING_FILE_HIT:
332 case TSK_INTERESTING_ITEM:
333 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/interesting_item.png");
336 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/installed.png");
338 case TSK_REMOTE_DRIVE:
339 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/drive_network.png");
342 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/os-account.png");
344 case TSK_OBJECT_DETECTED:
345 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/objects.png");
347 case TSK_WEB_FORM_AUTOFILL:
348 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/web-form.png");
351 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/cache.png");
353 case TSK_USER_CONTENT_SUSPECTED:
354 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/user-content.png");
357 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/metadata.png");
359 case TSK_CLIPBOARD_CONTENT:
360 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/clipboard.png");
363 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/accounts.png");
365 case TSK_WIFI_NETWORK:
366 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/network-wifi.png");
368 case TSK_WIFI_NETWORK_ADAPTER:
369 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/network-wifi.png");
371 case TSK_SIM_ATTACHED:
372 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/sim_card.png");
374 case TSK_BLUETOOTH_ADAPTER:
375 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/Bluetooth.png");
377 case TSK_DEVICE_INFO:
378 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/devices.png");
380 case TSK_VERIFICATION_FAILED:
381 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/validationFailed.png");
383 case TSK_WEB_ACCOUNT_TYPE:
384 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/web-account-type.png");
386 case TSK_WEB_FORM_ADDRESS:
387 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/web-form-address.png");
390 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/gps-area.png");
392 case TSK_WEB_CATEGORIZATION:
393 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/domain-16.png");
396 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/yara_16.png");
398 case TSK_PREVIOUSLY_SEEN:
399 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/previously-seen.png");
401 case TSK_PREVIOUSLY_UNSEEN:
402 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/previously-unseen.png");
404 case TSK_PREVIOUSLY_NOTABLE:
405 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/red-circle-exclamation.png");
408 logger.log(Level.WARNING,
"useDataTypeIcon: unhandled artifact type = {0}", dataType);
409 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/star.png");
410 iconFileName =
"star.png";
411 iconFilePath = subPath + File.separator + iconFileName;
414 }
else if (dataType.startsWith(ARTIFACT_TYPE.TSK_ACCOUNT.getDisplayName())) {
422 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/accounts.png");
423 iconFileName =
"accounts.png";
424 iconFilePath = subPath + File.separator + iconFileName;
426 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/star.png");
427 iconFileName =
"star.png";
428 iconFilePath = subPath + File.separator + iconFileName;
432 output =
new FileOutputStream(iconFilePath);
433 FileUtil.copy(in, output);
436 }
catch (IOException ex) {
437 logger.log(Level.SEVERE,
"Failed to extract images for HTML report.", ex);
439 if (output != null) {
443 }
catch (IOException ex) {
449 }
catch (IOException ex) {
470 logger.log(Level.SEVERE,
"Exception while getting open case.");
474 this.path = baseReportDir;
475 this.subPath = this.path + HTML_SUBDIR + File.separator;
478 FileUtil.createFolder(
new File(this.subPath));
479 FileUtil.createFolder(
new File(this.thumbsPath));
480 }
catch (IOException ex) {
481 logger.log(Level.SEVERE,
"Unable to make HTML report folder.");
499 }
catch (IOException ex) {
500 logger.log(Level.WARNING,
"Could not close the output writer when ending report.", ex);
517 out =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(subPath + title +
".html"),
"UTF-8"));
518 }
catch (FileNotFoundException ex) {
519 logger.log(Level.SEVERE,
"File not found: {0}", ex);
520 }
catch (UnsupportedEncodingException ex) {
521 logger.log(Level.SEVERE,
"Unrecognized encoding");
525 StringBuilder page =
new StringBuilder();
526 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")
528 .append(
"<div id=\"header\">").append(name).append(
"</div>\n")
529 .append(
"<div id=\"content\">\n");
530 if (!description.isEmpty()) {
531 page.append(
"<p><strong>");
532 page.append(description);
533 page.append(
"</strong></p>\n");
535 out.write(page.toString());
536 currentDataType = name;
538 }
catch (IOException ex) {
539 logger.log(Level.SEVERE,
"Failed to write page head: {0}", ex);
549 dataTypes.put(currentDataType, rowCount);
551 StringBuilder builder =
new StringBuilder();
553 builder.append(
"</div>\n</body>\n</html>\n");
554 out.write(builder.toString());
555 }
catch (IOException ex) {
556 logger.log(Level.SEVERE,
"Failed to write end of HTML report.", ex);
562 }
catch (IOException ex) {
563 logger.log(Level.WARNING,
"Could not close the output writer when ending data type.", ex);
577 StringBuilder output =
new StringBuilder();
578 String pageHeader = configPanel.getHeader();
579 if (pageHeader.isEmpty() ==
false) {
580 output.append(
"<div id=\"pageHeaderFooter\">")
581 .append(StringEscapeUtils.escapeHtml4(pageHeader))
584 return output.toString();
594 StringBuilder output =
new StringBuilder();
595 String pageFooter = configPanel.getFooter();
596 if (pageFooter.isEmpty() ==
false) {
597 output.append(
"<br/><div id=\"pageHeaderFooter\">")
598 .append(StringEscapeUtils.escapeHtml4(pageFooter))
601 return output.toString();
611 StringBuilder set =
new StringBuilder();
612 set.append(
"<h1><a name=\"").append(setName).append(
"\">").append(setName).append(
"</a></h1>\n");
613 set.append(
"<div class=\"keyword_list\">\n");
616 out.write(set.toString());
617 }
catch (IOException ex) {
618 logger.log(Level.SEVERE,
"Failed to write set: {0}", ex);
628 out.write(
"</div>\n");
629 }
catch (IOException ex) {
630 logger.log(Level.SEVERE,
"Failed to write end of set: {0}", ex);
641 StringBuilder index =
new StringBuilder();
642 index.append(
"<ul>\n");
643 for (String set : sets) {
644 index.append(
"\t<li><a href=\"#").append(set).append(
"\">").append(set).append(
"</a></li>\n");
646 index.append(
"</ul>\n");
648 out.write(index.toString());
649 }
catch (IOException ex) {
650 logger.log(Level.SEVERE,
"Failed to add set index: {0}", ex);
662 out.write(
"<h4>" + elementName +
"</h4>\n");
663 }
catch (IOException ex) {
664 logger.log(Level.SEVERE,
"Failed to write set element: {0}", ex);
675 StringBuilder ele =
new StringBuilder();
676 ele.append(
"<table>\n<thead>\n\t<tr>\n");
677 for (String title : titles) {
678 ele.append(
"\t\t<th>").append(title).append(
"</th>\n");
680 ele.append(
"\t</tr>\n</thead>\n");
683 out.write(ele.toString());
684 }
catch (IOException ex) {
685 logger.log(Level.SEVERE,
"Failed to write table start: {0}", ex);
696 StringBuilder htmlOutput =
new StringBuilder();
697 htmlOutput.append(
"<table>\n<thead>\n\t<tr>\n");
700 for (String columnHeader : columnHeaders) {
701 htmlOutput.append(
"\t\t<th>").append(columnHeader).append(
"</th>\n");
705 htmlOutput.append(
"\t\t<th></th>\n");
707 htmlOutput.append(
"\t</tr>\n</thead>\n");
710 out.write(htmlOutput.toString());
711 }
catch (IOException ex) {
712 logger.log(Level.SEVERE,
"Failed to write table start: {0}", ex);
722 out.write(
"</table>\n");
723 }
catch (IOException ex) {
724 logger.log(Level.SEVERE,
"Failed to write end of table: {0}", ex);
746 private void addRow(List<String> row,
boolean escapeText) {
747 StringBuilder builder =
new StringBuilder();
748 builder.append(
"\t<tr>\n");
749 for (String cell : row) {
751 builder.append(
"\t\t<td>").append(cellText).append(
"</td>\n");
753 builder.append(
"\t</tr>\n");
757 out.write(builder.toString());
758 }
catch (IOException ex) {
759 logger.log(Level.SEVERE,
"Failed to write row to out.", ex);
760 }
catch (NullPointerException ex) {
761 logger.log(Level.SEVERE,
"Output writer is null. Page was not initialized before writing.", ex);
773 Content content = contentTag.getContent();
774 if (content instanceof AbstractFile ==
false) {
778 AbstractFile file = (AbstractFile) content;
780 StringBuilder localFileLink =
new StringBuilder();
783 || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
784 || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)) {
785 localFileLink.append(
"<a href=\"");
787 String localFilePath =
saveContent(file, contentTag.getName().getDisplayName());
788 localFileLink.append(localFilePath);
789 localFileLink.append(
"\" target=\"_top\">");
792 StringBuilder builder =
new StringBuilder();
793 builder.append(
"\t<tr>\n");
794 int positionCounter = 0;
795 for (String cell : row) {
797 switch (positionCounter) {
800 builder.append(
"\t\t<td class=\"left_align_cell\">").append(localFileLink.toString()).append(cell).append(
"</a></td>\n");
804 builder.append(
"\t\t<td class=\"right_align_cell\">").append(cell).append(
"</td>\n");
808 builder.append(
"\t\t<td>").append(cell).append(
"</td>\n");
813 builder.append(
"\t</tr>\n");
817 out.write(builder.toString());
818 }
catch (IOException ex) {
819 logger.log(Level.SEVERE,
"Failed to write row to out.", ex);
820 }
catch (NullPointerException ex) {
821 logger.log(Level.SEVERE,
"Output writer is null. Page was not initialized before writing.", ex);
833 ArrayList<ImageTagRegion> tagRegions =
new ArrayList<>();
834 contentTags.forEach((contentTag) -> {
838 if (contentViewerTag != null) {
839 tagRegions.add(contentViewerTag.
getDetails());
842 logger.log(Level.WARNING,
"Could not get content viewer tag "
843 +
"from case db for content_tag with id %d", contentTag.getId());
855 List<String> currentRow =
new ArrayList<>();
858 for (Content content : images) {
860 addRow(currentRow,
false);
864 if (totalCount == MAX_THUMBS_PER_PAGE) {
868 rowCount = totalCount;
873 startDataType(NbBundle.getMessage(
this.getClass(),
"ReportHTML.addThumbRows.dataType.title", pages),
874 NbBundle.getMessage(
this.getClass(),
"ReportHTML.addThumbRows.dataType.msg"));
875 List<String> emptyHeaders =
new ArrayList<>();
877 emptyHeaders.add(
"");
886 AbstractFile file = (AbstractFile) content;
887 List<ContentTag> contentTags =
new ArrayList<>();
889 String thumbnailPath = null;
890 String imageWithTagsFullPath = null;
897 if (!imageTags.isEmpty()) {
907 File thumbnailImageWithTagsFile = Paths.get(thumbsPath, FilenameUtils.removeExtension(fileName) +
".png").toFile();
909 fullImageWithTagsPath = FilenameUtils.removeExtension(fullImageWithTagsPath) +
".png";
910 File fullImageWithTagsFile = Paths.get(fullImageWithTagsPath).toFile();
913 ImageIO.write(thumbnailWithTags,
"png", thumbnailImageWithTagsFile);
914 ImageIO.write(fullImageWithTags,
"png", fullImageWithTagsFile);
916 thumbnailPath = THUMBS_REL_PATH + thumbnailImageWithTagsFile.getName();
918 imageWithTagsFullPath = fullImageWithTagsPath.substring(subPath.length());
920 }
catch (TskCoreException ex) {
921 logger.log(Level.WARNING,
"Could not get tags for file.", ex);
922 }
catch (IOException | InterruptedException | ExecutionException ex) {
923 logger.log(Level.WARNING,
"Could make marked up thumbnail.", ex);
927 if (thumbnailPath == null) {
931 if (thumbnailPath == null) {
934 String contentPath =
saveContent(file,
"original");
937 nameInImage = file.getUniquePath();
938 }
catch (TskCoreException ex) {
939 nameInImage = file.getName();
942 StringBuilder linkToThumbnail =
new StringBuilder();
943 linkToThumbnail.append(
"<div id='thumbnail_link'><a href=\"")
944 .append((imageWithTagsFullPath != null) ? imageWithTagsFullPath : contentPath)
945 .append(
"\" target=\"_top\"><img src=\"")
946 .append(thumbnailPath).append(
"\" title=\"").append(nameInImage).append(
"\"/></a><br>")
947 .append(file.getName()).append(
"<br>");
948 if (imageWithTagsFullPath != null) {
949 linkToThumbnail.append(
"<a href=\"").append(contentPath).append(
"\" target=\"_top\">View Original</a><br>");
952 if (!contentTags.isEmpty()) {
953 linkToThumbnail.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.thumbLink.tags"));
955 for (
int i = 0; i < contentTags.size(); i++) {
956 ContentTag tag = contentTags.get(i);
958 linkToThumbnail.append(tag.getName().getDisplayName()).append(notableString);
959 if (i != contentTags.size() - 1) {
960 linkToThumbnail.append(
", ");
964 linkToThumbnail.append(
"</div>");
965 currentRow.add(linkToThumbnail.toString());
970 if (currentRow.isEmpty() ==
false) {
971 int extraCells = THUMBNAIL_COLUMNS - currentRow.size();
972 for (
int i = 0; i < extraCells; i++) {
976 addRow(currentRow,
false);
980 rowCount = totalCount;
984 if (c instanceof AbstractFile ==
false) {
987 AbstractFile file = (AbstractFile) c;
989 || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
990 || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS;
998 StringBuilder localFilePath =
new StringBuilder();
1000 localFilePath.append(subPath);
1001 localFilePath.append(dirName2);
1002 File localFileFolder =
new File(localFilePath.toString());
1003 if (!localFileFolder.exists()) {
1004 localFileFolder.mkdirs();
1015 String objectIdSuffix =
"_" + file.getId();
1016 int lastDotIndex = fileName.lastIndexOf(
".");
1017 if (lastDotIndex != -1 && lastDotIndex != 0) {
1019 fileName = fileName.substring(0, lastDotIndex) + objectIdSuffix + fileName.substring(lastDotIndex, fileName.length());
1023 fileName += objectIdSuffix;
1025 localFilePath.append(File.separator);
1026 localFilePath.append(fileName);
1028 return localFilePath.toString();
1046 File localFile =
new File(localFilePath);
1047 if (!localFile.exists()) {
1052 return localFilePath.substring(subPath.length());
1064 SimpleDateFormat sdf =
new java.text.SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss");
1065 return sdf.format(
new java.util.Date(date * 1000));
1070 return "report.html";
1075 return NbBundle.getMessage(this.getClass(),
"ReportHTML.getName.text");
1080 return NbBundle.getMessage(this.getClass(),
"ReportHTML.getDesc.text");
1087 Writer cssOut = null;
1089 cssOut =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(subPath +
"index.css"),
"UTF-8"));
1090 String css =
"body {margin: 0px; padding: 0px; background: #FFFFFF; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353;}\n"
1092 "#content {padding: 30px;}\n"
1094 "#header {width:100%; padding: 10px; line-height: 25px; background: #07A; color: #FFF; font-size: 20px;}\n"
1096 "#pageHeaderFooter {width: 100%; padding: 10px; line-height: 25px; text-align: center; font-size: 20px;}\n"
1098 "h1 {font-size: 20px; font-weight: normal; color: #07A; padding: 0 0 7px 0; margin-top: 25px; border-bottom: 1px solid #D6D6D6;}\n"
1100 "h2 {font-size: 20px; font-weight: bolder; color: #07A;}\n"
1102 "h3 {font-size: 16px; color: #07A;}\n"
1104 "h4 {background: #07A; color: #FFF; font-size: 16px; margin: 0 0 0 25px; padding: 0; padding-left: 15px;}\n"
1106 "ul.nav {list-style-type: none; line-height: 35px; padding: 0px; margin-left: 15px;}\n"
1108 "ul li a {font-size: 14px; color: #444; text-decoration: none; padding-left: 25px;}\n"
1110 "ul li a:hover {text-decoration: underline;}\n"
1112 "p {margin: 0 0 20px 0;}\n"
1114 "table {white-space:nowrap; min-width: 700px; padding: 2; margin: 0; border-collapse: collapse; border-bottom: 2px solid #e5e5e5;}\n"
1116 ".keyword_list table {margin: 0 0 25px 25px; border-bottom: 2px solid #dedede;}\n"
1118 "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"
1120 "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"
1122 "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"
1124 "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"
1126 "table tr:nth-child(even) td {background: #f3f3f3;}\n"
1128 "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;}";
1130 }
catch (FileNotFoundException ex) {
1131 logger.log(Level.SEVERE,
"Could not find index.css file to write to.", ex);
1132 }
catch (UnsupportedEncodingException ex) {
1133 logger.log(Level.SEVERE,
"Did not recognize encoding when writing index.css.", ex);
1134 }
catch (IOException ex) {
1135 logger.log(Level.SEVERE,
"Error creating Writer for index.css.", ex);
1138 if (cssOut != null) {
1142 }
catch (IOException ex) {
1151 Writer indexOut = null;
1152 String indexFilePath = path +
"report.html";
1157 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
1161 indexOut =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(indexFilePath),
"UTF-8"));
1162 StringBuilder index =
new StringBuilder();
1165 if (iconPath == null) {
1167 iconPath = HTML_SUBDIR +
"favicon.ico";
1169 iconPath = Paths.get(reportBranding.
getAgencyLogoPath()).getFileName().toString();
1171 index.append(
"<head>\n<title>").append(reportTitle).append(
" ").append(
1172 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.title", currentCase.
getDisplayName())).append(
1174 index.append(
"<link rel=\"icon\" type=\"image/ico\" href=\"")
1175 .append(iconPath).append(
"\" />\n");
1176 index.append(
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n");
1177 index.append(
"</head>\n");
1178 index.append(
"<frameset cols=\"350px,*\">\n");
1179 index.append(
"<frame src=\"" + HTML_SUBDIR).append(File.separator).append(
"nav.html\" name=\"nav\">\n");
1180 index.append(
"<frame src=\"" + HTML_SUBDIR).append(File.separator).append(
"summary.html\" name=\"content\">\n");
1181 index.append(
"<noframes>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.noFrames.msg")).append(
"<br />\n");
1182 index.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.noFrames.seeNav")).append(
"<br />\n");
1183 index.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.seeSum")).append(
"</noframes>\n");
1184 index.append(
"</frameset>\n");
1185 index.append(
"</html>");
1186 indexOut.write(index.toString());
1187 openCase.
addReport(indexFilePath, NbBundle.getMessage(
this.getClass(),
1188 "ReportHTML.writeIndex.srcModuleName.text"),
"");
1189 }
catch (IOException ex) {
1190 logger.log(Level.SEVERE,
"Error creating Writer for report.html: {0}", ex);
1191 }
catch (TskCoreException ex) {
1192 String errorMessage = String.format(
"Error adding %s to case as a report", indexFilePath);
1193 logger.log(Level.SEVERE, errorMessage, ex);
1196 if (indexOut != null) {
1200 }
catch (IOException ex) {
1209 Writer navOut = null;
1211 navOut =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(subPath +
"nav.html"),
"UTF-8"));
1212 StringBuilder nav =
new StringBuilder();
1213 nav.append(
"<html>\n<head>\n\t<title>").append(
1214 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeNav.title"))
1215 .append(
"</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n");
1216 nav.append(
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n</head>\n<body>\n");
1217 nav.append(
"<div id=\"content\">\n<h1>").append(
1218 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeNav.h1")).append(
"</h1>\n");
1219 nav.append(
"<ul class=\"nav\">\n");
1220 nav.append(
"<li style=\"background: url(summary.png) left center no-repeat;\"><a href=\"summary.html\" target=\"content\">")
1221 .append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeNav.summary")).append(
"</a></li>\n");
1223 for (String dataType : dataTypes.keySet()) {
1226 nav.append(
"<li style=\"background: url('").append(iconFileName)
1227 .append(
"') left center no-repeat;\"><a href=\"")
1228 .append(dataTypeEsc).append(
".html\" target=\"content\">")
1229 .append(dataType).append(
" (").append(dataTypes.get(dataType))
1230 .append(
")</a></li>\n");
1232 nav.append(
"</ul>\n");
1233 nav.append(
"</div>\n</body>\n</html>");
1234 navOut.write(nav.toString());
1235 }
catch (IOException ex) {
1236 logger.log(Level.SEVERE,
"Failed to write end of report navigation menu: {0}", ex);
1238 if (navOut != null) {
1242 }
catch (IOException ex) {
1243 logger.log(Level.WARNING,
"Could not close navigation out writer.");
1248 InputStream in = null;
1249 OutputStream output = null;
1254 if (generatorLogoPath != null && !generatorLogoPath.isEmpty()) {
1255 File from =
new File(generatorLogoPath);
1256 File to =
new File(subPath);
1257 FileUtil.copyFile(FileUtil.toFileObject(from), FileUtil.toFileObject(to),
"generator_logo");
1261 if (agencyLogoPath != null && !agencyLogoPath.isEmpty()) {
1262 Path destinationPath = Paths.get(subPath);
1263 Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), destinationPath.resolve(Paths.get(agencyLogoPath).getFileName()));
1266 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/favicon.ico");
1267 output =
new FileOutputStream(
new File(subPath +
"favicon.ico"));
1268 FileUtil.copy(in, output);
1272 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/summary.png");
1273 output =
new FileOutputStream(
new File(subPath +
"summary.png"));
1274 FileUtil.copy(in, output);
1278 }
catch (IOException ex) {
1279 logger.log(Level.SEVERE,
"Failed to extract images for HTML report.", ex);
1281 if (output != null) {
1285 }
catch (IOException ex) {
1291 }
catch (IOException ex) {
1301 Writer output = null;
1303 output =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(subPath +
"summary.html"),
"UTF-8"));
1304 StringBuilder head =
new StringBuilder();
1305 head.append(
"<html>\n<head>\n<title>").append(
1306 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.title")).append(
"</title>\n");
1307 head.append(
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n");
1308 head.append(
"<style type=\"text/css\">\n");
1309 head.append(
"#pageHeaderFooter {width: 100%; padding: 10px; line-height: 25px; text-align: center; font-size: 20px;}\n");
1310 head.append(
"body { padding: 0px; margin: 0px; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353; }\n");
1311 head.append(
"#wrapper { width: 90%; margin: 0px auto; margin-top: 35px; }\n");
1312 head.append(
"h1 { color: #07A; font-size: 36px; line-height: 42px; font-weight: normal; margin: 0px; border-bottom: 1px solid #81B9DB; }\n");
1313 head.append(
"h1 span { color: #F00; display: block; font-size: 16px; font-weight: bold; line-height: 22px;}\n");
1314 head.append(
"h2 { padding: 0 0 3px 0; margin: 0px; color: #07A; font-weight: normal; border-bottom: 1px dotted #81B9DB; }\n");
1315 head.append(
"h3 { padding: 5 0 3px 0; margin: 0px; color: #07A; font-weight: normal; }\n");
1316 head.append(
"table td { padding: 5px 25px 5px 0px; vertical-align:top;}\n");
1317 head.append(
"p.subheadding { padding: 0px; margin: 0px; font-size: 11px; color: #B5B5B5; }\n");
1318 head.append(
".title { width: 660px; margin-bottom: 50px; }\n");
1319 head.append(
".left { float: left; width: 250px; margin-top: 20px; text-align: center; }\n");
1320 head.append(
".left img { max-width: 250px; max-height: 250px; min-width: 200px; min-height: 200px; }\n");
1321 head.append(
".right { float: right; width: 385px; margin-top: 25px; font-size: 14px; }\n");
1322 head.append(
".clear { clear: both; }\n");
1323 head.append(
".info { padding: 10px 0;}\n");
1324 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");
1325 head.append(
".info table { margin: 10px 25px 10px 25px; }\n");
1326 head.append(
"ul {padding: 0;margin: 0;list-style-type: none;}");
1327 head.append(
"li {padding-bottom: 5px;}");
1328 head.append(
"</style>\n");
1329 head.append(
"</head>\n<body>\n");
1330 output.write(head.toString());
1332 DateFormat datetimeFormat =
new SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss");
1333 Date date =
new Date();
1334 String datetime = datetimeFormat.format(date);
1336 StringBuilder summary =
new StringBuilder();
1337 boolean running =
false;
1342 List<IngestJobInfo> ingestJobs = skCase.getIngestJobs();
1347 summary.append(
"<div id=\"wrapper\">\n");
1349 summary.append(
"<h1>").append(reportTitle)
1350 .append(running ? NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.warningMsg") :
"")
1352 summary.append(
"<p class=\"subheadding\">").append(
1353 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.reportGenOn.text", datetime)).append(
"</p>\n");
1354 summary.append(
"<div class=\"title\">\n");
1359 if (generatorLogoSet) {
1360 summary.append(
"<div class=\"left\">\n");
1361 summary.append(
"<img src=\"generator_logo.png\" />\n");
1362 summary.append(
"</div>\n");
1364 summary.append(
"<div class=\"clear\"></div>\n");
1365 if (reportFooter != null) {
1366 summary.append(
"<p class=\"subheadding\">").append(reportFooter).append(
"</p>\n");
1368 summary.append(
"</div>\n");
1370 summary.append(
"</body></html>");
1371 output.write(summary.toString());
1372 }
catch (FileNotFoundException ex) {
1373 logger.log(Level.SEVERE,
"Could not find summary.html file to write to.");
1374 }
catch (UnsupportedEncodingException ex) {
1375 logger.log(Level.SEVERE,
"Did not recognize encoding when writing summary.hmtl.");
1376 }
catch (IOException ex) {
1377 logger.log(Level.SEVERE,
"Error creating Writer for summary.html.");
1379 logger.log(Level.WARNING,
"Unable to get current sleuthkit Case for the HTML report.");
1382 if (output != null) {
1386 }
catch (IOException ex) {
1392 "ReportHTML.writeSum.case=Case:",
1393 "ReportHTML.writeSum.caseNumber=Case Number:",
1394 "ReportHTML.writeSum.caseNumImages=Number of data sources in case:",
1395 "ReportHTML.writeSum.caseNotes=Notes:",
1396 "ReportHTML.writeSum.examiner=Examiner:"
1404 StringBuilder summary =
new StringBuilder();
1410 String caseNumber = currentCase.
getNumber();
1414 }
catch (TskCoreException ex) {
1423 summary.append(
"<div class=\"title\">\n");
1424 if (agencyLogoSet) {
1425 summary.append(
"<div class=\"left\">\n");
1426 summary.append(
"<img src=\"");
1427 summary.append(Paths.get(reportBranding.
getAgencyLogoPath()).getFileName().toString());
1428 summary.append(
"\" />\n");
1429 summary.append(
"</div>\n");
1431 final String align = agencyLogoSet ?
"right" :
"left";
1432 summary.append(
"<div class=\"").append(align).append(
"\">\n");
1433 summary.append(
"<table>\n");
1436 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_case()).append(
"</td><td>")
1439 if (!caseNumber.isEmpty()) {
1440 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_caseNumber()).append(
"</td><td>")
1444 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_caseNumImages()).append(
"</td><td>")
1445 .append(imagecount).append(
"</td></tr>\n");
1447 if (!caseNotes.isEmpty()) {
1448 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_caseNotes()).append(
"</td><td>")
1453 if (!examinerName.isEmpty()) {
1454 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_examiner()).append(
"</td><td>")
1459 summary.append(
"</table>\n");
1460 summary.append(
"</div>\n");
1461 summary.append(
"<div class=\"clear\"></div>\n");
1462 summary.append(
"</div>\n");
1472 StringBuilder summary =
new StringBuilder();
1473 summary.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.imageInfoHeading"));
1474 summary.append(
"<div class=\"info\">\n");
1477 summary.append(
"<p>").append(c.getName()).append(
"</p>\n");
1478 if (c instanceof Image) {
1479 Image img = (Image) c;
1481 summary.append(
"<table>\n");
1482 summary.append(
"<tr><td>").append(
1483 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.timezone"))
1484 .append(
"</td><td>").append(img.getTimeZone()).append(
"</td></tr>\n");
1485 for (String imgPath : img.getPaths()) {
1486 summary.append(
"<tr><td>").append(
1487 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.path"))
1488 .append(
"</td><td>").append(imgPath).append(
"</td></tr>\n");
1490 summary.append(
"</table>\n");
1493 }
catch (TskCoreException ex) {
1494 logger.log(Level.WARNING,
"Unable to get image information for the HTML report.");
1496 summary.append(
"</div>\n");
1506 StringBuilder summary =
new StringBuilder();
1507 summary.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.softwareInfoHeading"));
1508 summary.append(
"<div class=\"info\">\n");
1509 summary.append(
"<table>\n");
1510 summary.append(
"<tr><td>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.autopsyVersion"))
1512 Map<Long, IngestModuleInfo> moduleInfoHashMap =
new HashMap<>();
1513 for (IngestJobInfo ingestJob : ingestJobs) {
1514 List<IngestModuleInfo> ingestModules = ingestJob.getIngestModuleInfo();
1515 for (IngestModuleInfo ingestModule : ingestModules) {
1516 if (!moduleInfoHashMap.containsKey(ingestModule.getIngestModuleId())) {
1517 moduleInfoHashMap.put(ingestModule.getIngestModuleId(), ingestModule);
1521 TreeMap<String, String> modules =
new TreeMap<>();
1522 for (IngestModuleInfo moduleinfo : moduleInfoHashMap.values()) {
1523 modules.put(moduleinfo.getDisplayName(), moduleinfo.getVersion());
1525 for (Map.Entry<String, String> module : modules.entrySet()) {
1526 summary.append(
"<tr><td>").append(module.getKey()).append(
" Module:")
1527 .append(
"</td><td>").append(module.getValue()).append(
"</td></tr>\n");
1529 summary.append(
"</table>\n");
1530 summary.append(
"</div>\n");
1531 summary.append(
"<div class=\"clear\"></div>\n");
1541 StringBuilder summary =
new StringBuilder();
1543 summary.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.ingestHistoryHeading"));
1544 summary.append(
"<div class=\"info\">\n");
1547 for (IngestJobInfo ingestJob : ingestJobs) {
1548 summary.append(
"<h3>Job ").append(jobnumber).append(
":</h3>\n");
1549 summary.append(
"<table>\n");
1550 summary.append(
"<tr><td>").append(
"Data Source:")
1551 .append(
"</td><td>").append(skCase.getContentById(ingestJob.getObjectId()).
getName()).append(
"</td></tr>\n");
1552 summary.append(
"<tr><td>").append(
"Status:")
1553 .append(
"</td><td>").append(ingestJob.getStatus()).append(
"</td></tr>\n");
1554 summary.append(
"<tr><td>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.modulesEnabledHeading"))
1555 .append(
"</td><td>");
1556 List<IngestModuleInfo> ingestModules = ingestJob.getIngestModuleInfo();
1557 summary.append(
"<ul>\n");
1558 for (IngestModuleInfo ingestModule : ingestModules) {
1559 summary.append(
"<li>").append(ingestModule.getDisplayName()).append(
"</li>");
1561 summary.append(
"</ul>\n");
1563 summary.append(
"</td></tr>\n");
1564 summary.append(
"</table>\n");
1566 summary.append(
"</div>\n");
1567 }
catch (TskCoreException ex) {
1568 logger.log(Level.WARNING,
"Unable to get ingest jobs for the HTML report.");
1590 File thumbFile = Paths.get(thumbsPath, fileName +
".png").toFile();
1591 if (bufferedThumb == null) {
1595 ImageIO.write(bufferedThumb,
"png", thumbFile);
1596 }
catch (IOException ex) {
1597 logger.log(Level.WARNING,
"Failed to write thumb file to report directory.", ex);
1600 if (thumbFile.exists()
1604 return THUMBS_REL_PATH
1605 + thumbFile.getName();
1617 String formattedString = StringEscapeUtils.escapeHtml4(text);
1618 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)