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

Copyright © 2012-2016 Basis Technology. Generated on: Fri Sep 29 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.