Autopsy  4.7.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ReportHTML.java
Go to the documentation of this file.
1 /*
2  *
3  * Autopsy Forensic Browser
4  *
5  * Copyright 2012-2018 Basis Technology Corp.
6  *
7  * Copyright 2012 42six Solutions.
8  * Contact: aebadirad <at> 42six <dot> com
9  * Project Contact/Architect: carrier <at> sleuthkit <dot> org
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 package org.sleuthkit.autopsy.report;
24 
25 import java.awt.image.BufferedImage;
26 import java.io.BufferedWriter;
27 import java.io.File;
28 import java.io.FileNotFoundException;
29 import java.io.FileOutputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.io.OutputStreamWriter;
34 import java.io.UnsupportedEncodingException;
35 import java.io.Writer;
36 import java.nio.file.Files;
37 import java.nio.file.Path;
38 import java.nio.file.Paths;
39 import java.text.DateFormat;
40 import java.text.SimpleDateFormat;
41 import java.util.ArrayList;
42 import java.util.Date;
43 import java.util.HashMap;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Set;
47 import java.util.TreeMap;
48 import java.util.logging.Level;
49 import javax.imageio.ImageIO;
50 import org.openide.filesystems.FileUtil;
51 import org.openide.util.NbBundle;
62 import org.sleuthkit.datamodel.AbstractFile;
63 import org.sleuthkit.datamodel.BlackboardArtifact;
64 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
65 import org.sleuthkit.datamodel.Content;
66 import org.sleuthkit.datamodel.ContentTag;
67 import org.sleuthkit.datamodel.Image;
68 import org.sleuthkit.datamodel.IngestJobInfo;
69 import org.sleuthkit.datamodel.IngestModuleInfo;
70 import org.sleuthkit.datamodel.SleuthkitCase;
71 import org.sleuthkit.datamodel.TskCoreException;
72 import org.sleuthkit.datamodel.TskData;
73 import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
74 
75 class ReportHTML implements TableReportModule {
76 
77  private static final Logger logger = Logger.getLogger(ReportHTML.class.getName());
78  private static final String THUMBS_REL_PATH = "thumbs" + File.separator; //NON-NLS
79  private static ReportHTML instance;
80  private static final int MAX_THUMBS_PER_PAGE = 1000;
81  private static final String HTML_SUBDIR = "content";
82  private Case currentCase;
83  private SleuthkitCase skCase;
84  static Integer THUMBNAIL_COLUMNS = 5;
85 
86  private Map<String, Integer> dataTypes;
87  private String path;
88  private String thumbsPath;
89  private String subPath;
90  private String currentDataType; // name of current data type
91  private Integer rowCount; // number of rows (aka artifacts or tags) for the current data type
92  private Writer out;
93 
94  private final ReportBranding reportBranding;
95 
96  // Get the default instance of this report
97  public static synchronized ReportHTML getDefault() {
98  if (instance == null) {
99  instance = new ReportHTML();
100  }
101  return instance;
102  }
103 
104  // Hidden constructor
105  private ReportHTML() {
106  reportBranding = new ReportBranding();
107  }
108 
109  // Refesh the member variables
110  private void refresh() throws NoCurrentCaseException {
111  currentCase = Case.getCurrentCaseThrows();
112  skCase = currentCase.getSleuthkitCase();
113 
114  dataTypes = new TreeMap<>();
115 
116  path = "";
117  thumbsPath = "";
118  subPath = "";
119  currentDataType = "";
120  rowCount = 0;
121 
122  if (out != null) {
123  try {
124  out.close();
125  } catch (IOException ex) {
126  }
127  }
128  out = null;
129  }
130 
137  private String dataTypeToFileName(String dataType) {
138 
139  String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(dataType);
140  // replace all ' ' with '_'
141  fileName = fileName.replaceAll(" ", "_");
142 
143  return fileName;
144  }
145 
150  private String useDataTypeIcon(String dataType) {
151  String iconFilePath;
152  String iconFileName;
153  InputStream in;
154  OutputStream output = null;
155 
156  logger.log(Level.INFO, "useDataTypeIcon: dataType = {0}", dataType); //NON-NLS
157 
158  // find the artifact with matching display name
159  BlackboardArtifact.ARTIFACT_TYPE artifactType = null;
160  for (ARTIFACT_TYPE v : ARTIFACT_TYPE.values()) {
161  if (v.getDisplayName().equals(dataType)) {
162  artifactType = v;
163  }
164  }
165 
166  if (null != artifactType) {
167  // set the icon file name
168  iconFileName = dataTypeToFileName(artifactType.getDisplayName()) + ".png"; //NON-NLS
169  iconFilePath = subPath + File.separator + iconFileName;
170 
171  // determine the source image to use
172  switch (artifactType) {
173  case TSK_WEB_BOOKMARK:
174  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/bookmarks.png"); //NON-NLS
175  break;
176  case TSK_WEB_COOKIE:
177  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/cookies.png"); //NON-NLS
178  break;
179  case TSK_WEB_HISTORY:
180  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/history.png"); //NON-NLS
181  break;
182  case TSK_WEB_DOWNLOAD:
183  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/downloads.png"); //NON-NLS
184  break;
185  case TSK_RECENT_OBJECT:
186  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/recent.png"); //NON-NLS
187  break;
188  case TSK_INSTALLED_PROG:
189  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/installed.png"); //NON-NLS
190  break;
191  case TSK_KEYWORD_HIT:
192  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/keywords.png"); //NON-NLS
193  break;
194  case TSK_HASHSET_HIT:
195  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/hash.png"); //NON-NLS
196  break;
197  case TSK_DEVICE_ATTACHED:
198  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/devices.png"); //NON-NLS
199  break;
200  case TSK_WEB_SEARCH_QUERY:
201  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/search.png"); //NON-NLS
202  break;
203  case TSK_METADATA_EXIF:
204  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/exif.png"); //NON-NLS
205  break;
206  case TSK_TAG_FILE:
207  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/userbookmarks.png"); //NON-NLS
208  break;
209  case TSK_TAG_ARTIFACT:
210  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/userbookmarks.png"); //NON-NLS
211  break;
212  case TSK_SERVICE_ACCOUNT:
213  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/account-icon-16.png"); //NON-NLS
214  break;
215  case TSK_CONTACT:
216  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/contact.png"); //NON-NLS
217  break;
218  case TSK_MESSAGE:
219  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/message.png"); //NON-NLS
220  break;
221  case TSK_CALLLOG:
222  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/calllog.png"); //NON-NLS
223  break;
224  case TSK_CALENDAR_ENTRY:
225  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/calendar.png"); //NON-NLS
226  break;
227  case TSK_SPEED_DIAL_ENTRY:
228  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/speeddialentry.png"); //NON-NLS
229  break;
230  case TSK_BLUETOOTH_PAIRING:
231  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/bluetooth.png"); //NON-NLS
232  break;
233  case TSK_GPS_BOOKMARK:
234  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gpsfav.png"); //NON-NLS
235  break;
236  case TSK_GPS_LAST_KNOWN_LOCATION:
237  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gps-lastlocation.png"); //NON-NLS
238  break;
239  case TSK_GPS_SEARCH:
240  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gps-search.png"); //NON-NLS
241  break;
242  case TSK_OS_INFO:
243  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/computer.png"); //NON-NLS
244  break;
245  case TSK_GPS_TRACKPOINT:
246  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gps_trackpoint.png"); //NON-NLS
247  break;
248  case TSK_GPS_ROUTE:
249  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gps_trackpoint.png"); //NON-NLS
250  break;
251  case TSK_EMAIL_MSG:
252  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS
253  break;
254  case TSK_ENCRYPTION_SUSPECTED:
255  case TSK_ENCRYPTION_DETECTED:
256  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/encrypted-file.png"); //NON-NLS
257  break;
258  case TSK_EXT_MISMATCH_DETECTED:
259  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/mismatch-16.png"); //NON-NLS
260  break;
261  case TSK_INTERESTING_ARTIFACT_HIT:
262  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS
263  break;
264  case TSK_INTERESTING_FILE_HIT:
265  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS
266  break;
267  case TSK_PROG_RUN:
268  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/installed.png"); //NON-NLS
269  break;
270  case TSK_REMOTE_DRIVE:
271  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/drive_network.png"); //NON-NLS
272  break;
273  case TSK_ACCOUNT:
274  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/accounts.png"); //NON-NLS
275  break;
276  default:
277  logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = " + dataType); //NON-NLS
278  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS
279  iconFileName = "star.png"; //NON-NLS
280  iconFilePath = subPath + File.separator + iconFileName;
281  break;
282  }
283  } else if (dataType.startsWith(ARTIFACT_TYPE.TSK_ACCOUNT.getDisplayName())) {
284  /*
285  * TSK_ACCOUNT artifacts get separated by their TSK_ACCOUNT_TYPE
286  * attribute, with a synthetic compound dataType name, so they are
287  * not caught by the switch statement above. For now we just give
288  * them all the general account icon, but we could do something else
289  * in the future.
290  */
291  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/accounts.png"); //NON-NLS
292  iconFileName = "accounts.png"; //NON-NLS
293  iconFilePath = subPath + File.separator + iconFileName;
294  } else { // no defined artifact found for this dataType
295  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS
296  iconFileName = "star.png"; //NON-NLS
297  iconFilePath = subPath + File.separator + iconFileName;
298  }
299 
300  try {
301  output = new FileOutputStream(iconFilePath);
302  FileUtil.copy(in, output);
303  in.close();
304  output.close();
305  } catch (IOException ex) {
306  logger.log(Level.SEVERE, "Failed to extract images for HTML report.", ex); //NON-NLS
307  } finally {
308  if (output != null) {
309  try {
310  output.flush();
311  output.close();
312  } catch (IOException ex) {
313  }
314  }
315  if (in != null) {
316  try {
317  in.close();
318  } catch (IOException ex) {
319  }
320  }
321  }
322 
323  return iconFileName;
324  }
325 
332  @Override
333  public void startReport(String baseReportDir) {
334  // Refresh the HTML report
335  try {
336  refresh();
337  } catch (NoCurrentCaseException ex) {
338  logger.log(Level.SEVERE, "Exception while getting open case."); //NON-NLS
339  return;
340  }
341  // Setup the path for the HTML report
342  this.path = baseReportDir; //NON-NLS
343  this.subPath = this.path + HTML_SUBDIR + File.separator;
344  this.thumbsPath = this.subPath + THUMBS_REL_PATH; //NON-NLS
345  try {
346  FileUtil.createFolder(new File(this.subPath));
347  FileUtil.createFolder(new File(this.thumbsPath));
348  } catch (IOException ex) {
349  logger.log(Level.SEVERE, "Unable to make HTML report folder."); //NON-NLS
350  }
351  // Write the basic files
352  writeCss();
353  writeIndex();
354  writeSummary();
355  }
356 
361  @Override
362  public void endReport() {
363  writeNav();
364  if (out != null) {
365  try {
366  out.close();
367  } catch (IOException ex) {
368  logger.log(Level.WARNING, "Could not close the output writer when ending report.", ex); //NON-NLS
369  }
370  }
371  }
372 
381  @Override
382  public void startDataType(String name, String description) {
383  String title = dataTypeToFileName(name);
384  try {
385  out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(subPath + title + ".html"), "UTF-8")); //NON-NLS
386  } catch (FileNotFoundException ex) {
387  logger.log(Level.SEVERE, "File not found: {0}", ex); //NON-NLS
388  } catch (UnsupportedEncodingException ex) {
389  logger.log(Level.SEVERE, "Unrecognized encoding"); //NON-NLS
390  }
391 
392  try {
393  StringBuilder page = new StringBuilder();
394  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"); //NON-NLS
395  page.append("<div id=\"header\">").append(name).append("</div>\n<div id=\"content\">\n"); //NON-NLS
396  if (!description.isEmpty()) {
397  page.append("<p><strong>"); //NON-NLS
398  page.append(description);
399  page.append("</string></p>\n"); //NON-NLS
400  }
401  out.write(page.toString());
402  currentDataType = name;
403  rowCount = 0;
404  } catch (IOException ex) {
405  logger.log(Level.SEVERE, "Failed to write page head: {0}", ex); //NON-NLS
406  }
407  }
408 
413  @Override
414  public void endDataType() {
415  dataTypes.put(currentDataType, rowCount);
416  try {
417  out.write("</div>\n</body>\n</html>\n"); //NON-NLS
418  } catch (IOException ex) {
419  logger.log(Level.SEVERE, "Failed to write end of HTML report.", ex); //NON-NLS
420  } finally {
421  if (out != null) {
422  try {
423  out.flush();
424  out.close();
425  } catch (IOException ex) {
426  logger.log(Level.WARNING, "Could not close the output writer when ending data type.", ex); //NON-NLS
427  }
428  out = null;
429  }
430  }
431  }
432 
438  @Override
439  public void startSet(String setName) {
440  StringBuilder set = new StringBuilder();
441  set.append("<h1><a name=\"").append(setName).append("\">").append(setName).append("</a></h1>\n"); //NON-NLS
442  set.append("<div class=\"keyword_list\">\n"); //NON-NLS
443 
444  try {
445  out.write(set.toString());
446  } catch (IOException ex) {
447  logger.log(Level.SEVERE, "Failed to write set: {0}", ex); //NON-NLS
448  }
449  }
450 
454  @Override
455  public void endSet() {
456  try {
457  out.write("</div>\n"); //NON-NLS
458  } catch (IOException ex) {
459  logger.log(Level.SEVERE, "Failed to write end of set: {0}", ex); //NON-NLS
460  }
461  }
462 
468  @Override
469  public void addSetIndex(List<String> sets) {
470  StringBuilder index = new StringBuilder();
471  index.append("<ul>\n"); //NON-NLS
472  for (String set : sets) {
473  index.append("\t<li><a href=\"#").append(set).append("\">").append(set).append("</a></li>\n"); //NON-NLS
474  }
475  index.append("</ul>\n"); //NON-NLS
476  try {
477  out.write(index.toString());
478  } catch (IOException ex) {
479  logger.log(Level.SEVERE, "Failed to add set index: {0}", ex); //NON-NLS
480  }
481  }
482 
488  @Override
489  public void addSetElement(String elementName) {
490  try {
491  out.write("<h4>" + elementName + "</h4>\n"); //NON-NLS
492  } catch (IOException ex) {
493  logger.log(Level.SEVERE, "Failed to write set element: {0}", ex); //NON-NLS
494  }
495  }
496 
502  @Override
503  public void startTable(List<String> titles) {
504  StringBuilder ele = new StringBuilder();
505  ele.append("<table>\n<thead>\n\t<tr>\n"); //NON-NLS
506  for (String title : titles) {
507  ele.append("\t\t<th>").append(title).append("</th>\n"); //NON-NLS
508  }
509  ele.append("\t</tr>\n</thead>\n"); //NON-NLS
510 
511  try {
512  out.write(ele.toString());
513  } catch (IOException ex) {
514  logger.log(Level.SEVERE, "Failed to write table start: {0}", ex); //NON-NLS
515  }
516  }
517 
525  public void startContentTagsTable(List<String> columnHeaders) {
526  StringBuilder htmlOutput = new StringBuilder();
527  htmlOutput.append("<table>\n<thead>\n\t<tr>\n"); //NON-NLS
528 
529  // Add the specified columns.
530  for (String columnHeader : columnHeaders) {
531  htmlOutput.append("\t\t<th>").append(columnHeader).append("</th>\n"); //NON-NLS
532  }
533 
534  // Add a column for a hyperlink to a local copy of the tagged content.
535  htmlOutput.append("\t\t<th></th>\n"); //NON-NLS
536 
537  htmlOutput.append("\t</tr>\n</thead>\n"); //NON-NLS
538 
539  try {
540  out.write(htmlOutput.toString());
541  } catch (IOException ex) {
542  logger.log(Level.SEVERE, "Failed to write table start: {0}", ex); //NON-NLS
543  }
544  }
545 
549  @Override
550  public void endTable() {
551  try {
552  out.write("</table>\n"); //NON-NLS
553  } catch (IOException ex) {
554  logger.log(Level.SEVERE, "Failed to write end of table: {0}", ex); //NON-NLS
555  }
556  }
557 
563  @Override
564  public void addRow(List<String> row) {
565  StringBuilder builder = new StringBuilder();
566  builder.append("\t<tr>\n"); //NON-NLS
567  for (String cell : row) {
568  String escapeHTMLCell = EscapeUtil.escapeHtml(cell);
569  builder.append("\t\t<td>").append(escapeHTMLCell).append("</td>\n"); //NON-NLS
570  }
571  builder.append("\t</tr>\n"); //NON-NLS
572  rowCount++;
573 
574  try {
575  out.write(builder.toString());
576  } catch (IOException ex) {
577  logger.log(Level.SEVERE, "Failed to write row to out.", ex); //NON-NLS
578  } catch (NullPointerException ex) {
579  logger.log(Level.SEVERE, "Output writer is null. Page was not initialized before writing.", ex); //NON-NLS
580  }
581  }
582 
593  public void addRowWithTaggedContentHyperlink(List<String> row, ContentTag contentTag) {
594  Content content = contentTag.getContent();
595  if (content instanceof AbstractFile == false) {
596  addRow(row);
597  return;
598  }
599  AbstractFile file = (AbstractFile) content;
600  // Add the hyperlink to the row. A column header for it was created in startTable().
601  StringBuilder localFileLink = new StringBuilder();
602  // Don't make a local copy of the file if it is a directory or unallocated space.
603  if (!(file.isDir()
604  || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
605  || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)) {
606  localFileLink.append("<a href=\""); //NON-NLS
607  // save it in a folder based on the tag name
608  String localFilePath = saveContent(file, contentTag.getName().getDisplayName());
609  localFileLink.append(localFilePath);
610  localFileLink.append("\" target=\"_top\">");
611  }
612 
613  StringBuilder builder = new StringBuilder();
614  builder.append("\t<tr>\n"); //NON-NLS
615  int positionCounter = 0;
616  for (String cell : row) {
617  // position-dependent code used to format this report. Not great, but understandable for formatting.
618  if (positionCounter == 1) { // Convert the file name to a hyperlink and left-align it
619  builder.append("\t\t<td class=\"left_align_cell\">").append(localFileLink.toString()).append(cell).append("</a></td>\n"); //NON-NLS
620  } else if (positionCounter == 7) { // Right-align the bytes column.
621  builder.append("\t\t<td class=\"right_align_cell\">").append(cell).append("</td>\n"); //NON-NLS
622  } else { // Regular case, not a file name nor a byte count
623  builder.append("\t\t<td>").append(cell).append("</td>\n"); //NON-NLS
624  }
625  ++positionCounter;
626  }
627  builder.append("\t</tr>\n"); //NON-NLS
628  rowCount++;
629 
630  try {
631  out.write(builder.toString());
632  } catch (IOException ex) {
633  logger.log(Level.SEVERE, "Failed to write row to out.", ex); //NON-NLS
634  } catch (NullPointerException ex) {
635  logger.log(Level.SEVERE, "Output writer is null. Page was not initialized before writing.", ex); //NON-NLS
636  }
637  }
638 
644  public void addThumbnailRows(Set<Content> images) {
645  List<String> currentRow = new ArrayList<>();
646  int totalCount = 0;
647  int pages = 1;
648  for (Content content : images) {
649  if (currentRow.size() == THUMBNAIL_COLUMNS) {
650  addRow(currentRow);
651  currentRow.clear();
652  }
653 
654  if (totalCount == MAX_THUMBS_PER_PAGE) {
655  // manually set the row count so the count of items shown in the
656  // navigation page reflects the number of thumbnails instead of
657  // the number of rows.
658  rowCount = totalCount;
659  totalCount = 0;
660  pages++;
661  endTable();
662  endDataType();
663  startDataType(NbBundle.getMessage(this.getClass(), "ReportHTML.addThumbRows.dataType.title", pages),
664  NbBundle.getMessage(this.getClass(), "ReportHTML.addThumbRows.dataType.msg"));
665  List<String> emptyHeaders = new ArrayList<>();
666  for (int i = 0; i < THUMBNAIL_COLUMNS; i++) {
667  emptyHeaders.add("");
668  }
669  startTable(emptyHeaders);
670  }
671 
672  if (failsContentCheck(content)) {
673  continue;
674  }
675 
676  AbstractFile file = (AbstractFile) content;
677 
678  // save copies of the orginal image and thumbnail image
679  String thumbnailPath = prepareThumbnail(file);
680  if (thumbnailPath == null) {
681  continue;
682  }
683  String contentPath = saveContent(file, "thumbs_fullsize"); //NON-NLS
684  String nameInImage;
685  try {
686  nameInImage = file.getUniquePath();
687  } catch (TskCoreException ex) {
688  nameInImage = file.getName();
689  }
690 
691  StringBuilder linkToThumbnail = new StringBuilder();
692  linkToThumbnail.append("<div id='thumbnail_link'>");
693  linkToThumbnail.append("<a href=\""); //NON-NLS
694  linkToThumbnail.append(contentPath);
695  linkToThumbnail.append("\" target=\"_top\">");
696  linkToThumbnail.append("<img src=\"").append(thumbnailPath).append("\" title=\"").append(nameInImage).append("\"/>"); //NON-NLS
697  linkToThumbnail.append("</a><br>"); //NON-NLS
698  linkToThumbnail.append(file.getName()).append("<br>"); //NON-NLS
699 
700  Services services = currentCase.getServices();
701  TagsManager tagsManager = services.getTagsManager();
702  try {
703  List<ContentTag> tags = tagsManager.getContentTagsByContent(content);
704  if (tags.size() > 0) {
705  linkToThumbnail.append(NbBundle.getMessage(this.getClass(), "ReportHTML.thumbLink.tags"));
706  }
707  for (int i = 0; i < tags.size(); i++) {
708  ContentTag tag = tags.get(i);
709  String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
710  linkToThumbnail.append(tag.getName().getDisplayName() + notableString);
711  if (i != tags.size() - 1) {
712  linkToThumbnail.append(", ");
713  }
714  }
715  } catch (TskCoreException ex) {
716  logger.log(Level.WARNING, "Could not find get tags for file.", ex); //NON-NLS
717  }
718  linkToThumbnail.append("</div>");
719  currentRow.add(linkToThumbnail.toString());
720 
721  totalCount++;
722  }
723 
724  if (currentRow.isEmpty() == false) {
725  int extraCells = THUMBNAIL_COLUMNS - currentRow.size();
726  for (int i = 0; i < extraCells; i++) {
727  // Finish out the row.
728  currentRow.add("");
729  }
730  addRow(currentRow);
731  }
732 
733  // manually set rowCount to be the total number of images.
734  rowCount = totalCount;
735  }
736 
737  private boolean failsContentCheck(Content c) {
738  if (c instanceof AbstractFile == false) {
739  return true;
740  }
741  AbstractFile file = (AbstractFile) c;
742  if (file.isDir()
743  || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
744  || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) {
745  return true;
746  }
747  return false;
748  }
749 
759  public String saveContent(AbstractFile file, String dirName) {
760  // clean up the dir name passed in
761  String dirName2 = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(dirName);
762 
763  // Make a folder for the local file with the same tagName as the tag.
764  StringBuilder localFilePath = new StringBuilder(); // full path
765 
766  localFilePath.append(subPath);
767  localFilePath.append(dirName2);
768  File localFileFolder = new File(localFilePath.toString());
769  if (!localFileFolder.exists()) {
770  localFileFolder.mkdirs();
771  }
772 
773  // Construct a file tagName for the local file that incorporates the file id to ensure uniqueness.
774  String fileName = file.getName();
775  String objectIdSuffix = "_" + file.getId();
776  int lastDotIndex = fileName.lastIndexOf(".");
777  if (lastDotIndex != -1 && lastDotIndex != 0) {
778  // The file tagName has a conventional extension. Insert the object id before the '.' of the extension.
779  fileName = fileName.substring(0, lastDotIndex) + objectIdSuffix + fileName.substring(lastDotIndex, fileName.length());
780  } else {
781  // The file has no extension or the only '.' in the file is an initial '.', as in a hidden file.
782  // Add the object id to the end of the file tagName.
783  fileName += objectIdSuffix;
784  }
785  localFilePath.append(File.separator);
786  localFilePath.append(fileName);
787 
788  // If the local file doesn't already exist, create it now.
789  // The existence check is necessary because it is possible to apply multiple tags with the same tagName to a file.
790  File localFile = new File(localFilePath.toString());
791  if (!localFile.exists()) {
792  ExtractFscContentVisitor.extract(file, localFile, null, null);
793  }
794 
795  // get the relative path
796  return localFilePath.toString().substring(subPath.length());
797  }
798 
806  @Override
807  public String dateToString(long date) {
808  SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
809  return sdf.format(new java.util.Date(date * 1000));
810  }
811 
812  @Override
813  public String getRelativeFilePath() {
814  return "report.html"; //NON-NLS
815  }
816 
817  @Override
818  public String getName() {
819  return NbBundle.getMessage(this.getClass(), "ReportHTML.getName.text");
820  }
821 
822  @Override
823  public String getDescription() {
824  return NbBundle.getMessage(this.getClass(), "ReportHTML.getDesc.text");
825  }
826 
830  private void writeCss() {
831  Writer cssOut = null;
832  try {
833  cssOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(subPath + "index.css"), "UTF-8")); //NON-NLS NON-NLS
834  String css = "body {margin: 0px; padding: 0px; background: #FFFFFF; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353;}\n"
835  + //NON-NLS
836  "#content {padding: 30px;}\n"
837  + //NON-NLS
838  "#header {width:100%; padding: 10px; line-height: 25px; background: #07A; color: #FFF; font-size: 20px;}\n"
839  + //NON-NLS
840  "h1 {font-size: 20px; font-weight: normal; color: #07A; padding: 0 0 7px 0; margin-top: 25px; border-bottom: 1px solid #D6D6D6;}\n"
841  + //NON-NLS
842  "h2 {font-size: 20px; font-weight: bolder; color: #07A;}\n"
843  + //NON-NLS
844  "h3 {font-size: 16px; color: #07A;}\n"
845  + //NON-NLS
846  "h4 {background: #07A; color: #FFF; font-size: 16px; margin: 0 0 0 25px; padding: 0; padding-left: 15px;}\n"
847  + //NON-NLS
848  "ul.nav {list-style-type: none; line-height: 35px; padding: 0px; margin-left: 15px;}\n"
849  + //NON-NLS
850  "ul li a {font-size: 14px; color: #444; text-decoration: none; padding-left: 25px;}\n"
851  + //NON-NLS
852  "ul li a:hover {text-decoration: underline;}\n"
853  + //NON-NLS
854  "p {margin: 0 0 20px 0;}\n"
855  + //NON-NLS
856  "table {white-space:nowrap; min-width: 700px; padding: 2; margin: 0; border-collapse: collapse; border-bottom: 2px solid #e5e5e5;}\n"
857  + //NON-NLS
858  ".keyword_list table {margin: 0 0 25px 25px; border-bottom: 2px solid #dedede;}\n"
859  + //NON-NLS
860  "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"
861  + //NON-NLS
862  "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"
863  + //NON-NLS
864  "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"
865  + //NON-NLS
866  "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"
867  + //NON-NLS
868  "table tr:nth-child(even) td {background: #f3f3f3;}\n"
869  + //NON-NLS
870  "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;}";
871  cssOut.write(css);
872  } catch (FileNotFoundException ex) {
873  logger.log(Level.SEVERE, "Could not find index.css file to write to.", ex); //NON-NLS
874  } catch (UnsupportedEncodingException ex) {
875  logger.log(Level.SEVERE, "Did not recognize encoding when writing index.css.", ex); //NON-NLS
876  } catch (IOException ex) {
877  logger.log(Level.SEVERE, "Error creating Writer for index.css.", ex); //NON-NLS
878  } finally {
879  try {
880  if (cssOut != null) {
881  cssOut.flush();
882  cssOut.close();
883  }
884  } catch (IOException ex) {
885  }
886  }
887  }
888 
892  private void writeIndex() {
893  Writer indexOut = null;
894  String indexFilePath = path + "report.html"; //NON-NLS
895  Case openCase;
896  try {
897  openCase = Case.getCurrentCaseThrows();
898  } catch (NoCurrentCaseException ex) {
899  logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
900  return;
901  }
902  try {
903  indexOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(indexFilePath), "UTF-8")); //NON-NLS
904  StringBuilder index = new StringBuilder();
905  final String reportTitle = reportBranding.getReportTitle();
906  String iconPath = reportBranding.getAgencyLogoPath();
907  if (iconPath == null) {
908  // use default Autopsy icon if custom icon is not set
909  iconPath = HTML_SUBDIR + "favicon.ico";
910  } else {
911  iconPath = Paths.get(reportBranding.getAgencyLogoPath()).getFileName().toString(); //ref to writeNav() for agency_logo
912  }
913  index.append("<head>\n<title>").append(reportTitle).append(" ").append(
914  NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.title", currentCase.getDisplayName())).append(
915  "</title>\n"); //NON-NLS
916  index.append("<link rel=\"icon\" type=\"image/ico\" href=\"")
917  .append(iconPath).append("\" />\n"); //NON-NLS
918  index.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"); //NON-NLS
919  index.append("</head>\n"); //NON-NLS
920  index.append("<frameset cols=\"350px,*\">\n"); //NON-NLS
921  index.append("<frame src=\"" + HTML_SUBDIR).append(File.separator).append("nav.html\" name=\"nav\">\n"); //NON-NLS
922  index.append("<frame src=\"" + HTML_SUBDIR).append(File.separator).append("summary.html\" name=\"content\">\n"); //NON-NLS
923  index.append("<noframes>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.noFrames.msg")).append("<br />\n"); //NON-NLS
924  index.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.noFrames.seeNav")).append("<br />\n"); //NON-NLS
925  index.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.seeSum")).append("</noframes>\n"); //NON-NLS
926  index.append("</frameset>\n"); //NON-NLS
927  index.append("</html>"); //NON-NLS
928  indexOut.write(index.toString());
929  openCase.addReport(indexFilePath, NbBundle.getMessage(this.getClass(),
930  "ReportHTML.writeIndex.srcModuleName.text"), "");
931  } catch (IOException ex) {
932  logger.log(Level.SEVERE, "Error creating Writer for report.html: {0}", ex); //NON-NLS
933  } catch (TskCoreException ex) {
934  String errorMessage = String.format("Error adding %s to case as a report", indexFilePath); //NON-NLS
935  logger.log(Level.SEVERE, errorMessage, ex);
936  } finally {
937  try {
938  if (indexOut != null) {
939  indexOut.flush();
940  indexOut.close();
941  }
942  } catch (IOException ex) {
943  }
944  }
945  }
946 
950  private void writeNav() {
951  Writer navOut = null;
952  try {
953  navOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(subPath + "nav.html"), "UTF-8")); //NON-NLS
954  StringBuilder nav = new StringBuilder();
955  nav.append("<html>\n<head>\n\t<title>").append( //NON-NLS
956  NbBundle.getMessage(this.getClass(), "ReportHTML.writeNav.title"))
957  .append("</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n"); //NON-NLS
958  nav.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n</head>\n<body>\n"); //NON-NLS
959  nav.append("<div id=\"content\">\n<h1>").append( //NON-NLS
960  NbBundle.getMessage(this.getClass(), "ReportHTML.writeNav.h1")).append("</h1>\n"); //NON-NLS
961  nav.append("<ul class=\"nav\">\n"); //NON-NLS
962  nav.append("<li style=\"background: url(summary.png) left center no-repeat;\"><a href=\"summary.html\" target=\"content\">") //NON-NLS
963  .append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeNav.summary")).append("</a></li>\n"); //NON-NLS
964 
965  for (String dataType : dataTypes.keySet()) {
966  String dataTypeEsc = dataTypeToFileName(dataType);
967  String iconFileName = useDataTypeIcon(dataType);
968  nav.append("<li style=\"background: url('").append(iconFileName) //NON-NLS
969  .append("') left center no-repeat;\"><a href=\"") //NON-NLS
970  .append(dataTypeEsc).append(".html\" target=\"content\">") //NON-NLS
971  .append(dataType).append(" (").append(dataTypes.get(dataType))
972  .append(")</a></li>\n"); //NON-NLS
973  }
974  nav.append("</ul>\n"); //NON-NLS
975  nav.append("</div>\n</body>\n</html>"); //NON-NLS
976  navOut.write(nav.toString());
977  } catch (IOException ex) {
978  logger.log(Level.SEVERE, "Failed to write end of report navigation menu: {0}", ex); //NON-NLS
979  } finally {
980  if (navOut != null) {
981  try {
982  navOut.flush();
983  navOut.close();
984  } catch (IOException ex) {
985  logger.log(Level.WARNING, "Could not close navigation out writer."); //NON-NLS
986  }
987  }
988  }
989 
990  InputStream in = null;
991  OutputStream output = null;
992  try {
993 
994  //pull generator and agency logo from branding, and the remaining resources from the core jar
995  String generatorLogoPath = reportBranding.getGeneratorLogoPath();
996  if (generatorLogoPath != null && !generatorLogoPath.isEmpty()) {
997  File from = new File(generatorLogoPath);
998  File to = new File(subPath);
999  FileUtil.copyFile(FileUtil.toFileObject(from), FileUtil.toFileObject(to), "generator_logo"); //NON-NLS
1000  }
1001 
1002  String agencyLogoPath = reportBranding.getAgencyLogoPath();
1003  if (agencyLogoPath != null && !agencyLogoPath.isEmpty()) {
1004  Path destinationPath = Paths.get(subPath);
1005  Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), destinationPath.resolve(Paths.get(agencyLogoPath).getFileName())); //NON-NLS
1006  }
1007 
1008  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/favicon.ico"); //NON-NLS
1009  output = new FileOutputStream(new File(subPath + "favicon.ico"));
1010  FileUtil.copy(in, output);
1011  in.close();
1012  output.close();
1013 
1014  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/summary.png"); //NON-NLS
1015  output = new FileOutputStream(new File(subPath + "summary.png"));
1016  FileUtil.copy(in, output);
1017  in.close();
1018  output.close();
1019 
1020  } catch (IOException ex) {
1021  logger.log(Level.SEVERE, "Failed to extract images for HTML report.", ex); //NON-NLS
1022  } finally {
1023  if (output != null) {
1024  try {
1025  output.flush();
1026  output.close();
1027  } catch (IOException ex) {
1028  }
1029  }
1030  if (in != null) {
1031  try {
1032  in.close();
1033  } catch (IOException ex) {
1034  }
1035  }
1036  }
1037  }
1038 
1042  private void writeSummary() {
1043  Writer out = null;
1044  try {
1045  out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(subPath + "summary.html"), "UTF-8")); //NON-NLS
1046  StringBuilder head = new StringBuilder();
1047  head.append("<html>\n<head>\n<title>").append( //NON-NLS
1048  NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.title")).append("</title>\n"); //NON-NLS
1049  head.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"); //NON-NLS
1050  head.append("<style type=\"text/css\">\n"); //NON-NLS
1051  head.append("body { padding: 0px; margin: 0px; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353; }\n"); //NON-NLS
1052  head.append("#wrapper { width: 90%; margin: 0px auto; margin-top: 35px; }\n"); //NON-NLS
1053  head.append("h1 { color: #07A; font-size: 36px; line-height: 42px; font-weight: normal; margin: 0px; border-bottom: 1px solid #81B9DB; }\n"); //NON-NLS
1054  head.append("h1 span { color: #F00; display: block; font-size: 16px; font-weight: bold; line-height: 22px;}\n"); //NON-NLS
1055  head.append("h2 { padding: 0 0 3px 0; margin: 0px; color: #07A; font-weight: normal; border-bottom: 1px dotted #81B9DB; }\n"); //NON-NLS
1056  head.append("h3 { padding: 5 0 3px 0; margin: 0px; color: #07A; font-weight: normal; }\n");
1057  head.append("table td { padding: 5px 25px 5px 0px; vertical-align:top;}\n"); //NON-NLS
1058  head.append("p.subheadding { padding: 0px; margin: 0px; font-size: 11px; color: #B5B5B5; }\n"); //NON-NLS
1059  head.append(".title { width: 660px; margin-bottom: 50px; }\n"); //NON-NLS
1060  head.append(".left { float: left; width: 250px; margin-top: 20px; text-align: center; }\n"); //NON-NLS
1061  head.append(".left img { max-width: 250px; max-height: 250px; min-width: 200px; min-height: 200px; }\n"); //NON-NLS
1062  head.append(".right { float: right; width: 385px; margin-top: 25px; font-size: 14px; }\n"); //NON-NLS
1063  head.append(".clear { clear: both; }\n"); //NON-NLS
1064  head.append(".info { padding: 10px 0;}\n");
1065  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"); //NON-NLS
1066  head.append(".info table { margin: 10px 25px 10px 25px; }\n"); //NON-NLS
1067  head.append("ul {padding: 0;margin: 0;list-style-type: none;}");
1068  head.append("li {padding-bottom: 5px;}");
1069  head.append("</style>\n"); //NON-NLS
1070  head.append("</head>\n<body>\n"); //NON-NLS
1071  out.write(head.toString());
1072 
1073  DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
1074  Date date = new Date();
1075  String datetime = datetimeFormat.format(date);
1076 
1077  StringBuilder summary = new StringBuilder();
1078  boolean running = false;
1079  if (IngestManager.getInstance().isIngestRunning()) {
1080  running = true;
1081  }
1082  SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
1083  List<IngestJobInfo> ingestJobs = skCase.getIngestJobs();
1084  final String reportTitle = reportBranding.getReportTitle();
1085  final String reportFooter = reportBranding.getReportFooter();
1086  final boolean generatorLogoSet = reportBranding.getGeneratorLogoPath() != null && !reportBranding.getGeneratorLogoPath().isEmpty();
1087 
1088  summary.append("<div id=\"wrapper\">\n"); //NON-NLS
1089  summary.append("<h1>").append(reportTitle) //NON-NLS
1090  .append(running ? NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.warningMsg") : "")
1091  .append("</h1>\n"); //NON-NLS
1092  summary.append("<p class=\"subheadding\">").append( //NON-NLS
1093  NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.reportGenOn.text", datetime)).append("</p>\n"); //NON-NLS
1094  summary.append("<div class=\"title\">\n"); //NON-NLS
1095  summary.append(writeSummaryCaseDetails());
1096  summary.append(writeSummaryImageInfo());
1097  summary.append(writeSummarySoftwareInfo(skCase,ingestJobs));
1098  summary.append(writeSummaryIngestHistoryInfo(skCase,ingestJobs));
1099  if (generatorLogoSet) {
1100  summary.append("<div class=\"left\">\n"); //NON-NLS
1101  summary.append("<img src=\"generator_logo.png\" />\n"); //NON-NLS
1102  summary.append("</div>\n"); //NON-NLS
1103  }
1104  summary.append("<div class=\"clear\"></div>\n"); //NON-NLS
1105  if (reportFooter != null) {
1106  summary.append("<p class=\"subheadding\">").append(reportFooter).append("</p>\n"); //NON-NLS
1107  }
1108  summary.append("</div>\n"); //NON-NLS
1109  summary.append("</body></html>"); //NON-NLS
1110  out.write(summary.toString());
1111  } catch (FileNotFoundException ex) {
1112  logger.log(Level.SEVERE, "Could not find summary.html file to write to."); //NON-NLS
1113  } catch (UnsupportedEncodingException ex) {
1114  logger.log(Level.SEVERE, "Did not recognize encoding when writing summary.hmtl."); //NON-NLS
1115  } catch (IOException ex) {
1116  logger.log(Level.SEVERE, "Error creating Writer for summary.html."); //NON-NLS
1117  } catch (NoCurrentCaseException | TskCoreException ex) {
1118  logger.log(Level.WARNING, "Unable to get current sleuthkit Case for the HTML report.");
1119  } finally {
1120  try {
1121  if (out != null) {
1122  out.flush();
1123  out.close();
1124  }
1125  } catch (IOException ex) {
1126  }
1127  }
1128  }
1129 
1136  private StringBuilder writeSummaryCaseDetails(){
1137  StringBuilder summary = new StringBuilder();
1138  String caseName = currentCase.getDisplayName();
1139  String caseNumber = currentCase.getNumber();
1140  String examiner = currentCase.getExaminer();
1141  final boolean agencyLogoSet = reportBranding.getAgencyLogoPath() != null && !reportBranding.getAgencyLogoPath().isEmpty();
1142  int imagecount;
1143  try {
1144  imagecount = currentCase.getDataSources().size();
1145  } catch (TskCoreException ex) {
1146  imagecount = 0;
1147  }
1148  summary.append("<div class=\"title\">\n"); //NON-NLS
1149  if (agencyLogoSet) {
1150  summary.append("<div class=\"left\">\n"); //NON-NLS
1151  summary.append("<img src=\"");
1152  summary.append(Paths.get(reportBranding.getAgencyLogoPath()).getFileName().toString());
1153  summary.append("\" />\n"); //NON-NLS
1154  summary.append("</div>\n"); //NON-NLS
1155  }
1156  final String align = agencyLogoSet ? "right" : "left"; //NON-NLS NON-NLS
1157  summary.append("<div class=\"").append(align).append("\">\n"); //NON-NLS
1158  summary.append("<table>\n"); //NON-NLS
1159  summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.caseName")) //NON-NLS
1160  .append("</td><td>").append(caseName).append("</td></tr>\n"); //NON-NLS NON-NLS
1161  summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.caseNum")) //NON-NLS
1162  .append("</td><td>").append(!caseNumber.isEmpty() ? caseNumber : NbBundle //NON-NLS
1163  .getMessage(this.getClass(), "ReportHTML.writeSum.noCaseNum")).append("</td></tr>\n"); //NON-NLS
1164  summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.examiner")).append("</td><td>") //NON-NLS
1165  .append(!examiner.isEmpty() ? examiner : NbBundle
1166  .getMessage(this.getClass(), "ReportHTML.writeSum.noExaminer"))
1167  .append("</td></tr>\n"); //NON-NLS
1168  summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.numImages")) //NON-NLS
1169  .append("</td><td>").append(imagecount).append("</td></tr>\n"); //NON-NLS
1170  summary.append("</table>\n"); //NON-NLS
1171  summary.append("</div>\n"); //NON-NLS
1172  summary.append("<div class=\"clear\"></div>\n"); //NON-NLS
1173  summary.append("</div>\n"); //NON-NLS
1174  return summary;
1175  }
1176 
1183  private StringBuilder writeSummaryImageInfo() {
1184  StringBuilder summary = new StringBuilder();
1185  summary.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.imageInfoHeading"));
1186  summary.append("<div class=\"info\">\n"); //NON-NLS
1187  try {
1188  for (Content c : currentCase.getDataSources()) {
1189  summary.append("<p>").append(c.getName()).append("</p>\n"); //NON-NLS
1190  if (c instanceof Image) {
1191  Image img = (Image) c;
1192 
1193  summary.append("<table>\n"); //NON-NLS
1194  summary.append("<tr><td>").append( //NON-NLS
1195  NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.timezone"))
1196  .append("</td><td>").append(img.getTimeZone()).append("</td></tr>\n"); //NON-NLS
1197  for (String imgPath : img.getPaths()) {
1198  summary.append("<tr><td>").append( //NON-NLS
1199  NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.path"))
1200  .append("</td><td>").append(imgPath).append("</td></tr>\n"); //NON-NLS
1201  }
1202  summary.append("</table>\n"); //NON-NLS
1203  }
1204  }
1205  } catch (TskCoreException ex) {
1206  logger.log(Level.WARNING, "Unable to get image information for the HTML report."); //NON-NLS
1207  }
1208  summary.append("</div>\n"); //NON-NLS
1209  return summary;
1210  }
1211 
1218  private StringBuilder writeSummarySoftwareInfo(SleuthkitCase skCase, List<IngestJobInfo> ingestJobs) {
1219  StringBuilder summary = new StringBuilder();
1220  summary.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.softwareInfoHeading"));
1221  summary.append("<div class=\"info\">\n");
1222  summary.append("<table>\n");
1223  summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.autopsyVersion"))
1224  .append("</td><td>").append(Version.getVersion()).append("</td></tr>\n");
1225  Map<Long, IngestModuleInfo> moduleInfoHashMap = new HashMap<>();
1226  for (IngestJobInfo ingestJob : ingestJobs) {
1227  List<IngestModuleInfo> ingestModules = ingestJob.getIngestModuleInfo();
1228  for (IngestModuleInfo ingestModule : ingestModules) {
1229  if (!moduleInfoHashMap.containsKey(ingestModule.getIngestModuleId())) {
1230  moduleInfoHashMap.put(ingestModule.getIngestModuleId(), ingestModule);
1231  }
1232  }
1233  }
1234  TreeMap<String, String> modules = new TreeMap<>();
1235  for (IngestModuleInfo moduleinfo : moduleInfoHashMap.values()) {
1236  modules.put(moduleinfo.getDisplayName(), moduleinfo.getVersion());
1237  }
1238  for (Map.Entry<String, String> module : modules.entrySet()) {
1239  summary.append("<tr><td>").append(module.getKey()).append(" Module:")
1240  .append("</td><td>").append(module.getValue()).append("</td></tr>\n");
1241  }
1242  summary.append("</table>\n");
1243  summary.append("</div>\n");
1244  summary.append("<div class=\"clear\"></div>\n"); //NON-NLS
1245  return summary;
1246  }
1247 
1254  private StringBuilder writeSummaryIngestHistoryInfo(SleuthkitCase skCase, List<IngestJobInfo> ingestJobs) {
1255  StringBuilder summary = new StringBuilder();
1256  try {
1257  summary.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.ingestHistoryHeading"));
1258  summary.append("<div class=\"info\">\n");
1259  int jobnumber = 1;
1260 
1261  for (IngestJobInfo ingestJob : ingestJobs) {
1262  summary.append("<h3>Job ").append(jobnumber).append(":</h3>\n");
1263  summary.append("<table>\n");
1264  summary.append("<tr><td>").append("Data Source:")
1265  .append("</td><td>").append(skCase.getContentById(ingestJob.getObjectId()).getName()).append("</td></tr>\n");
1266  summary.append("<tr><td>").append("Status:")
1267  .append("</td><td>").append(ingestJob.getStatus()).append("</td></tr>\n");
1268  summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.modulesEnabledHeading"))
1269  .append("</td><td>");
1270  List<IngestModuleInfo> ingestModules = ingestJob.getIngestModuleInfo();
1271  summary.append("<ul>\n");
1272  for (IngestModuleInfo ingestModule : ingestModules) {
1273  summary.append("<li>").append(ingestModule.getDisplayName()).append("</li>");
1274  }
1275  summary.append("</ul>\n");
1276  jobnumber++;
1277  summary.append("</td></tr>\n");
1278  summary.append("</table>\n");
1279  }
1280  summary.append("</div>\n");
1281  } catch (TskCoreException ex) {
1282  logger.log(Level.WARNING, "Unable to get ingest jobs for the HTML report.");
1283  }
1284  return summary;
1285  }
1286 
1287  private String prepareThumbnail(AbstractFile file) {
1288  BufferedImage bufferedThumb = ImageUtils.getThumbnail(file, ImageUtils.ICON_SIZE_MEDIUM);
1289  File thumbFile = Paths.get(thumbsPath, file.getName() + ".png").toFile();
1290  if (bufferedThumb == null) {
1291  return null;
1292  }
1293  try {
1294  ImageIO.write(bufferedThumb, "png", thumbFile);
1295  } catch (IOException ex) {
1296  logger.log(Level.WARNING, "Failed to write thumb file to report directory.", ex); //NON-NLS
1297  return null;
1298  }
1299  if (thumbFile.exists()
1300  == false) {
1301  return null;
1302  }
1303  return THUMBS_REL_PATH
1304  + thumbFile.getName();
1305  }
1306 
1307 }
static String escapeFileName(String fileName)
Definition: FileUtil.java:169

Copyright © 2012-2016 Basis Technology. Generated on: Mon Jun 18 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.