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

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