Autopsy  4.21.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
HEICProcessor.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2020 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.modules.pictureanalyzer.impls;
20 
21 import java.util.HashSet;
22 import java.util.Set;
23 import java.util.logging.Level;
24 import java.util.Arrays;
25 import java.util.concurrent.TimeUnit;
26 
27 import java.io.File;
28 import java.io.IOException;
29 import java.io.InputStream;
30 
31 import java.nio.file.Files;
32 import java.nio.file.Path;
33 import java.nio.file.Paths;
34 import java.nio.file.attribute.BasicFileAttributes;
35 import java.text.MessageFormat;
36 import java.util.List;
37 import org.apache.commons.io.FileUtils;
38 
39 import org.apache.commons.io.FilenameUtils;
40 
41 import org.openide.util.lookup.ServiceProvider;
42 
51 
57 
64 @ServiceProvider(service = PictureProcessor.class)
65 public class HEICProcessor implements PictureProcessor {
66 
67  private static final Logger logger = Logger.getLogger(HEICProcessor.class.getName());
68 
69  private static final String HEIC_MODULE_FOLDER = "HEIC";
70  private final HeifJNI heifJNI;
71 
72  public HEICProcessor() {
73  HeifJNI heifJNI;
74  try {
75  heifJNI = HeifJNI.getInstance();
76  } catch (UnsatisfiedLinkError ex) {
77  logger.log(Level.SEVERE, "libheif native dependencies not found. HEIC functionality will be automatically disabled.", ex);
78  heifJNI = null;
79  }
80  this.heifJNI = heifJNI;
81  }
82 
83  @Override
84  public void process(IngestJobContext context, AbstractFile file) {
85  try {
86  if (heifJNI == null) {
87  return;
88  }
89 
90  if (context.fileIngestIsCancelled()) {
91  return;
92  }
93 
94  if (file == null || file.getId() <= 0) {
95  return;
96  }
97 
98  byte[] heifBytes;
99  try (InputStream is = new ReadContentInputStream(file)) {
100  heifBytes = new byte[is.available()];
101  is.read(heifBytes);
102  }
103 
104  if (heifBytes == null || heifBytes.length == 0) {
105  return;
106  }
107 
108  convertToJPEG(context, heifBytes, file);
109  } catch (IOException ex) {
110  logger.log(Level.WARNING, "I/O error encountered during HEIC photo processing.", ex);
111  } catch (TskCoreException ex) {
112  logger.log(Level.SEVERE, "Unable to add pictures as derived files.", ex);
113  } catch (NoCurrentCaseException ex) {
114  logger.log(Level.WARNING, "No open case!", ex);
115  }
116  }
117 
125  private Path createModuleOutputFolder(AbstractFile file) throws IOException, NoCurrentCaseException {
126  final String moduleOutputDirectory = Case.getCurrentCaseThrows().getModuleDirectory();
127 
128  Path moduleOutputFolder = Paths.get(moduleOutputDirectory,
129  HEIC_MODULE_FOLDER,
130  String.valueOf(file.getId()));
131 
132  if (!Files.exists(moduleOutputFolder)) {
133  Files.createDirectories(moduleOutputFolder);
134  }
135 
136  return moduleOutputFolder;
137  }
138 
139  private void convertToJPEG(IngestJobContext context, byte[] heifBytes,
140  AbstractFile heicFile) throws IOException, TskCoreException, NoCurrentCaseException {
141 
142  Path outputFolder = createModuleOutputFolder(heicFile);
143 
144  final String baseFileName = FilenameUtils.getBaseName(FileUtil.escapeFileName(heicFile.getName()));
145  final Path outputFile = outputFolder.resolve(baseFileName + ".jpg");
146 
147  if (context.fileIngestIsCancelled()) {
148  return;
149  }
150 
151  try {
152  this.heifJNI.convertToDisk(heifBytes, outputFile.toString());
153  } catch (IllegalArgumentException | IllegalStateException ex) {
154  logger.log(Level.WARNING, MessageFormat.format("There was an error processing {0} (id: {1}).", heicFile.getName(), heicFile.getId()), ex);
155  return;
156  } catch (Throwable ex) {
157  logger.log(Level.SEVERE, MessageFormat.format("A severe error occurred while processing {0} (id: {1}).", heicFile.getName(), heicFile.getId()), ex);
158  return;
159  }
160 
161  if (context.fileIngestIsCancelled()) {
162  return;
163  }
164 
165  final Path caseDirectory = Paths.get(Case.getCurrentCaseThrows().getCaseDirectory());
166 
167  List<File> files = (List<File>) FileUtils.listFiles(outputFolder.toFile(), new String[]{"jpg", "jpeg"}, true);
168  for (File file : files) {
169  if (context.fileIngestIsCancelled()) {
170  return;
171  }
172 
173  Path candidate = file.toPath();
174 
175  final BasicFileAttributes attrs = Files.readAttributes(candidate, BasicFileAttributes.class);
176  final Path localCasePath = caseDirectory.relativize(candidate);
177 
179  .addDerivedFile(candidate.getFileName().toString(),
180  localCasePath.toString(), attrs.size(), 0L,
181  attrs.creationTime().to(TimeUnit.SECONDS),
182  attrs.lastAccessTime().to(TimeUnit.SECONDS),
183  attrs.lastModifiedTime().to(TimeUnit.SECONDS),
184  attrs.isRegularFile(), heicFile, "",
185  "", "", "", TskData.EncodingType.NONE);
186 
187  context.addFilesToJob(Arrays.asList(jpegFile));
189  }
190  }
191 
192  @Override
193  public Set<String> mimeTypes() {
194  return new HashSet<String>() {
195  {
196  add("image/heif");
197  add("image/heic");
198  }
199  };
200  }
201 }
DerivedFile addDerivedFile(String fileName, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, Content parentObj, String rederiveDetails, String toolName, String toolVersion, String otherDetails, TskData.EncodingType encodingType)
void convertToJPEG(IngestJobContext context, byte[] heifBytes, AbstractFile heicFile)
void fireModuleContentEvent(ModuleContentEvent moduleContentEvent)
native int convertToDisk(byte[] data, String jpgOutputPath)
static String escapeFileName(String fileName)
Definition: FileUtil.java:169
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void process(IngestJobContext context, AbstractFile file)
static synchronized IngestServices getInstance()

Copyright © 2012-2024 Sleuth Kit Labs. Generated on: Mon Feb 17 2025
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.