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

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