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(
" ",
"_");
220 OutputStream output = null;
222 logger.log(Level.INFO,
"useDataTypeIcon: dataType = {0}", dataType);
225 BlackboardArtifact.ARTIFACT_TYPE artifactType = null;
226 for (ARTIFACT_TYPE v : ARTIFACT_TYPE.values()) {
227 if (v.getDisplayName().equals(dataType)) {
232 if (null != artifactType) {
235 iconFilePath = subPath + File.separator + iconFileName;
238 switch (artifactType) {
239 case TSK_WEB_BOOKMARK:
240 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/bookmarks.png");
243 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/cookies.png");
245 case TSK_WEB_HISTORY:
246 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/history.png");
248 case TSK_WEB_DOWNLOAD:
249 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/downloads.png");
251 case TSK_RECENT_OBJECT:
252 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/recent.png");
254 case TSK_INSTALLED_PROG:
255 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/installed.png");
257 case TSK_KEYWORD_HIT:
258 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/keywords.png");
260 case TSK_HASHSET_HIT:
261 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/hash.png");
263 case TSK_DEVICE_ATTACHED:
264 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/devices.png");
266 case TSK_WEB_SEARCH_QUERY:
267 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/search.png");
269 case TSK_METADATA_EXIF:
270 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/exif.png");
273 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/userbookmarks.png");
275 case TSK_TAG_ARTIFACT:
276 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/userbookmarks.png");
278 case TSK_SERVICE_ACCOUNT:
279 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/account-icon-16.png");
282 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/contact.png");
285 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/message.png");
288 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/calllog.png");
290 case TSK_CALENDAR_ENTRY:
291 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/calendar.png");
293 case TSK_SPEED_DIAL_ENTRY:
294 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/speeddialentry.png");
296 case TSK_BLUETOOTH_PAIRING:
297 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/bluetooth.png");
299 case TSK_GPS_BOOKMARK:
300 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gpsfav.png");
302 case TSK_GPS_LAST_KNOWN_LOCATION:
303 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gps-lastlocation.png");
306 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gps-search.png");
309 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/computer.png");
311 case TSK_GPS_TRACKPOINT:
312 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gps_trackpoint.png");
315 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gps_trackpoint.png");
318 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/mail-icon-16.png");
320 case TSK_ENCRYPTION_SUSPECTED:
321 case TSK_ENCRYPTION_DETECTED:
322 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/encrypted-file.png");
324 case TSK_EXT_MISMATCH_DETECTED:
325 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/mismatch-16.png");
327 case TSK_INTERESTING_ARTIFACT_HIT:
328 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/interesting_item.png");
330 case TSK_INTERESTING_FILE_HIT:
331 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/interesting_item.png");
334 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/installed.png");
336 case TSK_REMOTE_DRIVE:
337 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/drive_network.png");
340 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/accounts.png");
342 case TSK_WIFI_NETWORK:
343 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/network-wifi.png");
345 case TSK_WIFI_NETWORK_ADAPTER:
346 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/network-wifi.png");
348 case TSK_SIM_ATTACHED:
349 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/sim_card.png");
351 case TSK_BLUETOOTH_ADAPTER:
352 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/Bluetooth.png");
354 case TSK_DEVICE_INFO:
355 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/devices.png");
357 case TSK_VERIFICATION_FAILED:
358 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/validationFailed.png");
361 logger.log(Level.WARNING,
"useDataTypeIcon: unhandled artifact type = {0}", dataType);
362 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/star.png");
363 iconFileName =
"star.png";
364 iconFilePath = subPath + File.separator + iconFileName;
367 }
else if (dataType.startsWith(ARTIFACT_TYPE.TSK_ACCOUNT.getDisplayName())) {
375 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/accounts.png");
376 iconFileName =
"accounts.png";
377 iconFilePath = subPath + File.separator + iconFileName;
379 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/star.png");
380 iconFileName =
"star.png";
381 iconFilePath = subPath + File.separator + iconFileName;
385 output =
new FileOutputStream(iconFilePath);
386 FileUtil.copy(in, output);
389 }
catch (IOException ex) {
390 logger.log(Level.SEVERE,
"Failed to extract images for HTML report.", ex);
392 if (output != null) {
396 }
catch (IOException ex) {
402 }
catch (IOException ex) {
423 logger.log(Level.SEVERE,
"Exception while getting open case.");
427 this.path = baseReportDir;
428 this.subPath = this.path + HTML_SUBDIR + File.separator;
431 FileUtil.createFolder(
new File(this.subPath));
432 FileUtil.createFolder(
new File(this.thumbsPath));
433 }
catch (IOException ex) {
434 logger.log(Level.SEVERE,
"Unable to make HTML report folder.");
452 }
catch (IOException ex) {
453 logger.log(Level.WARNING,
"Could not close the output writer when ending report.", ex);
470 out =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(subPath + title +
".html"),
"UTF-8"));
471 }
catch (FileNotFoundException ex) {
472 logger.log(Level.SEVERE,
"File not found: {0}", ex);
473 }
catch (UnsupportedEncodingException ex) {
474 logger.log(Level.SEVERE,
"Unrecognized encoding");
478 StringBuilder page =
new StringBuilder();
479 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")
481 .append(
"<div id=\"header\">").append(name).append(
"</div>\n")
482 .append(
"<div id=\"content\">\n");
483 if (!description.isEmpty()) {
484 page.append(
"<p><strong>");
485 page.append(description);
486 page.append(
"</strong></p>\n");
488 out.write(page.toString());
489 currentDataType = name;
491 }
catch (IOException ex) {
492 logger.log(Level.SEVERE,
"Failed to write page head: {0}", ex);
502 dataTypes.put(currentDataType, rowCount);
504 StringBuilder builder =
new StringBuilder();
506 builder.append(
"</div>\n</body>\n</html>\n");
507 out.write(builder.toString());
508 }
catch (IOException ex) {
509 logger.log(Level.SEVERE,
"Failed to write end of HTML report.", ex);
515 }
catch (IOException ex) {
516 logger.log(Level.WARNING,
"Could not close the output writer when ending data type.", ex);
530 StringBuilder output =
new StringBuilder();
531 String pageHeader = configPanel.getHeader();
532 if (pageHeader.isEmpty() ==
false) {
533 output.append(
"<div id=\"pageHeaderFooter\">")
534 .append(StringEscapeUtils.escapeHtml4(pageHeader))
537 return output.toString();
547 StringBuilder output =
new StringBuilder();
548 String pageFooter = configPanel.getFooter();
549 if (pageFooter.isEmpty() ==
false) {
550 output.append(
"<br/><div id=\"pageHeaderFooter\">")
551 .append(StringEscapeUtils.escapeHtml4(pageFooter))
554 return output.toString();
564 StringBuilder set =
new StringBuilder();
565 set.append(
"<h1><a name=\"").append(setName).append(
"\">").append(setName).append(
"</a></h1>\n");
566 set.append(
"<div class=\"keyword_list\">\n");
569 out.write(set.toString());
570 }
catch (IOException ex) {
571 logger.log(Level.SEVERE,
"Failed to write set: {0}", ex);
581 out.write(
"</div>\n");
582 }
catch (IOException ex) {
583 logger.log(Level.SEVERE,
"Failed to write end of set: {0}", ex);
594 StringBuilder index =
new StringBuilder();
595 index.append(
"<ul>\n");
596 for (String set : sets) {
597 index.append(
"\t<li><a href=\"#").append(set).append(
"\">").append(set).append(
"</a></li>\n");
599 index.append(
"</ul>\n");
601 out.write(index.toString());
602 }
catch (IOException ex) {
603 logger.log(Level.SEVERE,
"Failed to add set index: {0}", ex);
615 out.write(
"<h4>" + elementName +
"</h4>\n");
616 }
catch (IOException ex) {
617 logger.log(Level.SEVERE,
"Failed to write set element: {0}", ex);
628 StringBuilder ele =
new StringBuilder();
629 ele.append(
"<table>\n<thead>\n\t<tr>\n");
630 for (String title : titles) {
631 ele.append(
"\t\t<th>").append(title).append(
"</th>\n");
633 ele.append(
"\t</tr>\n</thead>\n");
636 out.write(ele.toString());
637 }
catch (IOException ex) {
638 logger.log(Level.SEVERE,
"Failed to write table start: {0}", ex);
649 StringBuilder htmlOutput =
new StringBuilder();
650 htmlOutput.append(
"<table>\n<thead>\n\t<tr>\n");
653 for (String columnHeader : columnHeaders) {
654 htmlOutput.append(
"\t\t<th>").append(columnHeader).append(
"</th>\n");
658 htmlOutput.append(
"\t\t<th></th>\n");
660 htmlOutput.append(
"\t</tr>\n</thead>\n");
663 out.write(htmlOutput.toString());
664 }
catch (IOException ex) {
665 logger.log(Level.SEVERE,
"Failed to write table start: {0}", ex);
675 out.write(
"</table>\n");
676 }
catch (IOException ex) {
677 logger.log(Level.SEVERE,
"Failed to write end of table: {0}", ex);
699 private void addRow(List<String> row,
boolean escapeText) {
700 StringBuilder builder =
new StringBuilder();
701 builder.append(
"\t<tr>\n");
702 for (String cell : row) {
704 builder.append(
"\t\t<td>").append(cellText).append(
"</td>\n");
706 builder.append(
"\t</tr>\n");
710 out.write(builder.toString());
711 }
catch (IOException ex) {
712 logger.log(Level.SEVERE,
"Failed to write row to out.", ex);
713 }
catch (NullPointerException ex) {
714 logger.log(Level.SEVERE,
"Output writer is null. Page was not initialized before writing.", ex);
726 Content content = contentTag.getContent();
727 if (content instanceof AbstractFile ==
false) {
731 AbstractFile file = (AbstractFile) content;
733 StringBuilder localFileLink =
new StringBuilder();
736 || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
737 || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)) {
738 localFileLink.append(
"<a href=\"");
740 String localFilePath =
saveContent(file, contentTag.getName().getDisplayName());
741 localFileLink.append(localFilePath);
742 localFileLink.append(
"\" target=\"_top\">");
745 StringBuilder builder =
new StringBuilder();
746 builder.append(
"\t<tr>\n");
747 int positionCounter = 0;
748 for (String cell : row) {
750 switch (positionCounter) {
753 builder.append(
"\t\t<td class=\"left_align_cell\">").append(localFileLink.toString()).append(cell).append(
"</a></td>\n");
757 builder.append(
"\t\t<td class=\"right_align_cell\">").append(cell).append(
"</td>\n");
761 builder.append(
"\t\t<td>").append(cell).append(
"</td>\n");
766 builder.append(
"\t</tr>\n");
770 out.write(builder.toString());
771 }
catch (IOException ex) {
772 logger.log(Level.SEVERE,
"Failed to write row to out.", ex);
773 }
catch (NullPointerException ex) {
774 logger.log(Level.SEVERE,
"Output writer is null. Page was not initialized before writing.", ex);
785 ArrayList<ImageTagRegion> tagRegions =
new ArrayList<>();
786 contentTags.forEach((contentTag) -> {
790 if (contentViewerTag != null) {
791 tagRegions.add(contentViewerTag.
getDetails());
794 logger.log(Level.WARNING,
"Could not get content viewer tag "
795 +
"from case db for content_tag with id %d", contentTag.getId());
807 List<String> currentRow =
new ArrayList<>();
810 for (Content content : images) {
812 addRow(currentRow,
false);
816 if (totalCount == MAX_THUMBS_PER_PAGE) {
820 rowCount = totalCount;
825 startDataType(NbBundle.getMessage(
this.getClass(),
"ReportHTML.addThumbRows.dataType.title", pages),
826 NbBundle.getMessage(
this.getClass(),
"ReportHTML.addThumbRows.dataType.msg"));
827 List<String> emptyHeaders =
new ArrayList<>();
829 emptyHeaders.add(
"");
838 AbstractFile file = (AbstractFile) content;
839 List<ContentTag> contentTags =
new ArrayList<>();
841 String thumbnailPath = null;
842 String imageWithTagsFullPath = null;
849 if (!imageTags.isEmpty()) {
859 File thumbnailImageWithTagsFile = Paths.get(thumbsPath, FilenameUtils.removeExtension(fileName) +
".png").toFile();
861 fullImageWithTagsPath = FilenameUtils.removeExtension(fullImageWithTagsPath) +
".png";
862 File fullImageWithTagsFile = Paths.get(fullImageWithTagsPath).toFile();
865 ImageIO.write(thumbnailWithTags,
"png", thumbnailImageWithTagsFile);
866 ImageIO.write(fullImageWithTags,
"png", fullImageWithTagsFile);
868 thumbnailPath = THUMBS_REL_PATH + thumbnailImageWithTagsFile.getName();
870 imageWithTagsFullPath = fullImageWithTagsPath.substring(subPath.length());
872 }
catch (TskCoreException ex) {
873 logger.log(Level.WARNING,
"Could not get tags for file.", ex);
874 }
catch (IOException | InterruptedException | ExecutionException ex) {
875 logger.log(Level.WARNING,
"Could make marked up thumbnail.", ex);
879 if (thumbnailPath == null) {
883 if (thumbnailPath == null) {
886 String contentPath =
saveContent(file,
"original");
889 nameInImage = file.getUniquePath();
890 }
catch (TskCoreException ex) {
891 nameInImage = file.getName();
894 StringBuilder linkToThumbnail =
new StringBuilder();
895 linkToThumbnail.append(
"<div id='thumbnail_link'><a href=\"")
896 .append((imageWithTagsFullPath != null) ? imageWithTagsFullPath : contentPath)
897 .append(
"\" target=\"_top\"><img src=\"")
898 .append(thumbnailPath).append(
"\" title=\"").append(nameInImage).append(
"\"/></a><br>")
899 .append(file.getName()).append(
"<br>");
900 if (imageWithTagsFullPath != null) {
901 linkToThumbnail.append(
"<a href=\"").append(contentPath).append(
"\" target=\"_top\">View Original</a><br>");
904 if (!contentTags.isEmpty()) {
905 linkToThumbnail.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.thumbLink.tags"));
907 for (
int i = 0; i < contentTags.size(); i++) {
908 ContentTag tag = contentTags.get(i);
910 linkToThumbnail.append(tag.getName().getDisplayName()).append(notableString);
911 if (i != contentTags.size() - 1) {
912 linkToThumbnail.append(
", ");
916 linkToThumbnail.append(
"</div>");
917 currentRow.add(linkToThumbnail.toString());
922 if (currentRow.isEmpty() ==
false) {
923 int extraCells = THUMBNAIL_COLUMNS - currentRow.size();
924 for (
int i = 0; i < extraCells; i++) {
928 addRow(currentRow,
false);
932 rowCount = totalCount;
936 if (c instanceof AbstractFile ==
false) {
939 AbstractFile file = (AbstractFile) c;
941 || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
942 || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS;
950 StringBuilder localFilePath =
new StringBuilder();
952 localFilePath.append(subPath);
953 localFilePath.append(dirName2);
954 File localFileFolder =
new File(localFilePath.toString());
955 if (!localFileFolder.exists()) {
956 localFileFolder.mkdirs();
967 String objectIdSuffix =
"_" + file.getId();
968 int lastDotIndex = fileName.lastIndexOf(
".");
969 if (lastDotIndex != -1 && lastDotIndex != 0) {
971 fileName = fileName.substring(0, lastDotIndex) + objectIdSuffix + fileName.substring(lastDotIndex, fileName.length());
975 fileName += objectIdSuffix;
977 localFilePath.append(File.separator);
978 localFilePath.append(fileName);
980 return localFilePath.toString();
998 File localFile =
new File(localFilePath);
999 if (!localFile.exists()) {
1004 return localFilePath.substring(subPath.length());
1016 SimpleDateFormat sdf =
new java.text.SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss");
1017 return sdf.format(
new java.util.Date(date * 1000));
1022 return "report.html";
1027 return NbBundle.getMessage(this.getClass(),
"ReportHTML.getName.text");
1032 return NbBundle.getMessage(this.getClass(),
"ReportHTML.getDesc.text");
1039 Writer cssOut = null;
1041 cssOut =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(subPath +
"index.css"),
"UTF-8"));
1042 String css =
"body {margin: 0px; padding: 0px; background: #FFFFFF; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353;}\n"
1044 "#content {padding: 30px;}\n"
1046 "#header {width:100%; padding: 10px; line-height: 25px; background: #07A; color: #FFF; font-size: 20px;}\n"
1048 "#pageHeaderFooter {width: 100%; padding: 10px; line-height: 25px; text-align: center; font-size: 20px;}\n"
1050 "h1 {font-size: 20px; font-weight: normal; color: #07A; padding: 0 0 7px 0; margin-top: 25px; border-bottom: 1px solid #D6D6D6;}\n"
1052 "h2 {font-size: 20px; font-weight: bolder; color: #07A;}\n"
1054 "h3 {font-size: 16px; color: #07A;}\n"
1056 "h4 {background: #07A; color: #FFF; font-size: 16px; margin: 0 0 0 25px; padding: 0; padding-left: 15px;}\n"
1058 "ul.nav {list-style-type: none; line-height: 35px; padding: 0px; margin-left: 15px;}\n"
1060 "ul li a {font-size: 14px; color: #444; text-decoration: none; padding-left: 25px;}\n"
1062 "ul li a:hover {text-decoration: underline;}\n"
1064 "p {margin: 0 0 20px 0;}\n"
1066 "table {white-space:nowrap; min-width: 700px; padding: 2; margin: 0; border-collapse: collapse; border-bottom: 2px solid #e5e5e5;}\n"
1068 ".keyword_list table {margin: 0 0 25px 25px; border-bottom: 2px solid #dedede;}\n"
1070 "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"
1072 "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"
1074 "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"
1076 "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"
1078 "table tr:nth-child(even) td {background: #f3f3f3;}\n"
1080 "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;}";
1082 }
catch (FileNotFoundException ex) {
1083 logger.log(Level.SEVERE,
"Could not find index.css file to write to.", ex);
1084 }
catch (UnsupportedEncodingException ex) {
1085 logger.log(Level.SEVERE,
"Did not recognize encoding when writing index.css.", ex);
1086 }
catch (IOException ex) {
1087 logger.log(Level.SEVERE,
"Error creating Writer for index.css.", ex);
1090 if (cssOut != null) {
1094 }
catch (IOException ex) {
1103 Writer indexOut = null;
1104 String indexFilePath = path +
"report.html";
1109 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
1113 indexOut =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(indexFilePath),
"UTF-8"));
1114 StringBuilder index =
new StringBuilder();
1117 if (iconPath == null) {
1119 iconPath = HTML_SUBDIR +
"favicon.ico";
1121 iconPath = Paths.get(reportBranding.
getAgencyLogoPath()).getFileName().toString();
1123 index.append(
"<head>\n<title>").append(reportTitle).append(
" ").append(
1124 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.title", currentCase.
getDisplayName())).append(
1126 index.append(
"<link rel=\"icon\" type=\"image/ico\" href=\"")
1127 .append(iconPath).append(
"\" />\n");
1128 index.append(
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n");
1129 index.append(
"</head>\n");
1130 index.append(
"<frameset cols=\"350px,*\">\n");
1131 index.append(
"<frame src=\"" + HTML_SUBDIR).append(File.separator).append(
"nav.html\" name=\"nav\">\n");
1132 index.append(
"<frame src=\"" + HTML_SUBDIR).append(File.separator).append(
"summary.html\" name=\"content\">\n");
1133 index.append(
"<noframes>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.noFrames.msg")).append(
"<br />\n");
1134 index.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.noFrames.seeNav")).append(
"<br />\n");
1135 index.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.seeSum")).append(
"</noframes>\n");
1136 index.append(
"</frameset>\n");
1137 index.append(
"</html>");
1138 indexOut.write(index.toString());
1139 openCase.
addReport(indexFilePath, NbBundle.getMessage(
this.getClass(),
1140 "ReportHTML.writeIndex.srcModuleName.text"),
"");
1141 }
catch (IOException ex) {
1142 logger.log(Level.SEVERE,
"Error creating Writer for report.html: {0}", ex);
1143 }
catch (TskCoreException ex) {
1144 String errorMessage = String.format(
"Error adding %s to case as a report", indexFilePath);
1145 logger.log(Level.SEVERE, errorMessage, ex);
1148 if (indexOut != null) {
1152 }
catch (IOException ex) {
1161 Writer navOut = null;
1163 navOut =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(subPath +
"nav.html"),
"UTF-8"));
1164 StringBuilder nav =
new StringBuilder();
1165 nav.append(
"<html>\n<head>\n\t<title>").append(
1166 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeNav.title"))
1167 .append(
"</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n");
1168 nav.append(
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n</head>\n<body>\n");
1169 nav.append(
"<div id=\"content\">\n<h1>").append(
1170 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeNav.h1")).append(
"</h1>\n");
1171 nav.append(
"<ul class=\"nav\">\n");
1172 nav.append(
"<li style=\"background: url(summary.png) left center no-repeat;\"><a href=\"summary.html\" target=\"content\">")
1173 .append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeNav.summary")).append(
"</a></li>\n");
1175 for (String dataType : dataTypes.keySet()) {
1178 nav.append(
"<li style=\"background: url('").append(iconFileName)
1179 .append(
"') left center no-repeat;\"><a href=\"")
1180 .append(dataTypeEsc).append(
".html\" target=\"content\">")
1181 .append(dataType).append(
" (").append(dataTypes.get(dataType))
1182 .append(
")</a></li>\n");
1184 nav.append(
"</ul>\n");
1185 nav.append(
"</div>\n</body>\n</html>");
1186 navOut.write(nav.toString());
1187 }
catch (IOException ex) {
1188 logger.log(Level.SEVERE,
"Failed to write end of report navigation menu: {0}", ex);
1190 if (navOut != null) {
1194 }
catch (IOException ex) {
1195 logger.log(Level.WARNING,
"Could not close navigation out writer.");
1200 InputStream in = null;
1201 OutputStream output = null;
1206 if (generatorLogoPath != null && !generatorLogoPath.isEmpty()) {
1207 File from =
new File(generatorLogoPath);
1208 File to =
new File(subPath);
1209 FileUtil.copyFile(FileUtil.toFileObject(from), FileUtil.toFileObject(to),
"generator_logo");
1213 if (agencyLogoPath != null && !agencyLogoPath.isEmpty()) {
1214 Path destinationPath = Paths.get(subPath);
1215 Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), destinationPath.resolve(Paths.get(agencyLogoPath).getFileName()));
1218 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/favicon.ico");
1219 output =
new FileOutputStream(
new File(subPath +
"favicon.ico"));
1220 FileUtil.copy(in, output);
1224 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/summary.png");
1225 output =
new FileOutputStream(
new File(subPath +
"summary.png"));
1226 FileUtil.copy(in, output);
1230 }
catch (IOException ex) {
1231 logger.log(Level.SEVERE,
"Failed to extract images for HTML report.", ex);
1233 if (output != null) {
1237 }
catch (IOException ex) {
1243 }
catch (IOException ex) {
1253 Writer output = null;
1255 output =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(subPath +
"summary.html"),
"UTF-8"));
1256 StringBuilder head =
new StringBuilder();
1257 head.append(
"<html>\n<head>\n<title>").append(
1258 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.title")).append(
"</title>\n");
1259 head.append(
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n");
1260 head.append(
"<style type=\"text/css\">\n");
1261 head.append(
"#pageHeaderFooter {width: 100%; padding: 10px; line-height: 25px; text-align: center; font-size: 20px;}\n");
1262 head.append(
"body { padding: 0px; margin: 0px; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353; }\n");
1263 head.append(
"#wrapper { width: 90%; margin: 0px auto; margin-top: 35px; }\n");
1264 head.append(
"h1 { color: #07A; font-size: 36px; line-height: 42px; font-weight: normal; margin: 0px; border-bottom: 1px solid #81B9DB; }\n");
1265 head.append(
"h1 span { color: #F00; display: block; font-size: 16px; font-weight: bold; line-height: 22px;}\n");
1266 head.append(
"h2 { padding: 0 0 3px 0; margin: 0px; color: #07A; font-weight: normal; border-bottom: 1px dotted #81B9DB; }\n");
1267 head.append(
"h3 { padding: 5 0 3px 0; margin: 0px; color: #07A; font-weight: normal; }\n");
1268 head.append(
"table td { padding: 5px 25px 5px 0px; vertical-align:top;}\n");
1269 head.append(
"p.subheadding { padding: 0px; margin: 0px; font-size: 11px; color: #B5B5B5; }\n");
1270 head.append(
".title { width: 660px; margin-bottom: 50px; }\n");
1271 head.append(
".left { float: left; width: 250px; margin-top: 20px; text-align: center; }\n");
1272 head.append(
".left img { max-width: 250px; max-height: 250px; min-width: 200px; min-height: 200px; }\n");
1273 head.append(
".right { float: right; width: 385px; margin-top: 25px; font-size: 14px; }\n");
1274 head.append(
".clear { clear: both; }\n");
1275 head.append(
".info { padding: 10px 0;}\n");
1276 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");
1277 head.append(
".info table { margin: 10px 25px 10px 25px; }\n");
1278 head.append(
"ul {padding: 0;margin: 0;list-style-type: none;}");
1279 head.append(
"li {padding-bottom: 5px;}");
1280 head.append(
"</style>\n");
1281 head.append(
"</head>\n<body>\n");
1282 output.write(head.toString());
1284 DateFormat datetimeFormat =
new SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss");
1285 Date date =
new Date();
1286 String datetime = datetimeFormat.format(date);
1288 StringBuilder summary =
new StringBuilder();
1289 boolean running =
false;
1294 List<IngestJobInfo> ingestJobs = skCase.getIngestJobs();
1299 summary.append(
"<div id=\"wrapper\">\n");
1301 summary.append(
"<h1>").append(reportTitle)
1302 .append(running ? NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.warningMsg") :
"")
1304 summary.append(
"<p class=\"subheadding\">").append(
1305 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.reportGenOn.text", datetime)).append(
"</p>\n");
1306 summary.append(
"<div class=\"title\">\n");
1311 if (generatorLogoSet) {
1312 summary.append(
"<div class=\"left\">\n");
1313 summary.append(
"<img src=\"generator_logo.png\" />\n");
1314 summary.append(
"</div>\n");
1316 summary.append(
"<div class=\"clear\"></div>\n");
1317 if (reportFooter != null) {
1318 summary.append(
"<p class=\"subheadding\">").append(reportFooter).append(
"</p>\n");
1320 summary.append(
"</div>\n");
1322 summary.append(
"</body></html>");
1323 output.write(summary.toString());
1324 }
catch (FileNotFoundException ex) {
1325 logger.log(Level.SEVERE,
"Could not find summary.html file to write to.");
1326 }
catch (UnsupportedEncodingException ex) {
1327 logger.log(Level.SEVERE,
"Did not recognize encoding when writing summary.hmtl.");
1328 }
catch (IOException ex) {
1329 logger.log(Level.SEVERE,
"Error creating Writer for summary.html.");
1331 logger.log(Level.WARNING,
"Unable to get current sleuthkit Case for the HTML report.");
1334 if (output != null) {
1338 }
catch (IOException ex) {
1344 "ReportHTML.writeSum.case=Case:",
1345 "ReportHTML.writeSum.caseNumber=Case Number:",
1346 "ReportHTML.writeSum.caseNumImages=Number of Images:",
1347 "ReportHTML.writeSum.caseNotes=Notes:",
1348 "ReportHTML.writeSum.examiner=Examiner:"
1356 StringBuilder summary =
new StringBuilder();
1362 String caseNumber = currentCase.
getNumber();
1366 }
catch (TskCoreException ex) {
1375 summary.append(
"<div class=\"title\">\n");
1376 if (agencyLogoSet) {
1377 summary.append(
"<div class=\"left\">\n");
1378 summary.append(
"<img src=\"");
1379 summary.append(Paths.get(reportBranding.
getAgencyLogoPath()).getFileName().toString());
1380 summary.append(
"\" />\n");
1381 summary.append(
"</div>\n");
1383 final String align = agencyLogoSet ?
"right" :
"left";
1384 summary.append(
"<div class=\"").append(align).append(
"\">\n");
1385 summary.append(
"<table>\n");
1388 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_case()).append(
"</td><td>")
1391 if (!caseNumber.isEmpty()) {
1392 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_caseNumber()).append(
"</td><td>")
1396 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_caseNumImages()).append(
"</td><td>")
1397 .append(imagecount).append(
"</td></tr>\n");
1399 if (!caseNotes.isEmpty()) {
1400 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_caseNotes()).append(
"</td><td>")
1405 if (!examinerName.isEmpty()) {
1406 summary.append(
"<tr><td>").append(Bundle.ReportHTML_writeSum_examiner()).append(
"</td><td>")
1411 summary.append(
"</table>\n");
1412 summary.append(
"</div>\n");
1413 summary.append(
"<div class=\"clear\"></div>\n");
1414 summary.append(
"</div>\n");
1424 StringBuilder summary =
new StringBuilder();
1425 summary.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.imageInfoHeading"));
1426 summary.append(
"<div class=\"info\">\n");
1429 summary.append(
"<p>").append(c.getName()).append(
"</p>\n");
1430 if (c instanceof Image) {
1431 Image img = (Image) c;
1433 summary.append(
"<table>\n");
1434 summary.append(
"<tr><td>").append(
1435 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.timezone"))
1436 .append(
"</td><td>").append(img.getTimeZone()).append(
"</td></tr>\n");
1437 for (String imgPath : img.getPaths()) {
1438 summary.append(
"<tr><td>").append(
1439 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.path"))
1440 .append(
"</td><td>").append(imgPath).append(
"</td></tr>\n");
1442 summary.append(
"</table>\n");
1445 }
catch (TskCoreException ex) {
1446 logger.log(Level.WARNING,
"Unable to get image information for the HTML report.");
1448 summary.append(
"</div>\n");
1458 StringBuilder summary =
new StringBuilder();
1459 summary.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.softwareInfoHeading"));
1460 summary.append(
"<div class=\"info\">\n");
1461 summary.append(
"<table>\n");
1462 summary.append(
"<tr><td>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.autopsyVersion"))
1464 Map<Long, IngestModuleInfo> moduleInfoHashMap =
new HashMap<>();
1465 for (IngestJobInfo ingestJob : ingestJobs) {
1466 List<IngestModuleInfo> ingestModules = ingestJob.getIngestModuleInfo();
1467 for (IngestModuleInfo ingestModule : ingestModules) {
1468 if (!moduleInfoHashMap.containsKey(ingestModule.getIngestModuleId())) {
1469 moduleInfoHashMap.put(ingestModule.getIngestModuleId(), ingestModule);
1473 TreeMap<String, String> modules =
new TreeMap<>();
1474 for (IngestModuleInfo moduleinfo : moduleInfoHashMap.values()) {
1475 modules.put(moduleinfo.getDisplayName(), moduleinfo.getVersion());
1477 for (Map.Entry<String, String> module : modules.entrySet()) {
1478 summary.append(
"<tr><td>").append(module.getKey()).append(
" Module:")
1479 .append(
"</td><td>").append(module.getValue()).append(
"</td></tr>\n");
1481 summary.append(
"</table>\n");
1482 summary.append(
"</div>\n");
1483 summary.append(
"<div class=\"clear\"></div>\n");
1493 StringBuilder summary =
new StringBuilder();
1495 summary.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.ingestHistoryHeading"));
1496 summary.append(
"<div class=\"info\">\n");
1499 for (IngestJobInfo ingestJob : ingestJobs) {
1500 summary.append(
"<h3>Job ").append(jobnumber).append(
":</h3>\n");
1501 summary.append(
"<table>\n");
1502 summary.append(
"<tr><td>").append(
"Data Source:")
1503 .append(
"</td><td>").append(skCase.getContentById(ingestJob.getObjectId()).
getName()).append(
"</td></tr>\n");
1504 summary.append(
"<tr><td>").append(
"Status:")
1505 .append(
"</td><td>").append(ingestJob.getStatus()).append(
"</td></tr>\n");
1506 summary.append(
"<tr><td>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.modulesEnabledHeading"))
1507 .append(
"</td><td>");
1508 List<IngestModuleInfo> ingestModules = ingestJob.getIngestModuleInfo();
1509 summary.append(
"<ul>\n");
1510 for (IngestModuleInfo ingestModule : ingestModules) {
1511 summary.append(
"<li>").append(ingestModule.getDisplayName()).append(
"</li>");
1513 summary.append(
"</ul>\n");
1515 summary.append(
"</td></tr>\n");
1516 summary.append(
"</table>\n");
1518 summary.append(
"</div>\n");
1519 }
catch (TskCoreException ex) {
1520 logger.log(Level.WARNING,
"Unable to get ingest jobs for the HTML report.");
1542 File thumbFile = Paths.get(thumbsPath, fileName +
".png").toFile();
1543 if (bufferedThumb == null) {
1547 ImageIO.write(bufferedThumb,
"png", thumbFile);
1548 }
catch (IOException ex) {
1549 logger.log(Level.WARNING,
"Failed to write thumb file to report directory.", ex);
1552 if (thumbFile.exists()
1556 return THUMBS_REL_PATH
1557 + thumbFile.getName();
1569 String formattedString = StringEscapeUtils.escapeHtml4(text);
1570 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)