23 package org.sleuthkit.autopsy.report;
25 import java.io.BufferedWriter;
27 import java.io.FileNotFoundException;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.OutputStream;
32 import java.io.OutputStreamWriter;
33 import java.io.UnsupportedEncodingException;
34 import java.io.Writer;
35 import java.text.DateFormat;
36 import java.text.SimpleDateFormat;
37 import java.util.ArrayList;
38 import java.util.Date;
39 import java.util.List;
41 import java.util.TreeMap;
42 import java.util.logging.Level;
43 import org.openide.filesystems.FileObject;
44 import org.openide.util.Exceptions;
45 import org.openide.util.NbBundle;
47 import org.openide.filesystems.FileUtil;
64 class ReportHTML
implements TableReportModule {
65 private static final Logger logger = Logger.getLogger(ReportHTML.class.getName());
66 private static final String THUMBS_REL_PATH =
"thumbs" + File.separator;
67 private static ReportHTML instance;
68 private static final int MAX_THUMBS_PER_PAGE = 1000;
69 private Case currentCase;
70 private SleuthkitCase skCase;
71 static Integer THUMBNAIL_COLUMNS = 5;
73 private Map<String, Integer> dataTypes;
75 private String thumbsPath;
76 private String currentDataType;
77 private Integer rowCount;
81 private final ReportBranding reportBranding;
84 public static synchronized ReportHTML getDefault() {
85 if (instance == null) {
86 instance =
new ReportHTML();
92 private ReportHTML() {
93 reportBranding =
new ReportBranding();
97 private void refresh() {
98 currentCase = Case.getCurrentCase();
99 skCase = currentCase.getSleuthkitCase();
101 dataTypes =
new TreeMap<>();
105 currentDataType =
"";
111 }
catch (IOException ex) {
122 private String dataTypeToFileName(String dataType) {
126 fileName = fileName.replaceAll(
" ",
"_");
136 private String useDataTypeIcon(String dataType)
141 OutputStream output = null;
143 logger.log(Level.INFO,
"useDataTypeIcon: dataType = {0}", dataType);
146 BlackboardArtifact.ARTIFACT_TYPE artifactType = null;
147 for (ARTIFACT_TYPE v : ARTIFACT_TYPE.values()) {
148 if (v.getDisplayName().equals(dataType)) {
153 if (null != artifactType)
156 iconFileName = dataTypeToFileName(artifactType.getDisplayName()) +
".png";
157 iconFilePath = path + File.separator + iconFileName;
160 switch (artifactType) {
161 case TSK_WEB_BOOKMARK:
162 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/bookmarks.png");
165 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/cookies.png");
167 case TSK_WEB_HISTORY:
168 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/history.png");
170 case TSK_WEB_DOWNLOAD:
171 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/downloads.png");
173 case TSK_RECENT_OBJECT:
174 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/recent.png");
176 case TSK_INSTALLED_PROG:
177 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/installed.png");
179 case TSK_KEYWORD_HIT:
180 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/keywords.png");
182 case TSK_HASHSET_HIT:
183 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/hash.png");
185 case TSK_DEVICE_ATTACHED:
186 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/devices.png");
188 case TSK_WEB_SEARCH_QUERY:
189 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/search.png");
191 case TSK_METADATA_EXIF:
192 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/exif.png");
195 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/userbookmarks.png");
197 case TSK_TAG_ARTIFACT:
198 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/userbookmarks.png");
200 case TSK_SERVICE_ACCOUNT:
201 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/account-icon-16.png");
204 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/contact.png");
207 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/message.png");
210 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/calllog.png");
212 case TSK_CALENDAR_ENTRY:
213 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/calendar.png");
215 case TSK_SPEED_DIAL_ENTRY:
216 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/speeddialentry.png");
218 case TSK_BLUETOOTH_PAIRING:
219 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/bluetooth.png");
221 case TSK_GPS_BOOKMARK:
222 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gpsfav.png");
224 case TSK_GPS_LAST_KNOWN_LOCATION:
225 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gps-lastlocation.png");
228 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gps-search.png");
231 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/computer.png");
233 case TSK_GPS_TRACKPOINT:
234 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gps_trackpoint.png");
237 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/gps_trackpoint.png");
240 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/mail-icon-16.png");
242 case TSK_ENCRYPTION_DETECTED:
243 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/encrypted-file.png");
245 case TSK_EXT_MISMATCH_DETECTED:
246 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/mismatch-16.png");
248 case TSK_INTERESTING_ARTIFACT_HIT:
249 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/interesting_item.png");
251 case TSK_INTERESTING_FILE_HIT:
252 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/images/interesting_item.png");
255 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/installed.png");
257 case TSK_REMOTE_DRIVE:
258 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/drive_network.png");
261 logger.log(Level.WARNING,
"useDataTypeIcon: unhandled artifact type = " + dataType);
262 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/star.png");
263 iconFileName =
"star.png";
264 iconFilePath = path + File.separator + iconFileName;
269 logger.log(Level.WARNING,
"useDataTypeIcon: no artifact found for data type = " + dataType);
270 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/star.png");
271 iconFileName =
"star.png";
272 iconFilePath = path + File.separator + iconFileName;
276 output =
new FileOutputStream(iconFilePath);
277 FileUtil.copy(in, output);
280 }
catch (IOException ex) {
281 logger.log(Level.SEVERE,
"Failed to extract images for HTML report.", ex);
283 if (output != null) {
287 }
catch (IOException ex) {
292 }
catch (IOException ex) {
305 public void startReport(String baseReportDir) {
309 this.path = baseReportDir +
"HTML Report" + File.separator;
310 this.thumbsPath = this.path +
"thumbs" + File.separator;
312 FileUtil.createFolder(
new File(this.path));
313 FileUtil.createFolder(
new File(this.thumbsPath));
314 }
catch (IOException ex) {
315 logger.log(Level.SEVERE,
"Unable to make HTML report folder.");
328 public void endReport() {
333 }
catch (IOException ex) {
334 logger.log(Level.WARNING,
"Could not close the output writer when ending report.", ex);
348 public void startDataType(String name, String description) {
349 String title = dataTypeToFileName(name);
351 out =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(path + title +
".html"),
"UTF-8"));
352 }
catch (FileNotFoundException ex) {
353 logger.log(Level.SEVERE,
"File not found: {0}", ex);
354 }
catch (UnsupportedEncodingException ex) {
355 logger.log(Level.SEVERE,
"Unrecognized encoding");
359 StringBuilder page =
new StringBuilder();
360 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");
361 page.append(
"<div id=\"header\">").append(name).append(
"</div>\n<div id=\"content\">\n");
362 if (!description.isEmpty()) {
363 page.append(
"<p><strong>");
364 page.append(description);
365 page.append(
"</string></p>\n");
367 out.write(page.toString());
368 currentDataType = name;
370 }
catch (IOException ex) {
371 logger.log(Level.SEVERE,
"Failed to write page head: {0}", ex);
380 public void endDataType() {
381 dataTypes.put(currentDataType, rowCount);
383 out.write(
"</div>\n</body>\n</html>\n");
384 }
catch (IOException ex) {
385 Exceptions.printStackTrace(ex);
391 }
catch (IOException ex) {
392 logger.log(Level.WARNING,
"Could not close the output writer when ending data type.", ex);
404 public void startSet(String setName) {
405 StringBuilder set =
new StringBuilder();
406 set.append(
"<h1><a name=\"").append(setName).append(
"\">").append(setName).append(
"</a></h1>\n");
407 set.append(
"<div class=\"keyword_list\">\n");
410 out.write(set.toString());
411 }
catch (IOException ex) {
412 logger.log(Level.SEVERE,
"Failed to write set: {0}", ex);
420 public void endSet() {
422 out.write(
"</div>\n");
423 }
catch (IOException ex) {
424 logger.log(Level.SEVERE,
"Failed to write end of set: {0}", ex);
433 public void addSetIndex(List<String> sets) {
434 StringBuilder index =
new StringBuilder();
435 index.append(
"<ul>\n");
436 for (String set : sets) {
437 index.append(
"\t<li><a href=\"#").append(set).append(
"\">").append(set).append(
"</a></li>\n");
439 index.append(
"</ul>\n");
441 out.write(index.toString());
442 }
catch (IOException ex) {
443 logger.log(Level.SEVERE,
"Failed to add set index: {0}", ex);
452 public void addSetElement(String elementName) {
454 out.write(
"<h4>" + elementName +
"</h4>\n");
455 }
catch (IOException ex) {
456 logger.log(Level.SEVERE,
"Failed to write set element: {0}", ex);
465 public void startTable(List<String> titles) {
466 StringBuilder ele =
new StringBuilder();
467 ele.append(
"<table>\n<thead>\n\t<tr>\n");
468 for(String title : titles) {
469 ele.append(
"\t\t<th>").append(title).append(
"</th>\n");
471 ele.append(
"\t</tr>\n</thead>\n");
474 out.write(ele.toString());
475 }
catch (IOException ex) {
476 logger.log(Level.SEVERE,
"Failed to write table start: {0}", ex);
487 public void startContentTagsTable(List<String> columnHeaders) {
488 StringBuilder htmlOutput =
new StringBuilder();
489 htmlOutput.append(
"<table>\n<thead>\n\t<tr>\n");
492 for(String columnHeader : columnHeaders) {
493 htmlOutput.append(
"\t\t<th>").append(columnHeader).append(
"</th>\n");
497 htmlOutput.append(
"\t\t<th></th>\n");
499 htmlOutput.append(
"\t</tr>\n</thead>\n");
502 out.write(htmlOutput.toString());
503 }
catch (IOException ex) {
504 logger.log(Level.SEVERE,
"Failed to write table start: {0}", ex);
512 public void endTable() {
514 out.write(
"</table>\n");
515 }
catch (IOException ex) {
516 logger.log(Level.SEVERE,
"Failed to write end of table: {0}", ex);
525 public void addRow(List<String> row) {
526 StringBuilder builder =
new StringBuilder();
527 builder.append(
"\t<tr>\n");
528 for (String cell : row) {
529 builder.append(
"\t\t<td>").append(cell).append(
"</td>\n");
531 builder.append(
"\t</tr>\n");
535 out.write(builder.toString());
536 }
catch (IOException ex) {
537 logger.log(Level.SEVERE,
"Failed to write row to out.", ex);
538 }
catch (NullPointerException ex) {
539 logger.log(Level.SEVERE,
"Output writer is null. Page was not initialized before writing.", ex);
552 public void addRowWithTaggedContentHyperlink(List<String> row, ContentTag contentTag) {
553 Content content = contentTag.getContent();
554 if (content instanceof AbstractFile ==
false) {
558 AbstractFile file = (AbstractFile) content;
561 file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS ||
562 file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) {
568 row.add(file.getMtimeAsDate());
569 row.add(file.getCtimeAsDate());
570 row.add(file.getAtimeAsDate());
571 row.add(file.getCrtimeAsDate());
572 row.add(Long.toString(file.getSize()));
573 row.add(file.getMd5Hash());
576 String localFilePath = saveContent(file, contentTag.getName().getDisplayName());
579 StringBuilder localFileLink =
new StringBuilder();
580 localFileLink.append(
"<a href=\"");
581 localFileLink.append(localFilePath);
582 localFileLink.append(
"\">");
584 StringBuilder builder =
new StringBuilder();
585 builder.append(
"\t<tr>\n");
586 int positionCounter=0;
587 for (String cell : row) {
589 if(positionCounter==1) {
590 builder.append(
"\t\t<td class=\"left_align_cell\">").append(localFileLink.toString()).append(cell).append(
"</a></td>\n");
592 else if (positionCounter==7) {
593 builder.append(
"\t\t<td class=\"right_align_cell\">").append(cell).append(
"</td>\n");
596 builder.append(
"\t\t<td>").append(cell).append(
"</td>\n");
600 builder.append(
"\t</tr>\n");
604 out.write(builder.toString());
606 catch (IOException ex) {
607 logger.log(Level.SEVERE,
"Failed to write row to out.", ex);
609 catch (NullPointerException ex) {
610 logger.log(Level.SEVERE,
"Output writer is null. Page was not initialized before writing.", ex);
618 public void addThumbnailRows(List<Content> images) {
619 List<String> currentRow =
new ArrayList<>();
622 for (Content content : images) {
623 if (currentRow.size() == THUMBNAIL_COLUMNS) {
628 if (totalCount == MAX_THUMBS_PER_PAGE) {
632 rowCount = totalCount;
637 startDataType(NbBundle.getMessage(
this.getClass(),
"ReportHTML.addThumbRows.dataType.title", pages),
638 NbBundle.getMessage(
this.getClass(),
"ReportHTML.addThumbRows.dataType.msg"));
639 List<String> emptyHeaders =
new ArrayList<>();
640 for (
int i = 0; i < THUMBNAIL_COLUMNS; i++) {
641 emptyHeaders.add(
"");
643 startTable(emptyHeaders);
646 if (failsContentCheck(content)) {
650 AbstractFile file = (AbstractFile) content;
653 String thumbnailPath = prepareThumbnail(file);
654 if (thumbnailPath == null) {
657 String contentPath = saveContent(file,
"thumbs_fullsize");
660 nameInImage = file.getUniquePath();
661 }
catch (TskCoreException ex) {
662 nameInImage = file.getName();
665 StringBuilder linkToThumbnail =
new StringBuilder();
666 linkToThumbnail.append(
"<a href=\"");
667 linkToThumbnail.append(contentPath);
668 linkToThumbnail.append(
"\">");
669 linkToThumbnail.append(
"<img src=\"").append(thumbnailPath).append(
"\" title=\"").append(nameInImage).append(
"\"/>");
670 linkToThumbnail.append(
"</a><br>");
671 linkToThumbnail.append(file.getName()).append(
"<br>");
673 Services services = currentCase.getServices();
674 TagsManager tagsManager = services.getTagsManager();
676 List<ContentTag> tags = tagsManager.getContentTagsByContent(content);
677 if (tags.size() > 0) {
678 linkToThumbnail.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.thumbLink.tags") );
680 for (
int i = 0; i < tags.size(); i++) {
681 ContentTag tag = tags.get(i);
682 linkToThumbnail.append(tag.getName().getDisplayName());
683 if (i != tags.size() - 1) {
684 linkToThumbnail.append(
", ");
687 }
catch (TskCoreException ex) {
688 logger.log(Level.WARNING,
"Could not find get tags for file.", ex);
691 currentRow.add(linkToThumbnail.toString());
696 if (currentRow.isEmpty() ==
false) {
697 int extraCells = THUMBNAIL_COLUMNS - currentRow.size();
698 for (
int i = 0; i < extraCells; i++) {
706 rowCount = totalCount;
709 private boolean failsContentCheck(Content c) {
710 if (c instanceof AbstractFile ==
false) {
713 AbstractFile file = (AbstractFile) c;
715 file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS ||
716 file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) {
728 public String saveContent(AbstractFile file, String dirName) {
730 String dirName2 = dirName.replace(
"/",
"_");
731 dirName2 = dirName2.replace(
"\\",
"_");
734 StringBuilder localFilePath =
new StringBuilder();
736 localFilePath.append(path);
737 localFilePath.append(dirName2);
738 File localFileFolder =
new File(localFilePath.toString());
739 if (!localFileFolder.exists()) {
740 localFileFolder.mkdirs();
744 String fileName = file.getName();
745 String objectIdSuffix =
"_" + file.getId();
746 int lastDotIndex = fileName.lastIndexOf(
".");
747 if (lastDotIndex != -1 && lastDotIndex != 0) {
749 fileName = fileName.substring(0, lastDotIndex) + objectIdSuffix + fileName.substring(lastDotIndex, fileName.length());
754 fileName += objectIdSuffix;
756 localFilePath.append(File.separator);
757 localFilePath.append(fileName);
761 File localFile =
new File(localFilePath.toString());
762 if (!localFile.exists()) {
763 ExtractFscContentVisitor.extract(file, localFile, null, null);
767 return localFilePath.toString().substring(path.length());
776 public String dateToString(
long date) {
777 SimpleDateFormat sdf =
new java.text.SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss");
778 return sdf.format(
new java.util.Date(date * 1000));
783 public String getRelativeFilePath() {
784 return "HTML Report" + File.separator +
"index.html";
789 public String getName() {
790 return NbBundle.getMessage(this.getClass(),
"ReportHTML.getName.text");
794 public String getDescription() {
795 return NbBundle.getMessage(this.getClass(),
"ReportHTML.getDesc.text");
801 private void writeCss() {
802 Writer cssOut = null;
804 cssOut =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(path +
"index.css"),
"UTF-8"));
805 String css =
"body {margin: 0px; padding: 0px; background: #FFFFFF; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353;}\n" +
806 "#content {padding: 30px;}\n" +
807 "#header {width:100%; padding: 10px; line-height: 25px; background: #07A; color: #FFF; font-size: 20px;}\n" +
808 "h1 {font-size: 20px; font-weight: normal; color: #07A; padding: 0 0 7px 0; margin-top: 25px; border-bottom: 1px solid #D6D6D6;}\n" +
809 "h2 {font-size: 20px; font-weight: bolder; color: #07A;}\n" +
810 "h3 {font-size: 16px; color: #07A;}\n" +
811 "h4 {background: #07A; color: #FFF; font-size: 16px; margin: 0 0 0 25px; padding: 0; padding-left: 15px;}\n" +
812 "ul.nav {list-style-type: none; line-height: 35px; padding: 0px; margin-left: 15px;}\n" +
813 "ul li a {font-size: 14px; color: #444; text-decoration: none; padding-left: 25px;}\n" +
814 "ul li a:hover {text-decoration: underline;}\n" +
815 "p {margin: 0 0 20px 0;}\n" +
816 "table {white-space:nowrap; min-width: 700px; padding: 2; margin: 0; border-collapse: collapse; border-bottom: 2px solid #e5e5e5;}\n" +
817 ".keyword_list table {margin: 0 0 25px 25px; border-bottom: 2px solid #dedede;}\n" +
818 "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" +
819 "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" +
820 "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" +
821 "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; }\n" +
822 "table tr:nth-child(even) td {background: #f3f3f3;}";
824 }
catch (FileNotFoundException ex) {
825 logger.log(Level.SEVERE,
"Could not find index.css file to write to.", ex);
826 }
catch (UnsupportedEncodingException ex) {
827 logger.log(Level.SEVERE,
"Did not recognize encoding when writing index.css.", ex);
828 }
catch (IOException ex) {
829 logger.log(Level.SEVERE,
"Error creating Writer for index.css.", ex);
836 }
catch (IOException ex) {
844 private void writeIndex() {
845 Writer indexOut = null;
846 String indexFilePath = path +
"index.html";
848 indexOut =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(indexFilePath),
"UTF-8"));
849 StringBuilder index =
new StringBuilder();
850 index.append(
"<head>\n<title>").append(
851 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.title", currentCase.getName())).append(
853 index.append(
"<link rel=\"icon\" type=\"image/ico\" href=\"favicon.ico\" />\n");
854 index.append(
"</head>\n");
855 index.append(
"<frameset cols=\"350px,*\">\n");
856 index.append(
"<frame src=\"nav.html\" name=\"nav\">\n");
857 index.append(
"<frame src=\"summary.html\" name=\"content\">\n");
858 index.append(
"<noframes>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.noFrames.msg")).append(
"<br />\n");
859 index.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.noFrames.seeNav")).append(
"<br />\n");
860 index.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeIndex.seeSum")).append(
"</noframes>\n");
861 index.append(
"</frameset>\n");
862 index.append(
"</html>");
863 indexOut.write(index.toString());
864 Case.getCurrentCase().addReport(indexFilePath, NbBundle.getMessage(
this.getClass(),
865 "ReportHTML.writeIndex.srcModuleName.text"),
"");
866 }
catch (IOException ex) {
867 logger.log(Level.SEVERE,
"Error creating Writer for index.html: {0}", ex);
868 }
catch (TskCoreException ex) {
869 String errorMessage = String.format(
"Error adding %s to case as a report", indexFilePath);
870 logger.log(Level.SEVERE, errorMessage, ex);
873 if(indexOut != null) {
877 }
catch (IOException ex) {
885 private void writeNav() {
886 Writer navOut = null;
888 navOut =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(path +
"nav.html"),
"UTF-8"));
889 StringBuilder nav =
new StringBuilder();
890 nav.append(
"<html>\n<head>\n\t<title>").append(
891 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeNav.title"))
892 .append(
"</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n</head>\n<body>\n");
893 nav.append(
"<div id=\"content\">\n<h1>").append(
894 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeNav.h1")).append(
"</h1>\n");
895 nav.append(
"<ul class=\"nav\">\n");
896 nav.append(
"<li style=\"background: url(summary.png) left center no-repeat;\"><a href=\"summary.html\" target=\"content\">")
897 .append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeNav.summary")).append(
"</a></li>\n");
899 for (String dataType : dataTypes.keySet()) {
900 String dataTypeEsc = dataTypeToFileName(dataType);
901 String iconFileName = useDataTypeIcon(dataType);
902 nav.append(
"<li style=\"background: url('").append(iconFileName)
903 .append(
"') left center no-repeat;\"><a href=\"")
904 .append(dataTypeEsc).append(
".html\" target=\"content\">")
905 .append(dataType).append(
" (").append(dataTypes.get(dataType))
906 .append(
")</a></li>\n");
908 nav.append(
"</ul>\n");
909 nav.append(
"</div>\n</body>\n</html>");
910 navOut.write(nav.toString());
911 }
catch (IOException ex) {
912 logger.log(Level.SEVERE,
"Failed to write end of report navigation menu: {0}", ex);
914 if (navOut != null) {
918 }
catch (IOException ex) {
919 logger.log(Level.WARNING,
"Could not close navigation out writer.");
924 InputStream in = null;
925 OutputStream output = null;
929 String generatorLogoPath = reportBranding.getGeneratorLogoPath();
930 if (generatorLogoPath != null && ! generatorLogoPath.isEmpty()) {
931 File from =
new File(generatorLogoPath);
932 File to =
new File(path);
933 FileUtil.copyFile(FileUtil.toFileObject(from), FileUtil.toFileObject(to),
"generator_logo");
936 String agencyLogoPath = reportBranding.getAgencyLogoPath();
937 if (agencyLogoPath != null && ! agencyLogoPath.isEmpty() ) {
938 File from =
new File(agencyLogoPath);
939 File to =
new File(path);
940 FileUtil.copyFile(FileUtil.toFileObject(from), FileUtil.toFileObject(to),
"agency_logo");
943 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/favicon.ico");
944 output =
new FileOutputStream(
new File(path + File.separator +
"favicon.ico"));
945 FileUtil.copy(in, output);
949 in = getClass().getResourceAsStream(
"/org/sleuthkit/autopsy/report/images/summary.png");
950 output =
new FileOutputStream(
new File(path + File.separator +
"summary.png"));
951 FileUtil.copy(in, output);
956 }
catch (IOException ex) {
957 logger.log(Level.SEVERE,
"Failed to extract images for HTML report.", ex);
959 if (output != null) {
963 }
catch (IOException ex) {
968 }
catch (IOException ex) {
977 private void writeSummary() {
980 out =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(path +
"summary.html"),
"UTF-8"));
981 StringBuilder head =
new StringBuilder();
982 head.append(
"<html>\n<head>\n<title>").append(
983 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.title")).append(
"</title>\n");
984 head.append(
"<style type=\"text/css\">\n");
985 head.append(
"body { padding: 0px; margin: 0px; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353; }\n");
986 head.append(
"#wrapper { width: 90%; margin: 0px auto; margin-top: 35px; }\n");
987 head.append(
"h1 { color: #07A; font-size: 36px; line-height: 42px; font-weight: normal; margin: 0px; border-bottom: 1px solid #81B9DB; }\n");
988 head.append(
"h1 span { color: #F00; display: block; font-size: 16px; font-weight: bold; line-height: 22px;}\n");
989 head.append(
"h2 { padding: 0 0 3px 0; margin: 0px; color: #07A; font-weight: normal; border-bottom: 1px dotted #81B9DB; }\n");
990 head.append(
"table td { padding-right: 25px; }\n");
991 head.append(
"p.subheadding { padding: 0px; margin: 0px; font-size: 11px; color: #B5B5B5; }\n");
992 head.append(
".title { width: 660px; margin-bottom: 50px; }\n");
993 head.append(
".left { float: left; width: 250px; margin-top: 20px; text-align: center; }\n");
994 head.append(
".left img { max-width: 250px; max-height: 250px; min-width: 200px; min-height: 200px; }\n");
995 head.append(
".right { float: right; width: 385px; margin-top: 25px; font-size: 14px; }\n");
996 head.append(
".clear { clear: both; }\n");
997 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");
998 head.append(
".info table { margin: 0 25px 20px 25px; }\n");
999 head.append(
"</style>\n");
1000 head.append(
"</head>\n<body>\n");
1001 out.write(head.toString());
1003 DateFormat datetimeFormat =
new SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss");
1004 Date date =
new Date();
1005 String datetime = datetimeFormat.format(date);
1007 String caseName = currentCase.getName();
1008 String caseNumber = currentCase.getNumber();
1009 String examiner = currentCase.getExaminer();
1012 imagecount = currentCase.getDataSources().size();
1013 }
catch (TskCoreException ex) {
1017 StringBuilder summary =
new StringBuilder();
1018 boolean running =
false;
1019 if (IngestManager.getInstance().isIngestRunning()) {
1023 final String reportTitle = reportBranding.getReportTitle();
1024 final String reportFooter = reportBranding.getReportFooter();
1025 final boolean agencyLogoSet = reportBranding.getAgencyLogoPath() != null && !reportBranding.getAgencyLogoPath().isEmpty();
1026 final boolean generatorLogoSet = reportBranding.getGeneratorLogoPath() != null && !reportBranding.getGeneratorLogoPath().isEmpty();
1028 summary.append(
"<div id=\"wrapper\">\n");
1029 summary.append(
"<h1>").append(reportTitle)
1030 .append(running ? NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.warningMsg") :
"")
1032 summary.append(
"<p class=\"subheadding\">").append(
1033 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.reportGenOn.text", datetime)).append(
"</p>\n");
1034 summary.append(
"<div class=\"title\">\n");
1035 if (agencyLogoSet) {
1036 summary.append(
"<div class=\"left\">\n");
1037 summary.append(
"<img src=\"agency_logo.png\" />\n");
1038 summary.append(
"</div>\n");
1040 final String align = agencyLogoSet?
"right":
"left";
1041 summary.append(
"<div class=\"").append(align).append(
"\">\n");
1042 summary.append(
"<table>\n");
1043 summary.append(
"<tr><td>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.caseName"))
1044 .append(
"</td><td>").append(caseName).append(
"</td></tr>\n");
1045 summary.append(
"<tr><td>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.caseNum"))
1046 .append(
"</td><td>").append(!caseNumber.isEmpty() ? caseNumber : NbBundle
1047 .getMessage(this.getClass(),
"ReportHTML.writeSum.noCaseNum")).append(
"</td></tr>\n");
1048 summary.append(
"<tr><td>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.examiner")).append(
"</td><td>")
1049 .append(!examiner.isEmpty() ? examiner : NbBundle
1050 .getMessage(this.getClass(),
"ReportHTML.writeSum.noExaminer"))
1051 .append(
"</td></tr>\n");
1052 summary.append(
"<tr><td>").append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.numImages"))
1053 .append(
"</td><td>").append(imagecount).append(
"</td></tr>\n");
1054 summary.append(
"</table>\n");
1055 summary.append(
"</div>\n");
1056 summary.append(
"<div class=\"clear\"></div>\n");
1057 summary.append(
"</div>\n");
1058 summary.append(NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.imageInfoHeading"));
1059 summary.append(
"<div class=\"info\">\n");
1061 for (Content c : currentCase.getDataSources()) {
1062 summary.append(
"<p>").append(c.getName()).append(
"</p>\n");
1063 if (c instanceof Image) {
1064 Image img = (Image) c;
1066 summary.append(
"<table>\n");
1067 summary.append(
"<tr><td>").append(
1068 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.timezone"))
1069 .append(
"</td><td>").append(img.getTimeZone()).append(
"</td></tr>\n");
1070 for(String imgPath : img.getPaths()) {
1071 summary.append(
"<tr><td>").append(
1072 NbBundle.getMessage(
this.getClass(),
"ReportHTML.writeSum.path"))
1073 .append(
"</td><td>").append(imgPath).append(
"</td></tr>\n");
1075 summary.append(
"</table>\n");
1078 }
catch (TskCoreException ex) {
1079 logger.log(Level.WARNING,
"Unable to get image information for the HTML report.");
1081 summary.append(
"</div>\n");
1082 if (generatorLogoSet) {
1083 summary.append(
"<div class=\"left\">\n");
1084 summary.append(
"<img src=\"generator_logo.png\" />\n");
1085 summary.append(
"</div>\n");
1087 summary.append(
"<div class=\"clear\"></div>\n");
1088 if (reportFooter != null) {
1089 summary.append(
"<p class=\"subheadding\">").append(reportFooter).append(
"</p>\n");
1091 summary.append(
"</div>\n");
1092 summary.append(
"</body></html>");
1093 out.write(summary.toString());
1094 }
catch (FileNotFoundException ex) {
1095 logger.log(Level.SEVERE,
"Could not find summary.html file to write to.");
1096 }
catch (UnsupportedEncodingException ex) {
1097 logger.log(Level.SEVERE,
"Did not recognize encoding when writing summary.hmtl.");
1098 }
catch (IOException ex) {
1099 logger.log(Level.SEVERE,
"Error creating Writer for summary.html.");
1106 }
catch (IOException ex) {
1111 private String prepareThumbnail(AbstractFile file) {
1112 File thumbFile = ImageUtils.getIconFile(file, ImageUtils.ICON_SIZE_MEDIUM);
1113 if (thumbFile.exists() ==
false) {
1117 File to =
new File(thumbsPath);
1118 FileObject from = FileUtil.toFileObject(thumbFile);
1119 FileObject dest = FileUtil.toFileObject(to);
1120 FileUtil.copyFile(from, dest, thumbFile.getName(),
"");
1121 }
catch (IOException ex) {
1122 logger.log(Level.SEVERE,
"Failed to write thumb file to report directory.", ex);
1125 return THUMBS_REL_PATH + thumbFile.getName();
static String escapeFileName(String fileName)