19 package org.sleuthkit.autopsy.modules.pictureanalyzer.impls;
 
   21 import java.util.HashSet;
 
   23 import java.util.logging.Level;
 
   24 import java.util.Arrays;
 
   25 import java.util.concurrent.TimeUnit;
 
   27 import java.io.BufferedInputStream;
 
   29 import java.io.IOException;
 
   31 import java.nio.file.DirectoryIteratorException;
 
   32 import java.nio.file.DirectoryStream;
 
   33 import java.nio.file.Files;
 
   34 import java.nio.file.Path;
 
   35 import java.nio.file.Paths;
 
   36 import java.nio.file.StandardCopyOption;
 
   37 import java.nio.file.attribute.BasicFileAttributes;
 
   39 import org.apache.commons.io.FilenameUtils;
 
   41 import org.openide.modules.InstalledFileLocator;
 
   42 import org.openide.util.lookup.ServiceProvider;
 
   68 @ServiceProvider(service = PictureProcessor.class)
 
   73     private static final int EXIT_SUCCESS = 0;
 
   74     private static final String HEIC_MODULE_FOLDER = 
"HEIC";
 
   75     private static final long TIMEOUT_IN_SEC = TimeUnit.SECONDS.convert(2, TimeUnit.MINUTES);
 
   78     private static final String IMAGE_MAGICK_FOLDER = 
"ImageMagick-7.0.10-27-portable-Q16-x64";
 
   79     private static final String IMAGE_MAGICK_EXE = 
"magick.exe";
 
   80     private static final String IMAGE_MAGICK_ERROR_FILE = 
"magick_error.txt";
 
   86         IMAGE_MAGICK_PATH = findImageMagick();
 
   88         if (IMAGE_MAGICK_PATH == null) {
 
   89             logger.log(Level.WARNING, 
"ImageMagick executable not found. " 
   90                     + 
"HEIC functionality will be automatically disabled.");
 
   95         final Path windowsLocation = Paths.get(IMAGE_MAGICK_FOLDER, IMAGE_MAGICK_EXE);
 
   96         final Path macAndLinuxLocation = Paths.get(
"/usr", 
"local", 
"bin", 
"magick");
 
  101             final File locatedExec = InstalledFileLocator.getDefault().locate(
 
  102                 windowsLocation.toString(), 
HEICProcessor.class.getPackage().getName(), 
false);
 
  104             return (locatedExec != null) ? locatedExec.toPath() : null;        
 
  105         } 
else if ((osName.equals(
"linux") || osName.startsWith(
"mac")) && 
 
  106                     Files.isExecutable(macAndLinuxLocation) && 
 
  107                     !Files.isDirectory(macAndLinuxLocation)) {
 
  108             return macAndLinuxLocation;      
 
  121         return Paths.get(moduleOutputDirectory,
 
  123                 String.valueOf(file.getId()));
 
  130         final Path moduleOutputFolder = getModuleOutputFolder(file);
 
  132         if (!Files.exists(moduleOutputFolder)) {
 
  133             Files.createDirectories(moduleOutputFolder);
 
  140             if (IMAGE_MAGICK_PATH == null) {
 
  143             createModuleOutputFolder(file);
 
  149             final Path localDiskCopy = extractToDisk(file);
 
  151             convertToJPEG(context, localDiskCopy, file);
 
  152         } 
catch (IOException ex) {
 
  153             logger.log(Level.WARNING, 
"I/O error encountered during HEIC photo processing.", ex);
 
  154         } 
catch (TskCoreException ex) {
 
  155             logger.log(Level.SEVERE, 
"Unable to add pictures as derived files.", ex);
 
  157             logger.log(Level.WARNING, 
"No open case!", ex);
 
  168         final Path localDiskCopy = Paths.get(tempDir, heicFileName);
 
  170         try (BufferedInputStream heicInputStream = 
new BufferedInputStream(
new ReadContentInputStream(heicFile))) {
 
  171             Files.copy(heicInputStream, localDiskCopy, StandardCopyOption.REPLACE_EXISTING);
 
  172             return localDiskCopy;
 
  180         final Path moduleOutputFolder = getModuleOutputFolder(heicFile);
 
  183         final Path outputFile = moduleOutputFolder.resolve(baseFileName + 
".jpg");
 
  185         final Path imageMagickErrorOutput = moduleOutputFolder.resolve(IMAGE_MAGICK_ERROR_FILE);
 
  186         Files.deleteIfExists(imageMagickErrorOutput);
 
  187         Files.createFile(imageMagickErrorOutput);
 
  192         final ProcessBuilder processBuilder = 
new ProcessBuilder()
 
  193                 .command(IMAGE_MAGICK_PATH.toString(),
 
  194                         localDiskCopy.toString(),
 
  195                         outputFile.toString());
 
  197         processBuilder.redirectError(imageMagickErrorOutput.toFile());
 
  201         if (context.fileIngestIsCancelled()) {
 
  205         if (exitStatus != EXIT_SUCCESS) {
 
  206             logger.log(Level.INFO, 
"Non-zero exit status for HEIC file [id: {0}]. Skipping...", heicFile.getId());
 
  212         final String glob = String.format(
"{%1$s.jpg,%1$s-*.jpg}", baseFileName);
 
  213         try (DirectoryStream<Path> stream = Files.newDirectoryStream(moduleOutputFolder, glob)) {
 
  216             for (Path candidate : stream) {
 
  217                 if (context.fileIngestIsCancelled()) {
 
  221                 final BasicFileAttributes attrs = Files.readAttributes(candidate, BasicFileAttributes.class);
 
  222                 final Path localCasePath = caseDirectory.relativize(candidate);
 
  225                         .addDerivedFile(candidate.getFileName().toString(),
 
  226                                 localCasePath.toString(), attrs.size(), 0L,
 
  227                                 attrs.creationTime().to(TimeUnit.SECONDS),
 
  228                                 attrs.lastAccessTime().to(TimeUnit.SECONDS),
 
  229                                 attrs.lastModifiedTime().to(TimeUnit.SECONDS),
 
  230                                 attrs.isRegularFile(), heicFile, 
"",
 
  231                                 "", 
"", 
"", TskData.EncodingType.NONE);
 
  233                 context.addFilesToJob(Arrays.asList(jpegFile));
 
  237         } 
catch (DirectoryIteratorException ex) {
 
  244         return new HashSet<String>() {
 
void createModuleOutputFolder(AbstractFile file)
 
static int execute(ProcessBuilder processBuilder)
 
String getTempDirectory()
 
Path getModuleOutputFolder(AbstractFile file)
 
String getCaseDirectory()
 
Path extractToDisk(AbstractFile heicFile)
 
Set< String > mimeTypes()
 
final Path IMAGE_MAGICK_PATH
 
SleuthkitCase getSleuthkitCase()
 
String getModuleDirectory()
 
boolean fileIngestIsCancelled()
 
void fireModuleContentEvent(ModuleContentEvent moduleContentEvent)
 
void convertToJPEG(IngestJobContext context, Path localDiskCopy, AbstractFile heicFile)
 
static String escapeFileName(String fileName)
 
synchronized static Logger getLogger(String name)
 
static Case getCurrentCaseThrows()
 
void process(IngestJobContext context, AbstractFile file)
 
static synchronized IngestServices getInstance()