Autopsy  3.1
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-2014 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.io.BufferedWriter;
26 import java.io.File;
27 import java.io.FileNotFoundException;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.OutputStream;
32 import java.io.OutputStreamWriter;
33 import java.io.UnsupportedEncodingException;
34 import java.io.Writer;
35 import java.text.DateFormat;
36 import java.text.SimpleDateFormat;
37 import java.util.ArrayList;
38 import java.util.Date;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.TreeMap;
42 import java.util.logging.Level;
43 import org.openide.filesystems.FileObject;
44 import org.openide.util.Exceptions;
45 import org.openide.util.NbBundle;
47 import org.openide.filesystems.FileUtil;
63 
64  class ReportHTML implements TableReportModule {
65  private static final Logger logger = Logger.getLogger(ReportHTML.class.getName());
66  private static final String THUMBS_REL_PATH = "thumbs" + File.separator; //NON-NLS
67  private static ReportHTML instance;
68  private static final int MAX_THUMBS_PER_PAGE = 1000;
69  private Case currentCase;
70  private SleuthkitCase skCase;
71  static Integer THUMBNAIL_COLUMNS = 5;
72 
73  private Map<String, Integer> dataTypes;
74  private String path;
75  private String thumbsPath;
76  private String currentDataType; // name of current data type
77  private Integer rowCount; // number of rows (aka artifacts or tags) for the current data type
78  private Writer out;
79 
80 
81  private final ReportBranding reportBranding;
82 
83  // Get the default instance of this report
84  public static synchronized ReportHTML getDefault() {
85  if (instance == null) {
86  instance = new ReportHTML();
87  }
88  return instance;
89  }
90 
91  // Hidden constructor
92  private ReportHTML() {
93  reportBranding = new ReportBranding();
94  }
95 
96  // Refesh the member variables
97  private void refresh() {
98  currentCase = Case.getCurrentCase();
99  skCase = currentCase.getSleuthkitCase();
100 
101  dataTypes = new TreeMap<>();
102 
103  path = "";
104  thumbsPath = "";
105  currentDataType = "";
106  rowCount = 0;
107 
108  if (out != null) {
109  try {
110  out.close();
111  } catch (IOException ex) {
112  }
113  }
114  out = null;
115  }
116 
122  private String dataTypeToFileName(String dataType) {
123 
124  String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(dataType);
125  // replace all ' ' with '_'
126  fileName = fileName.replaceAll(" ", "_");
127 
128  return fileName;
129  }
130 
131 
136  private String useDataTypeIcon(String dataType)
137  {
138  String iconFilePath;
139  String iconFileName;
140  InputStream in;
141  OutputStream output = null;
142 
143  logger.log(Level.INFO, "useDataTypeIcon: dataType = {0}", dataType); //NON-NLS
144 
145  // find the artifact with matching display name
146  BlackboardArtifact.ARTIFACT_TYPE artifactType = null;
147  for (ARTIFACT_TYPE v : ARTIFACT_TYPE.values()) {
148  if (v.getDisplayName().equals(dataType)) {
149  artifactType = v;
150  }
151  }
152 
153  if (null != artifactType)
154  {
155  // set the icon file name
156  iconFileName = dataTypeToFileName(artifactType.getDisplayName()) + ".png"; //NON-NLS
157  iconFilePath = path + File.separator + iconFileName;
158 
159  // determine the source image to use
160  switch (artifactType) {
161  case TSK_WEB_BOOKMARK:
162  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/bookmarks.png"); //NON-NLS
163  break;
164  case TSK_WEB_COOKIE:
165  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/cookies.png"); //NON-NLS
166  break;
167  case TSK_WEB_HISTORY:
168  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/history.png"); //NON-NLS
169  break;
170  case TSK_WEB_DOWNLOAD:
171  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/downloads.png"); //NON-NLS
172  break;
173  case TSK_RECENT_OBJECT:
174  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/recent.png"); //NON-NLS
175  break;
176  case TSK_INSTALLED_PROG:
177  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/installed.png"); //NON-NLS
178  break;
179  case TSK_KEYWORD_HIT:
180  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/keywords.png"); //NON-NLS
181  break;
182  case TSK_HASHSET_HIT:
183  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/hash.png"); //NON-NLS
184  break;
185  case TSK_DEVICE_ATTACHED:
186  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/devices.png"); //NON-NLS
187  break;
188  case TSK_WEB_SEARCH_QUERY:
189  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/search.png"); //NON-NLS
190  break;
191  case TSK_METADATA_EXIF:
192  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/exif.png"); //NON-NLS
193  break;
194  case TSK_TAG_FILE:
195  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/userbookmarks.png"); //NON-NLS
196  break;
197  case TSK_TAG_ARTIFACT:
198  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/userbookmarks.png"); //NON-NLS
199  break;
200  case TSK_SERVICE_ACCOUNT:
201  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/account-icon-16.png"); //NON-NLS
202  break;
203  case TSK_CONTACT:
204  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/contact.png"); //NON-NLS
205  break;
206  case TSK_MESSAGE:
207  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/message.png"); //NON-NLS
208  break;
209  case TSK_CALLLOG:
210  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/calllog.png"); //NON-NLS
211  break;
212  case TSK_CALENDAR_ENTRY:
213  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/calendar.png"); //NON-NLS
214  break;
215  case TSK_SPEED_DIAL_ENTRY:
216  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/speeddialentry.png"); //NON-NLS
217  break;
218  case TSK_BLUETOOTH_PAIRING:
219  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/bluetooth.png"); //NON-NLS
220  break;
221  case TSK_GPS_BOOKMARK:
222  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gpsfav.png"); //NON-NLS
223  break;
224  case TSK_GPS_LAST_KNOWN_LOCATION:
225  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gps-lastlocation.png"); //NON-NLS
226  break;
227  case TSK_GPS_SEARCH:
228  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gps-search.png"); //NON-NLS
229  break;
230  case TSK_OS_INFO:
231  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/computer.png"); //NON-NLS
232  break;
233  case TSK_GPS_TRACKPOINT:
234  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gps_trackpoint.png"); //NON-NLS
235  break;
236  case TSK_GPS_ROUTE:
237  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gps_trackpoint.png"); //NON-NLS
238  break;
239  case TSK_EMAIL_MSG:
240  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS
241  break;
242  case TSK_ENCRYPTION_DETECTED:
243  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/encrypted-file.png"); //NON-NLS
244  break;
245  case TSK_EXT_MISMATCH_DETECTED:
246  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/mismatch-16.png"); //NON-NLS
247  break;
248  case TSK_INTERESTING_ARTIFACT_HIT:
249  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS
250  break;
251  case TSK_INTERESTING_FILE_HIT:
252  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS
253  break;
254  case TSK_PROG_RUN:
255  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/installed.png"); //NON-NLS
256  break;
257  case TSK_REMOTE_DRIVE:
258  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/drive_network.png"); //NON-NLS
259  break;
260  default:
261  logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = " + dataType); //NON-NLS
262  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS
263  iconFileName = "star.png"; //NON-NLS
264  iconFilePath = path + File.separator + iconFileName;
265  break;
266  }
267  }
268  else { // no defined artifact found for this dataType
269  logger.log(Level.WARNING, "useDataTypeIcon: no artifact found for data type = " + dataType); //NON-NLS
270  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS
271  iconFileName = "star.png"; //NON-NLS
272  iconFilePath = path + File.separator + iconFileName;
273  }
274 
275  try {
276  output = new FileOutputStream(iconFilePath);
277  FileUtil.copy(in, output);
278  in.close();
279  output.close();
280  } catch (IOException ex) {
281  logger.log(Level.SEVERE, "Failed to extract images for HTML report.", ex); //NON-NLS
282  } finally {
283  if (output != null) {
284  try {
285  output.flush();
286  output.close();
287  } catch (IOException ex) {
288  }
289  } if (in != null) {
290  try {
291  in.close();
292  } catch (IOException ex) {
293  }
294  }
295  }
296 
297  return iconFileName;
298  }
304  @Override
305  public void startReport(String baseReportDir) {
306  // Refresh the HTML report
307  refresh();
308  // Setup the path for the HTML report
309  this.path = baseReportDir + "HTML Report" + File.separator; //NON-NLS
310  this.thumbsPath = this.path + "thumbs" + File.separator; //NON-NLS
311  try {
312  FileUtil.createFolder(new File(this.path));
313  FileUtil.createFolder(new File(this.thumbsPath));
314  } catch (IOException ex) {
315  logger.log(Level.SEVERE, "Unable to make HTML report folder."); //NON-NLS
316  }
317  // Write the basic files
318  writeCss();
319  writeIndex();
320  writeSummary();
321  }
322 
327  @Override
328  public void endReport() {
329  writeNav();
330  if (out != null) {
331  try {
332  out.close();
333  } catch (IOException ex) {
334  logger.log(Level.WARNING, "Could not close the output writer when ending report.", ex); //NON-NLS
335  }
336  }
337  }
338 
347  @Override
348  public void startDataType(String name, String description) {
349  String title = dataTypeToFileName(name);
350  try {
351  out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path + title + ".html"), "UTF-8")); //NON-NLS
352  } catch (FileNotFoundException ex) {
353  logger.log(Level.SEVERE, "File not found: {0}", ex); //NON-NLS
354  } catch (UnsupportedEncodingException ex) {
355  logger.log(Level.SEVERE, "Unrecognized encoding"); //NON-NLS
356  }
357 
358  try {
359  StringBuilder page = new StringBuilder();
360  page.append("<html>\n<head>\n\t<title>").append(name).append("</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n</head>\n<body>\n"); //NON-NLS
361  page.append("<div id=\"header\">").append(name).append("</div>\n<div id=\"content\">\n"); //NON-NLS
362  if (!description.isEmpty()) {
363  page.append("<p><strong>"); //NON-NLS
364  page.append(description);
365  page.append("</string></p>\n"); //NON-NLS
366  }
367  out.write(page.toString());
368  currentDataType = name;
369  rowCount = 0;
370  } catch (IOException ex) {
371  logger.log(Level.SEVERE, "Failed to write page head: {0}", ex); //NON-NLS
372  }
373  }
374 
379  @Override
380  public void endDataType() {
381  dataTypes.put(currentDataType, rowCount);
382  try {
383  out.write("</div>\n</body>\n</html>\n"); //NON-NLS
384  } catch (IOException ex) {
385  Exceptions.printStackTrace(ex);
386  } finally {
387  if(out != null) {
388  try {
389  out.flush();
390  out.close();
391  } catch (IOException ex) {
392  logger.log(Level.WARNING, "Could not close the output writer when ending data type.", ex); //NON-NLS
393  }
394  out = null;
395  }
396  }
397  }
398 
403  @Override
404  public void startSet(String setName) {
405  StringBuilder set = new StringBuilder();
406  set.append("<h1><a name=\"").append(setName).append("\">").append(setName).append("</a></h1>\n"); //NON-NLS
407  set.append("<div class=\"keyword_list\">\n"); //NON-NLS
408 
409  try {
410  out.write(set.toString());
411  } catch (IOException ex) {
412  logger.log(Level.SEVERE, "Failed to write set: {0}", ex); //NON-NLS
413  }
414  }
415 
419  @Override
420  public void endSet() {
421  try {
422  out.write("</div>\n"); //NON-NLS
423  } catch (IOException ex) {
424  logger.log(Level.SEVERE, "Failed to write end of set: {0}", ex); //NON-NLS
425  }
426  }
427 
432  @Override
433  public void addSetIndex(List<String> sets) {
434  StringBuilder index = new StringBuilder();
435  index.append("<ul>\n"); //NON-NLS
436  for (String set : sets) {
437  index.append("\t<li><a href=\"#").append(set).append("\">").append(set).append("</a></li>\n"); //NON-NLS
438  }
439  index.append("</ul>\n"); //NON-NLS
440  try {
441  out.write(index.toString());
442  } catch (IOException ex) {
443  logger.log(Level.SEVERE, "Failed to add set index: {0}", ex); //NON-NLS
444  }
445  }
446 
451  @Override
452  public void addSetElement(String elementName) {
453  try {
454  out.write("<h4>" + elementName + "</h4>\n"); //NON-NLS
455  } catch (IOException ex) {
456  logger.log(Level.SEVERE, "Failed to write set element: {0}", ex); //NON-NLS
457  }
458  }
459 
464  @Override
465  public void startTable(List<String> titles) {
466  StringBuilder ele = new StringBuilder();
467  ele.append("<table>\n<thead>\n\t<tr>\n"); //NON-NLS
468  for(String title : titles) {
469  ele.append("\t\t<th>").append(title).append("</th>\n"); //NON-NLS
470  }
471  ele.append("\t</tr>\n</thead>\n"); //NON-NLS
472 
473  try {
474  out.write(ele.toString());
475  } catch (IOException ex) {
476  logger.log(Level.SEVERE, "Failed to write table start: {0}", ex); //NON-NLS
477  }
478  }
479 
487  public void startContentTagsTable(List<String> columnHeaders) {
488  StringBuilder htmlOutput = new StringBuilder();
489  htmlOutput.append("<table>\n<thead>\n\t<tr>\n"); //NON-NLS
490 
491  // Add the specified columns.
492  for(String columnHeader : columnHeaders) {
493  htmlOutput.append("\t\t<th>").append(columnHeader).append("</th>\n"); //NON-NLS
494  }
495 
496  // Add a column for a hyperlink to a local copy of the tagged content.
497  htmlOutput.append("\t\t<th></th>\n"); //NON-NLS
498 
499  htmlOutput.append("\t</tr>\n</thead>\n"); //NON-NLS
500 
501  try {
502  out.write(htmlOutput.toString());
503  } catch (IOException ex) {
504  logger.log(Level.SEVERE, "Failed to write table start: {0}", ex); //NON-NLS
505  }
506  }
507 
511  @Override
512  public void endTable() {
513  try {
514  out.write("</table>\n"); //NON-NLS
515  } catch (IOException ex) {
516  logger.log(Level.SEVERE, "Failed to write end of table: {0}", ex); //NON-NLS
517  }
518  }
519 
524  @Override
525  public void addRow(List<String> row) {
526  StringBuilder builder = new StringBuilder();
527  builder.append("\t<tr>\n"); //NON-NLS
528  for (String cell : row) {
529  builder.append("\t\t<td>").append(cell).append("</td>\n"); //NON-NLS
530  }
531  builder.append("\t</tr>\n"); //NON-NLS
532  rowCount++;
533 
534  try {
535  out.write(builder.toString());
536  } catch (IOException ex) {
537  logger.log(Level.SEVERE, "Failed to write row to out.", ex); //NON-NLS
538  } catch (NullPointerException ex) {
539  logger.log(Level.SEVERE, "Output writer is null. Page was not initialized before writing.", ex); //NON-NLS
540  }
541  }
542 
552  public void addRowWithTaggedContentHyperlink(List<String> row, ContentTag contentTag) {
553  Content content = contentTag.getContent();
554  if (content instanceof AbstractFile == false) {
555  addRow(row);
556  return;
557  }
558  AbstractFile file = (AbstractFile) content;
559  // Don't make a local copy of the file if it is a directory or unallocated space.
560  if (file.isDir() ||
561  file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS ||
562  file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) {
563  row.add("");
564  return;
565  }
566 
567  // Add metadata about the file to HTML output
568  row.add(file.getMtimeAsDate());
569  row.add(file.getCtimeAsDate());
570  row.add(file.getAtimeAsDate());
571  row.add(file.getCrtimeAsDate());
572  row.add(Long.toString(file.getSize()));
573  row.add(file.getMd5Hash());
574 
575  // save it in a folder based on the tag name
576  String localFilePath = saveContent(file, contentTag.getName().getDisplayName());
577 
578  // Add the hyperlink to the row. A column header for it was created in startTable().
579  StringBuilder localFileLink = new StringBuilder();
580  localFileLink.append("<a href=\""); //NON-NLS
581  localFileLink.append(localFilePath);
582  localFileLink.append("\">");
583 
584  StringBuilder builder = new StringBuilder();
585  builder.append("\t<tr>\n"); //NON-NLS
586  int positionCounter=0;
587  for (String cell : row) {
588  // position-dependent code used to format this report. Not great, but understandable for formatting.
589  if(positionCounter==1) { // Convert the file name to a hyperlink and left-align it
590  builder.append("\t\t<td class=\"left_align_cell\">").append(localFileLink.toString()).append(cell).append("</a></td>\n"); //NON-NLS
591  }
592  else if (positionCounter==7) { // Right-align the bytes column.
593  builder.append("\t\t<td class=\"right_align_cell\">").append(cell).append("</td>\n"); //NON-NLS
594  }
595  else { // Regular case, not a file name nor a byte count
596  builder.append("\t\t<td>").append(cell).append("</td>\n"); //NON-NLS
597  }
598  ++positionCounter;
599  }
600  builder.append("\t</tr>\n"); //NON-NLS
601  rowCount++;
602 
603  try {
604  out.write(builder.toString());
605  }
606  catch (IOException ex) {
607  logger.log(Level.SEVERE, "Failed to write row to out.", ex); //NON-NLS
608  }
609  catch (NullPointerException ex) {
610  logger.log(Level.SEVERE, "Output writer is null. Page was not initialized before writing.", ex); //NON-NLS
611  }
612  }
613 
618  public void addThumbnailRows(List<Content> images) {
619  List<String> currentRow = new ArrayList<>();
620  int totalCount = 0;
621  int pages = 0;
622  for (Content content : images) {
623  if (currentRow.size() == THUMBNAIL_COLUMNS) {
624  addRow(currentRow);
625  currentRow.clear();
626  }
627 
628  if (totalCount == MAX_THUMBS_PER_PAGE) {
629  // manually set the row count so the count of items shown in the
630  // navigation page reflects the number of thumbnails instead of
631  // the number of rows.
632  rowCount = totalCount;
633  totalCount = 0;
634  pages++;
635  endTable();
636  endDataType();
637  startDataType(NbBundle.getMessage(this.getClass(), "ReportHTML.addThumbRows.dataType.title", pages),
638  NbBundle.getMessage(this.getClass(), "ReportHTML.addThumbRows.dataType.msg"));
639  List<String> emptyHeaders = new ArrayList<>();
640  for (int i = 0; i < THUMBNAIL_COLUMNS; i++) {
641  emptyHeaders.add("");
642  }
643  startTable(emptyHeaders);
644  }
645 
646  if (failsContentCheck(content)) {
647  continue;
648  }
649 
650  AbstractFile file = (AbstractFile) content;
651 
652  // save copies of the orginal image and thumbnail image
653  String thumbnailPath = prepareThumbnail(file);
654  if (thumbnailPath == null) {
655  continue;
656  }
657  String contentPath = saveContent(file, "thumbs_fullsize"); //NON-NLS
658  String nameInImage;
659  try {
660  nameInImage = file.getUniquePath();
661  } catch (TskCoreException ex) {
662  nameInImage = file.getName();
663  }
664 
665  StringBuilder linkToThumbnail = new StringBuilder();
666  linkToThumbnail.append("<a href=\""); //NON-NLS
667  linkToThumbnail.append(contentPath);
668  linkToThumbnail.append("\">");
669  linkToThumbnail.append("<img src=\"").append(thumbnailPath).append("\" title=\"").append(nameInImage).append("\"/>"); //NON-NLS
670  linkToThumbnail.append("</a><br>"); //NON-NLS
671  linkToThumbnail.append(file.getName()).append("<br>"); //NON-NLS
672 
673  Services services = currentCase.getServices();
674  TagsManager tagsManager = services.getTagsManager();
675  try {
676  List<ContentTag> tags = tagsManager.getContentTagsByContent(content);
677  if (tags.size() > 0) {
678  linkToThumbnail.append(NbBundle.getMessage(this.getClass(), "ReportHTML.thumbLink.tags") );
679  }
680  for (int i = 0; i < tags.size(); i++) {
681  ContentTag tag = tags.get(i);
682  linkToThumbnail.append(tag.getName().getDisplayName());
683  if (i != tags.size() - 1) {
684  linkToThumbnail.append(", ");
685  }
686  }
687  } catch (TskCoreException ex) {
688  logger.log(Level.WARNING, "Could not find get tags for file.", ex); //NON-NLS
689  }
690 
691  currentRow.add(linkToThumbnail.toString());
692 
693  totalCount++;
694  }
695 
696  if (currentRow.isEmpty() == false) {
697  int extraCells = THUMBNAIL_COLUMNS - currentRow.size();
698  for (int i = 0; i < extraCells; i++) {
699  // Finish out the row.
700  currentRow.add("");
701  }
702  addRow(currentRow);
703  }
704 
705  // manually set rowCount to be the total number of images.
706  rowCount = totalCount;
707  }
708 
709  private boolean failsContentCheck(Content c) {
710  if (c instanceof AbstractFile == false) {
711  return true;
712  }
713  AbstractFile file = (AbstractFile) c;
714  if (file.isDir() ||
715  file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS ||
716  file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) {
717  return true;
718  }
719  return false;
720  }
721 
728  public String saveContent(AbstractFile file, String dirName) {
729  // clean up the dir name passed in
730  String dirName2 = dirName.replace("/", "_");
731  dirName2 = dirName2.replace("\\", "_");
732 
733  // Make a folder for the local file with the same tagName as the tag.
734  StringBuilder localFilePath = new StringBuilder(); // full path
735 
736  localFilePath.append(path);
737  localFilePath.append(dirName2);
738  File localFileFolder = new File(localFilePath.toString());
739  if (!localFileFolder.exists()) {
740  localFileFolder.mkdirs();
741  }
742 
743  // Construct a file tagName for the local file that incorporates the file id to ensure uniqueness.
744  String fileName = file.getName();
745  String objectIdSuffix = "_" + file.getId();
746  int lastDotIndex = fileName.lastIndexOf(".");
747  if (lastDotIndex != -1 && lastDotIndex != 0) {
748  // The file tagName has a conventional extension. Insert the object id before the '.' of the extension.
749  fileName = fileName.substring(0, lastDotIndex) + objectIdSuffix + fileName.substring(lastDotIndex, fileName.length());
750  }
751  else {
752  // The file has no extension or the only '.' in the file is an initial '.', as in a hidden file.
753  // Add the object id to the end of the file tagName.
754  fileName += objectIdSuffix;
755  }
756  localFilePath.append(File.separator);
757  localFilePath.append(fileName);
758 
759  // If the local file doesn't already exist, create it now.
760  // The existence check is necessary because it is possible to apply multiple tags with the same tagName to a file.
761  File localFile = new File(localFilePath.toString());
762  if (!localFile.exists()) {
763  ExtractFscContentVisitor.extract(file, localFile, null, null);
764  }
765 
766  // get the relative path
767  return localFilePath.toString().substring(path.length());
768  }
769 
775  @Override
776  public String dateToString(long date) {
777  SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
778  return sdf.format(new java.util.Date(date * 1000));
779  }
780 
781 
782  @Override
783  public String getRelativeFilePath() {
784  return "HTML Report" + File.separator + "index.html"; //NON-NLS
785  }
786 
787 
788  @Override
789  public String getName() {
790  return NbBundle.getMessage(this.getClass(), "ReportHTML.getName.text");
791  }
792 
793  @Override
794  public String getDescription() {
795  return NbBundle.getMessage(this.getClass(), "ReportHTML.getDesc.text");
796  }
797 
801  private void writeCss() {
802  Writer cssOut = null;
803  try {
804  cssOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path + "index.css"), "UTF-8")); //NON-NLS NON-NLS
805  String css = "body {margin: 0px; padding: 0px; background: #FFFFFF; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353;}\n" + //NON-NLS
806  "#content {padding: 30px;}\n" + //NON-NLS
807  "#header {width:100%; padding: 10px; line-height: 25px; background: #07A; color: #FFF; font-size: 20px;}\n" + //NON-NLS
808  "h1 {font-size: 20px; font-weight: normal; color: #07A; padding: 0 0 7px 0; margin-top: 25px; border-bottom: 1px solid #D6D6D6;}\n" + //NON-NLS
809  "h2 {font-size: 20px; font-weight: bolder; color: #07A;}\n" + //NON-NLS
810  "h3 {font-size: 16px; color: #07A;}\n" + //NON-NLS
811  "h4 {background: #07A; color: #FFF; font-size: 16px; margin: 0 0 0 25px; padding: 0; padding-left: 15px;}\n" + //NON-NLS
812  "ul.nav {list-style-type: none; line-height: 35px; padding: 0px; margin-left: 15px;}\n" + //NON-NLS
813  "ul li a {font-size: 14px; color: #444; text-decoration: none; padding-left: 25px;}\n" + //NON-NLS
814  "ul li a:hover {text-decoration: underline;}\n" + //NON-NLS
815  "p {margin: 0 0 20px 0;}\n" + //NON-NLS
816  "table {white-space:nowrap; min-width: 700px; padding: 2; margin: 0; border-collapse: collapse; border-bottom: 2px solid #e5e5e5;}\n" + //NON-NLS
817  ".keyword_list table {margin: 0 0 25px 25px; border-bottom: 2px solid #dedede;}\n" + //NON-NLS
818  "table th {white-space:nowrap; display: table-cell; text-align: center; padding: 2px 4px; background: #e5e5e5; color: #777; font-size: 11px; text-shadow: #e9f9fd 0 1px 0; border-top: 1px solid #dedede; border-bottom: 2px solid #e5e5e5;}\n" + //NON-NLS
819  "table .left_align_cell{display: table-cell; padding: 2px 4px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align: left; }\n" + //NON-NLS
820  "table .right_align_cell{display: table-cell; padding: 2px 4px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align: right; }\n" + //NON-NLS
821  "table td {white-space:nowrap; display: table-cell; padding: 2px 3px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align:left; }\n" + //NON-NLS
822  "table tr:nth-child(even) td {background: #f3f3f3;}"; //NON-NLS
823  cssOut.write(css);
824  } catch (FileNotFoundException ex) {
825  logger.log(Level.SEVERE, "Could not find index.css file to write to.", ex); //NON-NLS
826  } catch (UnsupportedEncodingException ex) {
827  logger.log(Level.SEVERE, "Did not recognize encoding when writing index.css.", ex); //NON-NLS
828  } catch (IOException ex) {
829  logger.log(Level.SEVERE, "Error creating Writer for index.css.", ex); //NON-NLS
830  } finally {
831  try {
832  if(cssOut != null) {
833  cssOut.flush();
834  cssOut.close();
835  }
836  } catch (IOException ex) {
837  }
838  }
839  }
840 
844  private void writeIndex() {
845  Writer indexOut = null;
846  String indexFilePath = path + "index.html"; //NON-NLS
847  try {
848  indexOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(indexFilePath), "UTF-8")); //NON-NLS
849  StringBuilder index = new StringBuilder();
850  index.append("<head>\n<title>").append( //NON-NLS
851  NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.title", currentCase.getName())).append(
852  "</title>\n"); //NON-NLS
853  index.append("<link rel=\"icon\" type=\"image/ico\" href=\"favicon.ico\" />\n"); //NON-NLS
854  index.append("</head>\n"); //NON-NLS
855  index.append("<frameset cols=\"350px,*\">\n"); //NON-NLS
856  index.append("<frame src=\"nav.html\" name=\"nav\">\n"); //NON-NLS
857  index.append("<frame src=\"summary.html\" name=\"content\">\n"); //NON-NLS
858  index.append("<noframes>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.noFrames.msg")).append("<br />\n"); //NON-NLS
859  index.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.noFrames.seeNav")).append("<br />\n"); //NON-NLS
860  index.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.seeSum")).append("</noframes>\n"); //NON-NLS
861  index.append("</frameset>\n"); //NON-NLS
862  index.append("</html>"); //NON-NLS
863  indexOut.write(index.toString());
864  Case.getCurrentCase().addReport(indexFilePath, NbBundle.getMessage(this.getClass(),
865  "ReportHTML.writeIndex.srcModuleName.text"), "");
866  } catch (IOException ex) {
867  logger.log(Level.SEVERE, "Error creating Writer for index.html: {0}", ex); //NON-NLS
868  } catch (TskCoreException ex) {
869  String errorMessage = String.format("Error adding %s to case as a report", indexFilePath); //NON-NLS
870  logger.log(Level.SEVERE, errorMessage, ex);
871  } finally {
872  try {
873  if(indexOut != null) {
874  indexOut.flush();
875  indexOut.close();
876  }
877  } catch (IOException ex) {
878  }
879  }
880  }
881 
885  private void writeNav() {
886  Writer navOut = null;
887  try {
888  navOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path + "nav.html"), "UTF-8")); //NON-NLS
889  StringBuilder nav = new StringBuilder();
890  nav.append("<html>\n<head>\n\t<title>").append( //NON-NLS
891  NbBundle.getMessage(this.getClass(), "ReportHTML.writeNav.title"))
892  .append("</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n</head>\n<body>\n"); //NON-NLS
893  nav.append("<div id=\"content\">\n<h1>").append( //NON-NLS
894  NbBundle.getMessage(this.getClass(), "ReportHTML.writeNav.h1")).append("</h1>\n"); //NON-NLS
895  nav.append("<ul class=\"nav\">\n"); //NON-NLS
896  nav.append("<li style=\"background: url(summary.png) left center no-repeat;\"><a href=\"summary.html\" target=\"content\">") //NON-NLS
897  .append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeNav.summary")).append("</a></li>\n"); //NON-NLS
898 
899  for (String dataType : dataTypes.keySet()) {
900  String dataTypeEsc = dataTypeToFileName(dataType);
901  String iconFileName = useDataTypeIcon(dataType);
902  nav.append("<li style=\"background: url('").append(iconFileName) //NON-NLS
903  .append("') left center no-repeat;\"><a href=\"") //NON-NLS
904  .append(dataTypeEsc).append(".html\" target=\"content\">") //NON-NLS
905  .append(dataType).append(" (").append(dataTypes.get(dataType))
906  .append(")</a></li>\n"); //NON-NLS
907  }
908  nav.append("</ul>\n"); //NON-NLS
909  nav.append("</div>\n</body>\n</html>"); //NON-NLS
910  navOut.write(nav.toString());
911  } catch (IOException ex) {
912  logger.log(Level.SEVERE, "Failed to write end of report navigation menu: {0}", ex); //NON-NLS
913  } finally {
914  if (navOut != null) {
915  try {
916  navOut.flush();
917  navOut.close();
918  } catch (IOException ex) {
919  logger.log(Level.WARNING, "Could not close navigation out writer."); //NON-NLS
920  }
921  }
922  }
923 
924  InputStream in = null;
925  OutputStream output = null;
926  try {
927 
928  //pull generator and agency logo from branding, and the remaining resources from the core jar
929  String generatorLogoPath = reportBranding.getGeneratorLogoPath();
930  if (generatorLogoPath != null && ! generatorLogoPath.isEmpty()) {
931  File from = new File(generatorLogoPath);
932  File to = new File(path);
933  FileUtil.copyFile(FileUtil.toFileObject(from), FileUtil.toFileObject(to), "generator_logo"); //NON-NLS
934  }
935 
936  String agencyLogoPath = reportBranding.getAgencyLogoPath();
937  if (agencyLogoPath != null && ! agencyLogoPath.isEmpty() ) {
938  File from = new File(agencyLogoPath);
939  File to = new File(path);
940  FileUtil.copyFile(FileUtil.toFileObject(from), FileUtil.toFileObject(to), "agency_logo"); //NON-NLS
941  }
942 
943  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/favicon.ico"); //NON-NLS
944  output = new FileOutputStream(new File(path + File.separator + "favicon.ico"));
945  FileUtil.copy(in, output);
946  in.close();
947  output.close();
948 
949  in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/summary.png"); //NON-NLS
950  output = new FileOutputStream(new File(path + File.separator + "summary.png"));
951  FileUtil.copy(in, output);
952  in.close();
953  output.close();
954 
955 
956  } catch (IOException ex) {
957  logger.log(Level.SEVERE, "Failed to extract images for HTML report.", ex); //NON-NLS
958  } finally {
959  if (output != null) {
960  try {
961  output.flush();
962  output.close();
963  } catch (IOException ex) {
964  }
965  } if (in != null) {
966  try {
967  in.close();
968  } catch (IOException ex) {
969  }
970  }
971  }
972  }
973 
977  private void writeSummary() {
978  Writer out = null;
979  try {
980  out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path + "summary.html"), "UTF-8")); //NON-NLS
981  StringBuilder head = new StringBuilder();
982  head.append("<html>\n<head>\n<title>").append( //NON-NLS
983  NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.title")).append("</title>\n"); //NON-NLS
984  head.append("<style type=\"text/css\">\n"); //NON-NLS
985  head.append("body { padding: 0px; margin: 0px; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353; }\n"); //NON-NLS
986  head.append("#wrapper { width: 90%; margin: 0px auto; margin-top: 35px; }\n"); //NON-NLS
987  head.append("h1 { color: #07A; font-size: 36px; line-height: 42px; font-weight: normal; margin: 0px; border-bottom: 1px solid #81B9DB; }\n"); //NON-NLS
988  head.append("h1 span { color: #F00; display: block; font-size: 16px; font-weight: bold; line-height: 22px;}\n"); //NON-NLS
989  head.append("h2 { padding: 0 0 3px 0; margin: 0px; color: #07A; font-weight: normal; border-bottom: 1px dotted #81B9DB; }\n"); //NON-NLS
990  head.append("table td { padding-right: 25px; }\n"); //NON-NLS
991  head.append("p.subheadding { padding: 0px; margin: 0px; font-size: 11px; color: #B5B5B5; }\n"); //NON-NLS
992  head.append(".title { width: 660px; margin-bottom: 50px; }\n"); //NON-NLS
993  head.append(".left { float: left; width: 250px; margin-top: 20px; text-align: center; }\n"); //NON-NLS
994  head.append(".left img { max-width: 250px; max-height: 250px; min-width: 200px; min-height: 200px; }\n"); //NON-NLS
995  head.append(".right { float: right; width: 385px; margin-top: 25px; font-size: 14px; }\n"); //NON-NLS
996  head.append(".clear { clear: both; }\n"); //NON-NLS
997  head.append(".info p { padding: 3px 10px; background: #e5e5e5; color: #777; font-size: 12px; font-weight: bold; text-shadow: #e9f9fd 0 1px 0; border-top: 1px solid #dedede; border-bottom: 2px solid #dedede; }\n"); //NON-NLS
998  head.append(".info table { margin: 0 25px 20px 25px; }\n"); //NON-NLS
999  head.append("</style>\n"); //NON-NLS
1000  head.append("</head>\n<body>\n"); //NON-NLS
1001  out.write(head.toString());
1002 
1003  DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
1004  Date date = new Date();
1005  String datetime = datetimeFormat.format(date);
1006 
1007  String caseName = currentCase.getName();
1008  String caseNumber = currentCase.getNumber();
1009  String examiner = currentCase.getExaminer();
1010  int imagecount;
1011  try {
1012  imagecount = currentCase.getDataSources().size();
1013  } catch (TskCoreException ex) {
1014  imagecount = 0;
1015  }
1016 
1017  StringBuilder summary = new StringBuilder();
1018  boolean running = false;
1019  if (IngestManager.getInstance().isIngestRunning()) {
1020  running = true;
1021  }
1022 
1023  final String reportTitle = reportBranding.getReportTitle();
1024  final String reportFooter = reportBranding.getReportFooter();
1025  final boolean agencyLogoSet = reportBranding.getAgencyLogoPath() != null && !reportBranding.getAgencyLogoPath().isEmpty();
1026  final boolean generatorLogoSet = reportBranding.getGeneratorLogoPath() != null && !reportBranding.getGeneratorLogoPath().isEmpty();
1027 
1028  summary.append("<div id=\"wrapper\">\n"); //NON-NLS
1029  summary.append("<h1>").append(reportTitle) //NON-NLS
1030  .append(running ? NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.warningMsg") : "")
1031  .append("</h1>\n"); //NON-NLS
1032  summary.append("<p class=\"subheadding\">").append( //NON-NLS
1033  NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.reportGenOn.text", datetime)).append("</p>\n"); //NON-NLS
1034  summary.append("<div class=\"title\">\n"); //NON-NLS
1035  if (agencyLogoSet) {
1036  summary.append("<div class=\"left\">\n"); //NON-NLS
1037  summary.append("<img src=\"agency_logo.png\" />\n"); //NON-NLS
1038  summary.append("</div>\n"); //NON-NLS
1039  }
1040  final String align = agencyLogoSet?"right":"left"; //NON-NLS NON-NLS
1041  summary.append("<div class=\"").append(align).append("\">\n"); //NON-NLS
1042  summary.append("<table>\n"); //NON-NLS
1043  summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.caseName")) //NON-NLS
1044  .append("</td><td>").append(caseName).append("</td></tr>\n"); //NON-NLS NON-NLS
1045  summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.caseNum")) //NON-NLS
1046  .append("</td><td>").append(!caseNumber.isEmpty() ? caseNumber : NbBundle //NON-NLS
1047  .getMessage(this.getClass(), "ReportHTML.writeSum.noCaseNum")).append("</td></tr>\n"); //NON-NLS
1048  summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.examiner")).append("</td><td>") //NON-NLS
1049  .append(!examiner.isEmpty() ? examiner : NbBundle
1050  .getMessage(this.getClass(), "ReportHTML.writeSum.noExaminer"))
1051  .append("</td></tr>\n"); //NON-NLS
1052  summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.numImages")) //NON-NLS
1053  .append("</td><td>").append(imagecount).append("</td></tr>\n"); //NON-NLS
1054  summary.append("</table>\n"); //NON-NLS
1055  summary.append("</div>\n"); //NON-NLS
1056  summary.append("<div class=\"clear\"></div>\n"); //NON-NLS
1057  summary.append("</div>\n"); //NON-NLS
1058  summary.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.imageInfoHeading"));
1059  summary.append("<div class=\"info\">\n"); //NON-NLS
1060  try {
1061  for (Content c : currentCase.getDataSources()) {
1062  summary.append("<p>").append(c.getName()).append("</p>\n"); //NON-NLS
1063  if (c instanceof Image) {
1064  Image img = (Image) c;
1065 
1066  summary.append("<table>\n"); //NON-NLS
1067  summary.append("<tr><td>").append( //NON-NLS
1068  NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.timezone"))
1069  .append("</td><td>").append(img.getTimeZone()).append("</td></tr>\n"); //NON-NLS
1070  for(String imgPath : img.getPaths()) {
1071  summary.append("<tr><td>").append( //NON-NLS
1072  NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.path"))
1073  .append("</td><td>").append(imgPath).append("</td></tr>\n"); //NON-NLS
1074  }
1075  summary.append("</table>\n"); //NON-NLS
1076  }
1077  }
1078  } catch (TskCoreException ex) {
1079  logger.log(Level.WARNING, "Unable to get image information for the HTML report."); //NON-NLS
1080  }
1081  summary.append("</div>\n"); //NON-NLS
1082  if (generatorLogoSet) {
1083  summary.append("<div class=\"left\">\n"); //NON-NLS
1084  summary.append("<img src=\"generator_logo.png\" />\n"); //NON-NLS
1085  summary.append("</div>\n"); //NON-NLS
1086  }
1087  summary.append("<div class=\"clear\"></div>\n"); //NON-NLS
1088  if (reportFooter != null) {
1089  summary.append("<p class=\"subheadding\">").append(reportFooter).append("</p>\n"); //NON-NLS
1090  }
1091  summary.append("</div>\n"); //NON-NLS
1092  summary.append("</body></html>"); //NON-NLS
1093  out.write(summary.toString());
1094  } catch (FileNotFoundException ex) {
1095  logger.log(Level.SEVERE, "Could not find summary.html file to write to."); //NON-NLS
1096  } catch (UnsupportedEncodingException ex) {
1097  logger.log(Level.SEVERE, "Did not recognize encoding when writing summary.hmtl."); //NON-NLS
1098  } catch (IOException ex) {
1099  logger.log(Level.SEVERE, "Error creating Writer for summary.html."); //NON-NLS
1100  } finally {
1101  try {
1102  if(out != null) {
1103  out.flush();
1104  out.close();
1105  }
1106  } catch (IOException ex) {
1107  }
1108  }
1109  }
1110 
1111  private String prepareThumbnail(AbstractFile file) {
1112  File thumbFile = ImageUtils.getIconFile(file, ImageUtils.ICON_SIZE_MEDIUM);
1113  if (thumbFile.exists() == false) {
1114  return null;
1115  }
1116  try {
1117  File to = new File(thumbsPath);
1118  FileObject from = FileUtil.toFileObject(thumbFile);
1119  FileObject dest = FileUtil.toFileObject(to);
1120  FileUtil.copyFile(from, dest, thumbFile.getName(), "");
1121  } catch (IOException ex) {
1122  logger.log(Level.SEVERE, "Failed to write thumb file to report directory.", ex); //NON-NLS
1123  }
1124 
1125  return THUMBS_REL_PATH + thumbFile.getName();
1126  }
1127 
1128 }
static String escapeFileName(String fileName)
Definition: FileUtil.java:155

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