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

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