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

Copyright © 2012-2018 Basis Technology. Generated on: Thu Oct 4 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.