diff --git a/Core/src/org/sleuthkit/autopsy/core/Installer.java b/Core/src/org/sleuthkit/autopsy/core/Installer.java index 55d119ec78..0d61453cd4 100644 --- a/Core/src/org/sleuthkit/autopsy/core/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/core/Installer.java @@ -18,11 +18,11 @@ */ package org.sleuthkit.autopsy.core; -import com.sun.javafx.application.PlatformImpl; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import javafx.application.Platform; +import javafx.embed.swing.JFXPanel; import org.sleuthkit.autopsy.coreutils.Logger; import org.openide.modules.ModuleInstall; import org.openide.windows.WindowManager; @@ -37,10 +37,11 @@ public class Installer extends ModuleInstall { private List packageInstallers; private static final Logger logger = Logger.getLogger(Installer.class.getName()); - private volatile boolean javaFxInit = true; + private static volatile boolean javaFxInit = false; public Installer() { - javaFxInit = true; + logger.log(Level.INFO, "core installer created"); + javaFxInit = false; packageInstallers = new ArrayList(); packageInstallers.add(org.sleuthkit.autopsy.coreutils.Installer.getDefault()); @@ -54,23 +55,20 @@ public class Installer extends ModuleInstall { * Check if JavaFx initialized * @return false if java fx not initialized (classes coult not load), true if initialized */ - public boolean isJavaFxInited() { - return this.javaFxInit; + public static boolean isJavaFxInited() { + return javaFxInit; } - private void initJavaFx() { + private static void initJavaFx() { //initialize java fx if exists + System.setProperty("javafx.macosx.embedded", "true"); try { + // Creating a JFXPanel initializes JavaFX + new JFXPanel(); Platform.setImplicitExit(false); - PlatformImpl.startup(new Runnable() { - @Override - public void run() { - logger.log(Level.INFO, "Initializing JavaFX for image viewing"); - } - }); + javaFxInit = true; } catch (UnsatisfiedLinkError | NoClassDefFoundError | Exception e) { //in case javafx not present - javaFxInit = false; final String msg = "Error initializing JavaFX. "; final String details = " Some features will not be available. " + " Check that you have the right JRE installed (Oracle JRE > 1.7.10). "; diff --git a/Core/src/org/sleuthkit/autopsy/core/layer.xml b/Core/src/org/sleuthkit/autopsy/core/layer.xml index f72ce99565..3b5b8b481e 100644 --- a/Core/src/org/sleuthkit/autopsy/core/layer.xml +++ b/Core/src/org/sleuthkit/autopsy/core/layer.xml @@ -361,6 +361,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/CoreComponentControl.java b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/CoreComponentControl.java index 7c7b7fd182..2ada97d001 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/CoreComponentControl.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/CoreComponentControl.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.corecomponentinterfaces; import java.util.Collection; import java.util.Iterator; +import java.util.Set; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import org.openide.util.Lookup; @@ -71,17 +72,47 @@ public class CoreComponentControl { /** * Closes all TopComponent windows that needed ({@link DataExplorer}, {@link DataResult}, and - * {@link DataContent}) + * {@link DataContent}). + * + * Note: The DataContent Top Component must be closed before the Directory Tree + * and Favorites Top Components. Otherwise a NullPointerException will be thrown + * from JFXPanel. */ public static void closeCoreWindows() { WindowManager wm = WindowManager.getDefault(); + Set modes = wm.getModes(); Iterator iter = wm.getModes().iterator(); + TopComponent directoryTree = null; + TopComponent favorites = null; + String tcName = ""; while (iter.hasNext()) { Mode mode = iter.next(); for (TopComponent tc : mode.getTopComponents()) { - tc.close(); + tcName = tc.getName(); + if (tcName == null) { + logger.log(Level.INFO, "tcName was null"); + tcName = ""; + } + switch (tcName) { + case "Directory Tree": + directoryTree = tc; + break; + case "Favorites": + favorites = tc; + break; + default: + tc.close(); + break; + } } } + + if (directoryTree != null) { + directoryTree.close(); + } + if (favorites != null) { + favorites.close(); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java index a825751e4c..d38b2e900c 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java @@ -189,8 +189,7 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent, // let the user decide if we should stay with the current viewer int tabIndex = keepCurrentViewer ? currTabIndex : preferredViewerIndex; - // set the tab to the one the user wants, then set that viewer's node. - jTabbedPane1.setSelectedIndex(tabIndex); + UpdateWrapper dcv = viewers.get(tabIndex); // this is really only needed if no tabs were enabled if (jTabbedPane1.isEnabledAt(tabIndex) == false) { @@ -199,6 +198,9 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent, else { dcv.setNode(selectedNode); } + + // set the tab to the one the user wants, then set that viewer's node. + jTabbedPane1.setSelectedIndex(tabIndex); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java index 544bc6b85b..ac9e09e8f3 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java @@ -128,19 +128,19 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo return; //prevent from loading twice if setNode() called mult. times } - resetComponent(); + lastFile = file; final Dimension dims = DataContentViewerMedia.this.getSize(); if (imagePanelInited && containsExt(file.getName(), IMAGES)) { imagePanel.showImageFx(file, dims); - this.switchPanels(false); + this.switchPanels(false); } else if (videoPanelInited && (containsExt(file.getName(), VIDEOS) || containsExt(file.getName(), AUDIOS))) { videoPanel.setupVideo(file, dims); switchPanels(true); } - lastFile = file; + } catch (Exception e) { logger.log(Level.SEVERE, "Exception while setting node", e); } @@ -182,8 +182,8 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo @Override public void resetComponent() { - videoPanel.reset(); - // @@@ Seems like we should also reset the image viewer... + // No need to reset the video panel. It resets itself when a node is + // set. lastFile = null; } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.form new file mode 100644 index 0000000000..5143a19b43 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.form @@ -0,0 +1,23 @@ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.java index 88e8cc3105..1fa8b72f5d 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.java @@ -18,14 +18,13 @@ */ package org.sleuthkit.autopsy.corecomponents; -import com.sun.javafx.application.PlatformImpl; -import java.awt.Component; import java.awt.Dimension; import java.io.IOException; import java.nio.file.Paths; import java.util.List; import java.util.concurrent.CancellationException; import java.util.logging.Level; +import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.value.ChangeListener; @@ -45,19 +44,23 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import javafx.scene.media.Media; +import javafx.scene.media.MediaBuilder; import javafx.scene.media.MediaException; import javafx.scene.media.MediaPlayer; import javafx.scene.media.MediaPlayer.Status; +import static javafx.scene.media.MediaPlayer.Status.PAUSED; +import static javafx.scene.media.MediaPlayer.Status.PLAYING; import static javafx.scene.media.MediaPlayer.Status.READY; +import static javafx.scene.media.MediaPlayer.Status.STOPPED; +import javafx.scene.media.MediaPlayerBuilder; import javafx.scene.media.MediaView; +import javafx.scene.text.Font; import javafx.util.Duration; -import javax.swing.BoxLayout; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; -import org.openide.modules.ModuleInstall; import org.openide.util.Cancellable; import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProviders; @@ -67,6 +70,7 @@ import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.autopsy.core.Installer; /** * Video viewer part of the Media View layered pane. @@ -79,7 +83,6 @@ public class FXVideoPanel extends MediaViewVideoPanel { private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName()); private boolean fxInited = false; // FX Components - private MediaPlayer fxMediaPlayer; private MediaPane mediaPane; // Current media content representations private AbstractFile currentFile; @@ -90,35 +93,53 @@ public class FXVideoPanel extends MediaViewVideoPanel { * Creates new form MediaViewVideoPanel */ public FXVideoPanel() { - org.sleuthkit.autopsy.core.Installer coreInstaller = - ModuleInstall.findObject(org.sleuthkit.autopsy.core.Installer.class, false); - if (coreInstaller != null) { - fxInited = coreInstaller.isJavaFxInited(); - } + fxInited = Installer.isJavaFxInited(); initComponents(); - customizeComponents(); + if (fxInited) { + setupFx(); + } } public JPanel getVideoPanel() { - return videoPanel; + return this; } - - public Component getVideoComponent() { - return videoComponent; - } - - private void customizeComponents() { - setupFx(); + + private void setupFx() { + Platform.runLater(new Runnable() { + @Override + public void run() { + videoComponent = new JFXPanel(); + mediaPane = new MediaPane(); + Scene fxScene = new Scene(mediaPane); + videoComponent.setScene(fxScene); + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + add(videoComponent); + } + }); + } + }); } @Override - synchronized void setupVideo(final AbstractFile file, final Dimension dims) { + void setupVideo(final AbstractFile file, final Dimension dims) { + if(file.equals(currentFile)) { + return; + } + if (!Case.isCaseOpen()) { + //handle in-between condition when case is being closed + //and an image was previously selected + return; + } + reset(); currentFile = file; final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC); if (deleted) { mediaPane.setInfoLabelText("Playback of deleted videos is not supported, use an external player."); - videoPanel.removeAll(); + removeAll(); return; } @@ -133,56 +154,22 @@ public class FXVideoPanel extends MediaViewVideoPanel { ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile)); em.execute(); + + mediaPane.setFit(dims); } - synchronized void setupFx() { - if(!fxInited) { - return; - } - logger.log(Level.INFO, "In Setup FX"); - PlatformImpl.runLater(new Runnable() { - @Override - public void run() { - mediaPane = new MediaPane(); - logger.log(Level.INFO, "Created MediaPane"); - Scene fxScene = new Scene(mediaPane); - videoComponent = new JFXPanel(); - videoComponent.setScene(fxScene); - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - // Configure VideoPanel - videoPanel.removeAll(); - videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS)); - videoPanel.add(videoComponent); - videoPanel.setVisible(true); - } - }); - } - }); - } @Override void reset() { - - PlatformImpl.runLater(new Runnable() { + Platform.runLater(new Runnable() { @Override public void run() { - if (fxMediaPlayer != null) { - if (fxMediaPlayer.getStatus() == MediaPlayer.Status.PLAYING ) { - fxMediaPlayer.stop(); - } - fxMediaPlayer = null; - } - - if (videoComponent != null) { - videoComponent = null; + if (mediaPane != null) { + mediaPane.reset(); } } - }); - + }); currentFile = null; } @@ -207,37 +194,14 @@ public class FXVideoPanel extends MediaViewVideoPanel { * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") - // + // //GEN-BEGIN:initComponents private void initComponents() { - videoPanel = new javax.swing.JPanel(); - - javax.swing.GroupLayout videoPanelLayout = new javax.swing.GroupLayout(videoPanel); - videoPanel.setLayout(videoPanelLayout); - videoPanelLayout.setHorizontalGroup( - videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 448, Short.MAX_VALUE) - ); - videoPanelLayout.setVerticalGroup( - videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 248, Short.MAX_VALUE) - ); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(videoPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - ); - }// - - // Variables declaration - do not modify - private javax.swing.JPanel videoPanel; - // End of variables declaration + setBackground(new java.awt.Color(0, 0, 0)); + setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.LINE_AXIS)); + }// //GEN-END:initComponents + // Variables declaration - do not modify//GEN-BEGIN:variables + // End of variables declaration//GEN-END:variables @Override public boolean isInited() { @@ -265,8 +229,13 @@ public class FXVideoPanel extends MediaViewVideoPanel { return extractedBytes; } - public Media getMedia() { - return new Media(Paths.get(jFile.getAbsolutePath()).toUri().toString()); + /** + * Get the URI of the media file. + * + * @return the URI of the media file. + */ + public String getMediaUri() { + return Paths.get(jFile.getAbsolutePath()).toUri().toString(); } @Override @@ -307,13 +276,10 @@ public class FXVideoPanel extends MediaViewVideoPanel { if (!this.isCancelled()) { logger.log(Level.INFO, "ExtractMedia in done: " + jFile.getName()); try { - PlatformImpl.runLater(new Runnable() { + Platform.runLater(new Runnable() { @Override public void run() { - fxMediaPlayer = new MediaPlayer(getMedia()); - logger.log(Level.INFO, "Fx Media Player null? " + (fxMediaPlayer == null)); - logger.log(Level.INFO, "Media Tools null? " + (mediaPane == null)); - mediaPane.setMediaPlayer(fxMediaPlayer); + mediaPane.prepareMedia(getMediaUri()); } }); } catch(MediaException e) { @@ -327,14 +293,27 @@ public class FXVideoPanel extends MediaViewVideoPanel { } } + /** + * The JavaFX Component that contains the Media and it's Controls. + * + */ private class MediaPane extends BorderPane { private MediaPlayer mediaPlayer; private MediaView mediaView; + /** The Duration of the media. **/ private Duration duration; + + /** The container for the media controls. **/ private HBox mediaTools; + + /** The container for the media video output. **/ private HBox mediaViewPane; + + private VBox controlPanel; + private Slider progressSlider; private Button pauseButton; + private Button stopButton; private Label progressLabel; private Label infoLabel; private int totalHours; @@ -342,6 +321,36 @@ public class FXVideoPanel extends MediaViewVideoPanel { private int totalSeconds; private String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d "; + /** The EventHandler for MediaPlayer.onReady(). **/ + private final ReadyListener READY_LISTENER = new ReadyListener(); + + /** The EventHandler for MediaPlayer.onEndOfMedia(). **/ + private final EndOfMediaListener END_LISTENER = new EndOfMediaListener(); + + /** The EventHandler for the CurrentTime property of the MediaPlayer. **/ + private final TimeListener TIME_LISTENER = new TimeListener(); + + /** The EventHandler for MediaPlayer.onPause and MediaPlayer.onStop. **/ + private final NotPlayListener NOT_PLAY_LISTENER = new NotPlayListener(); + + /** The EventHandler for MediaPlayer.onPlay. **/ + private final PlayListener PLAY_LISTENER = new PlayListener(); + + private static final String PLAY_TEXT = "►"; + + private static final String PAUSE_TEXT = "||"; + + private static final String STOP_TEXT = "X"; + + /** CSS-formatted skin for pauseButton when showing PLAY_TEXT. **/ + private static final String PLAY_STYLE = "-fx-text-fill: green;"; + + /** CSS-formatted skin for pauseButton when showing PAUSE_TEXT. **/ + private static final String PAUSE_STYLE = "-fx-font-weight: bolder;"; + + /** CSS-formatted skin for stopButton. **/ + private static final String STOP_STYLE = "-fx-text-fill: red; -fx-font-weight: bold;"; + public MediaPane() { // Video Display mediaViewPane = new HBox(); @@ -349,26 +358,30 @@ public class FXVideoPanel extends MediaViewVideoPanel { mediaViewPane.setAlignment(Pos.CENTER); mediaView = new MediaView(); mediaViewPane.getChildren().add(mediaView); - setAlignment(mediaViewPane, Pos.CENTER); setCenter(mediaViewPane); // Media Controls - VBox controlPanel = new VBox(); + controlPanel = new VBox(); mediaTools = new HBox(); mediaTools.setAlignment(Pos.CENTER); mediaTools.setPadding(new Insets(5, 10, 5, 10)); - pauseButton = new Button("►"); + pauseButton = new Button(PLAY_TEXT); + pauseButton.setStyle(PLAY_STYLE); + stopButton = new Button(STOP_TEXT); + stopButton.setStyle(STOP_STYLE); mediaTools.getChildren().add(pauseButton); - mediaTools.getChildren().add(new Label(" ")); + mediaTools.getChildren().add(new Label(" ")); + mediaTools.getChildren().add(stopButton); + mediaTools.getChildren().add(new Label(" ")); progressSlider = new Slider(); HBox.setHgrow(progressSlider,Priority.ALWAYS); progressSlider.setMinWidth(50); progressSlider.setMaxWidth(Double.MAX_VALUE); mediaTools.getChildren().add(progressSlider); progressLabel = new Label(); - progressLabel.setPrefWidth(130); - progressLabel.setMinWidth(50); + progressLabel.setPrefWidth(135); + progressLabel.setMinWidth(135); mediaTools.getChildren().add(progressLabel); controlPanel.getChildren().add(mediaTools); @@ -379,8 +392,39 @@ public class FXVideoPanel extends MediaViewVideoPanel { setProgressActionListeners(); } + /** + * Setup the MediaPane for media playback. Run on the JavaFx Thread. + * + * + * @param mediaUri the URI of the media + */ + public void prepareMedia(String mediaUri) { + mediaPlayer = createMediaPlayer(mediaUri); + mediaView.setMediaPlayer(mediaPlayer); + } + + /** + * Reset this MediaPane. + * + */ + public void reset() { + if (mediaPlayer != null) { + if (mediaPlayer.getStatus() == Status.PLAYING) { + mediaPlayer.stop(); + } + mediaPlayer = null; + } + resetProgress(); + } + + /** + * Set the Information Label of this MediaPane. + * + * @param text + */ public void setInfoLabelText(final String text) { - PlatformImpl.runLater(new Runnable() { + logger.log(Level.INFO, "Setting Info Label Text: " + text); + Platform.runLater(new Runnable() { @Override public void run() { infoLabel.setText(text); @@ -388,54 +432,26 @@ public class FXVideoPanel extends MediaViewVideoPanel { }); } - public void setMediaPlayer(MediaPlayer mp) { - pauseButton.setDisable(true); - mediaPlayer = mp; - mediaView.setMediaPlayer(mp); - pauseButton.setDisable(false); - - setMediaActionListeners(); - } - - private void setMediaActionListeners() { - mediaPlayer.setOnReady(new Runnable() { + /** + * Set the size of the MediaPane and it's components. + * + * @param dims the current dimensions of the DataContentViewer + */ + public void setFit(final Dimension dims) { + Platform.runLater(new Runnable() { @Override public void run() { - duration = mediaPlayer.getMedia().getDuration(); - long durationInMillis = (long) fxMediaPlayer.getMedia().getDuration().toMillis(); - - // pick out the total hours, minutes, seconds - long durationSeconds = (int) durationInMillis / 1000; - totalHours = (int) durationSeconds / 3600; - durationSeconds -= totalHours * 3600; - totalMinutes = (int) durationSeconds / 60; - durationSeconds -= totalMinutes * 60; - totalSeconds = (int) durationSeconds; - updateProgress(); - } - }); - - mediaPlayer.setOnEndOfMedia(new Runnable() { - @Override - public void run() { - Duration beginning = mediaPlayer.getStartTime(); - mediaPlayer.stop(); - mediaPlayer.pause(); - pauseButton.setText("►"); - updateSlider(beginning); - updateTime(beginning); + setPrefSize(dims.getWidth(), dims.getHeight()); + // Set the Video output to fit the size allocated for it. give an + // extra few px to ensure the info label will be shown + mediaView.setFitHeight(dims.getHeight() - controlPanel.getHeight()); } }); - - mediaPlayer.currentTimeProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue observable, Duration oldValue, Duration newValue) { - updateSlider(newValue); - updateTime(newValue); - } - }); - } + } + /** + * Set the action listeners for the pause button and progress slider. + */ private void setProgressActionListeners() { pauseButton.setOnAction(new EventHandler() { @Override @@ -445,22 +461,31 @@ public class FXVideoPanel extends MediaViewVideoPanel { switch (status) { // If playing, pause case PLAYING: - pauseButton.setText("►"); mediaPlayer.pause(); break; // If ready, paused or stopped, continue playing case READY: case PAUSED: case STOPPED: - pauseButton.setText("||"); mediaPlayer.play(); break; default: + logger.log(Level.INFO, "MediaPlayer in unexpected state: " + status.toString()); + // If the MediaPlayer is in an unexpected state, stop playback. + mediaPlayer.stop(); + setInfoLabelText("Playback error."); break; } } }); + stopButton.setOnAction(new EventHandler() { + @Override + public void handle(ActionEvent e) { + mediaPlayer.stop(); + } + }); + progressSlider.valueProperty().addListener(new InvalidationListener() { @Override public void invalidated(Observable o) { @@ -471,15 +496,61 @@ public class FXVideoPanel extends MediaViewVideoPanel { }); } + /** + * Reset the progress label and slider to zero. + */ + private void resetProgress() { + totalHours = 0; + totalMinutes = 0; + totalSeconds = 0; + progressSlider.setValue(0.0); + updateTime(Duration.ZERO); + } + + /** + * Construct a MediaPlayer from the given Media URI. + * + * Also adds the necessary listeners to MediaPlayer events. + * + * @param mediaUri the location of the media. + * @return a MediaPlayer + */ + private MediaPlayer createMediaPlayer(String mediaUri) { + MediaBuilder mediaBuilder = MediaBuilder.create(); + mediaBuilder.source(mediaUri); + Media media = mediaBuilder.build(); + + MediaPlayerBuilder mediaPlayerBuilder = MediaPlayerBuilder.create(); + mediaPlayerBuilder.media(media); + mediaPlayerBuilder.onReady(READY_LISTENER); + mediaPlayerBuilder.onPaused(NOT_PLAY_LISTENER); + mediaPlayerBuilder.onStopped(NOT_PLAY_LISTENER); + mediaPlayerBuilder.onPlaying(PLAY_LISTENER); + mediaPlayerBuilder.onEndOfMedia(END_LISTENER); + + MediaPlayer player = mediaPlayerBuilder.build(); + player.currentTimeProperty().addListener(TIME_LISTENER); + + return player; + } + + /** + * Update the progress slider and label with the current time of the media. + */ private void updateProgress() { Duration currentTime = mediaPlayer.getCurrentTime(); updateSlider(currentTime); updateTime(currentTime); } + /** + * Update the slider with the current time. + * + * @param currentTime + */ private void updateSlider(Duration currentTime) { if (progressSlider != null) { - progressSlider.setDisable(duration.isUnknown()); + progressSlider.setDisable(currentTime.isUnknown()); if (!progressSlider.isDisabled() && duration.greaterThan(Duration.ZERO) && !progressSlider.isValueChanging()) { progressSlider.setValue(currentTime.divide(duration.toMillis()).toMillis() * 100.0); @@ -487,6 +558,11 @@ public class FXVideoPanel extends MediaViewVideoPanel { } } + /** + * Update the progress label with the current time. + * + * @param currentTime + */ private void updateTime(Duration currentTime) { long millisElapsed = (long) currentTime.toMillis(); @@ -502,11 +578,16 @@ public class FXVideoPanel extends MediaViewVideoPanel { String durationStr = String.format(durationFormat, elapsedHours, elapsedMinutes, elapsedSeconds, totalHours, totalMinutes, totalSeconds); - progressLabel.setText(durationStr); + setProgressLabelText(durationStr); } + /** + * Update the progress label to show the text. + * + * @param text + */ private void setProgressLabelText(final String text) { - PlatformImpl.runLater(new Runnable() { + Platform.runLater(new Runnable() { @Override public void run() { progressLabel.setText(text); @@ -515,13 +596,87 @@ public class FXVideoPanel extends MediaViewVideoPanel { } private void setInfoLabelToolTipText(final String text) { - PlatformImpl.runLater(new Runnable() { + Platform.runLater(new Runnable() { @Override public void run() { infoLabel.setTooltip(new Tooltip(text)); } }); } + + /** + * Responds to MediaPlayer onReady events. + * + * Updates the progress label with the duration of the media. + */ + private class ReadyListener implements Runnable { + @Override + public void run() { + duration = mediaPlayer.getMedia().getDuration(); + long durationInMillis = (long) mediaPlayer.getMedia().getDuration().toMillis(); + + // pick out the total hours, minutes, seconds + long durationSeconds = (int) durationInMillis / 1000; + totalHours = (int) durationSeconds / 3600; + durationSeconds -= totalHours * 3600; + totalMinutes = (int) durationSeconds / 60; + durationSeconds -= totalMinutes * 60; + totalSeconds = (int) durationSeconds; + updateProgress(); + } + } + + /** + * Responds to MediaPlayer onEndOfMediaEvents. + * + * Prepares the media to be replayed. + */ + private class EndOfMediaListener implements Runnable { + @Override + public void run() { + Duration beginning = mediaPlayer.getStartTime(); + mediaPlayer.stop(); + mediaPlayer.pause(); + pauseButton.setText(PLAY_TEXT); + updateSlider(beginning); + updateTime(beginning); + } + } + + /** + * Responds to changes in the MediaPlayer currentTime property. + * + * Updates the progress slider and label with the current Time. + */ + private class TimeListener implements ChangeListener { + @Override + public void changed(ObservableValue observable, Duration oldValue, Duration newValue) { + updateSlider(newValue); + updateTime(newValue); + } + } + + /** + * Triggered when MediaPlayer State changes to PAUSED or Stopped. + */ + private class NotPlayListener implements Runnable { + @Override + public void run() { + pauseButton.setText(PLAY_TEXT); + pauseButton.setStyle(PLAY_STYLE); + } + } + + /** + * Triggered when MediaPlayer State changes to PLAYING. + */ + private class PlayListener implements Runnable { + @Override + public void run() { + pauseButton.setText(PAUSE_TEXT); + pauseButton.setStyle(PAUSE_STYLE); + } + } } /** @@ -566,7 +721,7 @@ public class FXVideoPanel extends MediaViewVideoPanel { // } // // private void initFx(final java.io.File file) { -// PlatformImpl.runAndWait(new Runnable() { +// Platform.runAndWait(new Runnable() { // @Override // public void run() { // logger.log(Level.INFO, "In initFX."); @@ -620,7 +775,7 @@ public class FXVideoPanel extends MediaViewVideoPanel { // logger.log(Level.INFO, "Grabbing a frame..."); // final long timeStamp = i * frameInterval + INTER_FRAME_PERIOD_MS; // -// // PlatformImpl.runLater(new Runnable() { +// // Platform.runLater(new Runnable() { // // @Override // // public void run() { // // synchronized (frameLock) { diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.form new file mode 100644 index 0000000000..890e369b0d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.form @@ -0,0 +1,120 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.java index 1eea6fb84d..955cc1a525 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.java @@ -437,7 +437,7 @@ public class GstVideoPanel extends MediaViewVideoPanel { * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") - // + // //GEN-BEGIN:initComponents private void initComponents() { videoPanel = new javax.swing.JPanel(); @@ -455,69 +455,70 @@ public class GstVideoPanel extends MediaViewVideoPanel { ); videoPanelLayout.setVerticalGroup( videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 188, Short.MAX_VALUE) + .addGap(0, 231, Short.MAX_VALUE) ); - org.openide.awt.Mnemonics.setLocalizedText(pauseButton, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.pauseButton.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(pauseButton, org.openide.util.NbBundle.getMessage(GstVideoPanel.class, "MediaViewVideoPanel.pauseButton.text")); // NOI18N pauseButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { pauseButtonActionPerformed(evt); } }); - org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.progressLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(GstVideoPanel.class, "MediaViewVideoPanel.progressLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(infoLabel, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.infoLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(infoLabel, org.openide.util.NbBundle.getMessage(GstVideoPanel.class, "MediaViewVideoPanel.infoLabel.text")); // NOI18N javax.swing.GroupLayout controlPanelLayout = new javax.swing.GroupLayout(controlPanel); controlPanel.setLayout(controlPanelLayout); controlPanelLayout.setHorizontalGroup( controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(controlPanelLayout.createSequentialGroup() - .addComponent(pauseButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 357, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(progressLabel) - .addContainerGap()) - .addGroup(controlPanelLayout.createSequentialGroup() - .addComponent(infoLabel) - .addGap(0, 0, Short.MAX_VALUE)) + .addContainerGap() + .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(controlPanelLayout.createSequentialGroup() + .addGap(6, 6, 6) + .addComponent(infoLabel) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(controlPanelLayout.createSequentialGroup() + .addComponent(pauseButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 265, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(progressLabel) + .addContainerGap()))) ); controlPanelLayout.setVerticalGroup( controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(controlPanelLayout.createSequentialGroup() + .addContainerGap() .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(pauseButton) .addComponent(progressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(progressLabel, javax.swing.GroupLayout.Alignment.TRAILING)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(infoLabel)) + .addComponent(pauseButton) + .addComponent(progressLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(infoLabel) + .addContainerGap()) ); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(videoPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(controlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) + .addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) ); - }// + }// - private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) { + private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed synchronized (playbinLock) { - if (gstPlaybin2 == null) { - infoLabel.setText("Error: Playbin is null"); - return; - } State state = gstPlaybin2.getState(); if (state.equals(State.PLAYING)) { if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) { @@ -551,16 +552,17 @@ public class GstVideoPanel extends MediaViewVideoPanel { em.getExtractedBytes(); } } - } - // Variables declaration - do not modify + }//GEN-LAST:event_pauseButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel controlPanel; private javax.swing.JLabel infoLabel; private javax.swing.JButton pauseButton; private javax.swing.JLabel progressLabel; private javax.swing.JSlider progressSlider; private javax.swing.JPanel videoPanel; - // End of variables declaration - + // End of variables declaration//GEN-END:variables + private class VideoProgressWorker extends SwingWorker { private String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d "; diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Installer.java b/Core/src/org/sleuthkit/autopsy/corecomponents/Installer.java index 66eb1d2d23..ecf5b847b7 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Installer.java @@ -18,10 +18,10 @@ */ package org.sleuthkit.autopsy.corecomponents; -import com.sun.javafx.application.PlatformImpl; import java.awt.Insets; +import java.util.Map; +import java.util.TreeMap; import java.util.logging.Level; -import javafx.application.Platform; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.BorderFactory; import javax.swing.UIManager; @@ -29,9 +29,9 @@ import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.UnsupportedLookAndFeelException; import org.netbeans.swing.tabcontrol.plaf.DefaultTabbedContainerUI; import org.openide.modules.ModuleInstall; +import org.openide.util.Exceptions; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; /** * Manages this module's lifecycle. Opens the startup dialog during startup. @@ -57,7 +57,7 @@ public class Installer extends ModuleInstall { public void restored() { super.restored(); - //setupLAF(); + setupLAF(); UIManager.put("ViewTabDisplayerUI", "org.sleuthkit.autopsy.corecomponents.NoTabsTabDisplayerUI"); UIManager.put(DefaultTabbedContainerUI.KEY_VIEW_CONTENT_BORDER, BorderFactory.createEmptyBorder()); UIManager.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0)); @@ -84,25 +84,58 @@ public class Installer extends ModuleInstall { //UIManager.put("nimbusBase", new Color()); //UIManager.put("nimbusBlueGrey", new Color()); //UIManager.put("control", new Color()); + + if (System.getProperty("os.name").toLowerCase().contains("mac")) { + setupMacOsXLAF(); + } + + } - - Logger logger = Logger.getLogger(Installer.class.getName()); - //use Nimbus if available + /** + * Set the look and feel to be the Cross Platform 'Metal', but keep Aqua + * dependent elements that set the Menu Bar to be in the correct place on + * Mac OS X. + */ + private void setupMacOsXLAF() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (ClassNotFoundException | InstantiationException + | IllegalAccessException | UnsupportedLookAndFeelException ex) { + logger.log(Level.WARNING, "Unable to set theme. ", ex); + } + + final String[] UI_MENU_ITEM_KEYS = new String[]{"MenuBarUI", + "MenuUI", + "MenuItemUI", + "CheckBoxMenuItemUI", + "RadioButtonMenuItemUI", + "PopupMenuUI"}; + + Map uiEntries = new TreeMap(); + + // Store the keys that deal with menu items + for(String key : UI_MENU_ITEM_KEYS) { + uiEntries.put(key, UIManager.get(key)); + } + + + //use Metal if available for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { try { UIManager.setLookAndFeel(info.getClassName()); - } catch (ClassNotFoundException ex) { - logger.log(Level.WARNING, "Unable to set theme. ", ex); - } catch (InstantiationException ex) { - logger.log(Level.WARNING, "Unable to set theme. ", ex); - } catch (IllegalAccessException ex) { - logger.log(Level.WARNING, "Unable to set theme. ", ex); - } catch (UnsupportedLookAndFeelException ex) { + } catch (ClassNotFoundException | InstantiationException | + IllegalAccessException | UnsupportedLookAndFeelException ex) { logger.log(Level.WARNING, "Unable to set theme. ", ex); } break; } } + + // Overwrite the Metal menu item keys to use the Aqua versions + for(Map.Entry entry : uiEntries.entrySet()) { + UIManager.put(entry.getKey(), entry.getValue()); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java index 8589b0ea7d..bcaa1d280c 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java @@ -18,14 +18,13 @@ */ package org.sleuthkit.autopsy.corecomponents; -import com.sun.javafx.application.PlatformImpl; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; -import java.util.Collection; import java.util.logging.Level; +import javafx.application.Platform; import javafx.embed.swing.JFXPanel; import javafx.embed.swing.SwingFXUtils; import javafx.scene.Scene; @@ -33,8 +32,7 @@ import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javax.imageio.ImageIO; -import org.openide.modules.ModuleInstall; -import org.openide.util.Lookup; +import javax.swing.SwingUtilities; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.corelibs.ScalrWrapper; import org.sleuthkit.autopsy.coreutils.Logger; @@ -61,11 +59,7 @@ public class MediaViewImagePanel extends javax.swing.JPanel { - org.sleuthkit.autopsy.core.Installer coreInstaller = - ModuleInstall.findObject(org.sleuthkit.autopsy.core.Installer.class, false); - if (coreInstaller != null) { - fxInited = coreInstaller.isJavaFxInited(); - } + fxInited = org.sleuthkit.autopsy.core.Installer.isJavaFxInited(); if (fxInited) { @@ -82,7 +76,7 @@ public class MediaViewImagePanel extends javax.swing.JPanel { */ private void setupFx() { // load the image - PlatformImpl.runLater(new Runnable() { + Platform.runLater(new Runnable() { @Override public void run() { fxPanel = new JFXPanel(); @@ -129,7 +123,7 @@ public class MediaViewImagePanel extends javax.swing.JPanel { fxPanel.setVisible(false); // load the image - PlatformImpl.runLater(new Runnable() { + Platform.runLater(new Runnable() { @Override public void run() { if (!Case.isCaseOpen()) { @@ -188,8 +182,13 @@ public class MediaViewImagePanel extends javax.swing.JPanel { fxPanel.setScene(fxScene); - //show the panel after fully loaded - fxPanel.setVisible(true); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + //show the panel after fully loaded + fxPanel.setVisible(true); + } + }); } }); diff --git a/CoreLibs/src/org/sleuthkit/autopsy/corelibs/SigarLoader.java b/CoreLibs/src/org/sleuthkit/autopsy/corelibs/SigarLoader.java index 6e06539cda..f0f0859478 100644 --- a/CoreLibs/src/org/sleuthkit/autopsy/corelibs/SigarLoader.java +++ b/CoreLibs/src/org/sleuthkit/autopsy/corelibs/SigarLoader.java @@ -18,7 +18,7 @@ */ package org.sleuthkit.autopsy.corelibs; -import java.io.File; +import com.sun.javafx.PlatformUtil; import org.hyperic.sigar.Sigar; /** @@ -42,7 +42,11 @@ public class SigarLoader { if (sigar == null) { try { //rely on netbeans / jna to locate the lib variation for architecture/OS - System.loadLibrary("libsigar"); + if (PlatformUtil.isWindows()) { + System.loadLibrary("libsigar"); + } else { + System.loadLibrary("sigar"); + } sigar = new Sigar(); sigar.enableLogging(false); //forces a test diff --git a/Timeline/release/mactime/mactime.pl b/Timeline/release/mactime/mactime.pl new file mode 100755 index 0000000000..868d930906 --- /dev/null +++ b/Timeline/release/mactime/mactime.pl @@ -0,0 +1,938 @@ +#!/usr/bin/perl -w +my $VER="4.1.0"; +# +# This program is based on the 'mactime' program by Dan Farmer and +# and the 'mac_daddy' program by Rob Lee. +# +# It takes as input data from either 'ils -m' or 'fls -m' (from The Sleuth +# Kit) or 'mac-robber'. +# Based on the dates as arguments given, the data is sorted by and +# printed. +# +# The Sleuth Kit +# Brian Carrier [carrier sleuthkit [dot] org] +# Copyright (c) 2003-2012 Brian Carrier. All rights reserved +# +# TASK +# Copyright (c) 2002 Brian Carrier, @stake Inc. All rights reserved +# +# +# The modifications to the original mactime are distributed under +# the Common Public License 1.0 +# +# +# Copyright 1999 by Dan Farmer. All rights reserved. Some individual +# files may be covered by other copyrights (this will be noted in the +# file itself.) +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies. +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +use POSIX; +use strict; + +my $debug = 0; + +# %month_to_digit = ("Jan", 1, "Feb", 2, "Mar", 3, "Apr", 4, "May", 5, "Jun", 6, +# "Jul", 7, "Aug", 8, "Sep", 9, "Oct", 10, "Nov", 11, "Dec", 12); +my %digit_to_month = ( + "01", "Jan", "02", "Feb", "03", "Mar", "04", "Apr", + "05", "May", "06", "Jun", "07", "Jul", "08", "Aug", + "09", "Sep", "10", "Oct", "11", "Nov", "12", "Dec" +); +my %digit_to_day = ( + "0", "Sun", "1", "Mon", "2", "Tue", "3", "Wed", + "4", "Thu", "5", "Fri", "6", "Sat" +); + +sub usage { + print <all_names() ) { + push( @t_list, $_ ); + } + foreach( keys( %{DateTime::TimeZone->links()}) ) { + push( @t_list, $_ ); + } + + return sort { $a cmp $b } @t_list; +} + +usage() if (scalar(@ARGV) == 0); + +while ((scalar(@ARGV) > 0) && (($_ = $ARGV[0]) =~ /^-(.)(.*)/)) { + + # Body File + if (/^-b$/) { + shift(@ARGV); + if (defined $ARGV[0]) { + $BODY = $ARGV[0]; + } + else { + print "-b requires body file argument\n"; + } + } + elsif (/^-d$/) { + $COMMA = 1; + } + + # Group File + elsif (/^-g$/) { + shift(@ARGV); + if (defined $ARGV[0]) { + &'load_group_info($ARGV[0]); + $GROUP = $ARGV[0]; + } + else { + print "-g requires group file argument\n"; + usage(); + } + } + + # Password File + elsif (/^-p$/) { + shift(@ARGV); + if (defined $ARGV[0]) { + &'load_passwd_info($ARGV[0]); + $PASSWD = $ARGV[0]; + } + else { + print "-p requires password file argument\n"; + usage(); + } + } + elsif (/^-h$/) { + $header = 1; + } + + # Index File + elsif (/^-i$/) { + shift(@ARGV); + + if (defined $ARGV[0]) { + + # Find out what type + if ($ARGV[0] eq "day") { + $INDEX_TYPE = $INDEX_DAY; + } + elsif ($ARGV[0] eq "hour") { + $INDEX_TYPE = $INDEX_HOUR; + } + shift(@ARGV); + unless (defined $ARGV[0]) { + print "-i requires index file argument\n"; + usage(); + } + $INDEX = $ARGV[0]; + } + else { + print "-i requires index file argument and type\n"; + usage(); + } + open(INDEX, ">$INDEX") or die "Can not open $INDEX"; + } + elsif (/^-V$/) { + version(); + exit(0); + } + elsif (/^-m$/) { + $month_num = 1; + } + elsif (/^-y$/) { + $iso8601 = 1; + } + elsif (/^-z$/) { + shift(@ARGV); + if (defined $ARGV[0]) { + my $tz = "$ARGV[0]"; + + if ($tz =~ m/^list$/i) { + if ($_HAS_DATETIME_TIMEZONE) { + my $txt = " +----------------------------------- + TIMEZONE LIST +-----------------------------------\n"; + foreach ( get_timezone_list() ) { + $txt .= $_ . "\n"; + } + print( $txt ); + } + else { + print "DateTime module not loaded -- cannot list timezones\n"; + } + exit(0); + } + # validate the string if we have DateTime module + elsif ($_HAS_DATETIME_TIMEZONE) { + my $realtz = 0; + foreach ( get_timezone_list() ) { + if ($tz =~ m/^$_$/i) { + $realtz = $_; + last; + } + } + if ($realtz) { + $ENV{TZ} = $realtz; + } + else { + print "invalid timezone provided. Use -z to list valid timezones.\n"; + usage(); + } + } + # blindly take it otherwise + else { + $ENV{TZ} = $tz; + } + } + else { + print "-z requires the time zone argument\n"; + usage(); + } + } + else { + print "Unknown option: $_\n"; + usage(); + } + shift(@ARGV); +} + +# Was the time given +if (defined $ARGV[0]) { + my $t_in; + my $t_out; + + $TIME = $ARGV[0]; + if ($ARGV[0] =~ /\.\./) { + ($t_in, $t_out) = split(/\.\./, $ARGV[0]); + } + else { + $t_in = $ARGV[0]; + $t_out = 0; + } + $in_seconds = parse_isodate($t_in); + die "Invalid Date: $t_in\n" if ($in_seconds < 0); + + if ($t_out) { + $out_seconds = parse_isodate($t_out); + die "Invalid Date: $t_out\n" if ($out_seconds < 0); + } + else { + $out_seconds = 0; + } +} +else { + $in_seconds = 0; + $out_seconds = 0; +} + +# Print header info +print_header() if ($header == 1); + +# Print the index header +if ($INDEX ne "") { + my $time_str = ""; + if ($INDEX_TYPE == $INDEX_DAY) { + $time_str = "Daily"; + } + else { + $time_str = "Hourly"; + } + if ($BODY ne "") { + print INDEX "$time_str Summary for Timeline of $BODY\n\n"; + } + else { + print INDEX "$time_str Summary for Timeline of STDIN\n\n"; + } +} + +read_body(); + +print_tl(); + +################ SUBROUTINES ################## + +#convert yyyy-mm-dd string to Unix date +sub parse_isodate { + my $iso_date = shift; + + my $sec = 0; + my $min = 0; + my $hour = 0; + my $wday = 0; + my $yday = 0; + if ($iso_date =~ /^(\d\d\d\d)\-(\d\d)\-(\d\d)$/) { + return mktime($sec, $min, $hour, $3, $2 - 1, $1 - 1900, $wday, $yday); + } + else { + return -1; + } +} + +# Read the body file from the BODY variable +sub read_body { + + # Read the body file from STDIN or the -b specified body file + if ($BODY ne "") { + open(BODY, "<$BODY") or die "Can't open $BODY"; + } + else { + open(BODY, "<&STDIN") or die "Can't dup STDIN"; + } + + while () { + next if ((/^\#/) || (/^\s+$/)); + + chomp; + + my ( + $tmp1, $file, $st_ino, $st_ls, + $st_uid, $st_gid, $st_size, $st_atime, + $st_mtime, $st_ctime, $st_crtime, $tmp2 + ) + = &tm_split($_); + + # Sanity check so that we ignore the header entries + next unless ((defined $st_ino) && ($st_ino =~ /[\d-]+/)); + next unless ((defined $st_uid) && ($st_uid =~ /\d+/)); + next unless ((defined $st_gid) && ($st_gid =~ /\d+/)); + next unless ((defined $st_size) && ($st_size =~ /\d+/)); + next unless ((defined $st_mtime) && ($st_mtime =~ /\d+/)); + next unless ((defined $st_atime) && ($st_atime =~ /\d+/)); + next unless ((defined $st_ctime) && ($st_ctime =~ /\d+/)); + next unless ((defined $st_crtime) && ($st_crtime =~ /\d+/)); + + # we need *some* value in mactimes! + next if (!$st_atime && !$st_mtime && !$st_ctime && !$st_crtime); + + # Skip if these are all too early + next + if ( ($st_mtime < $in_seconds) + && ($st_atime < $in_seconds) + && ($st_ctime < $in_seconds) + && ($st_crtime < $in_seconds)); + + # add leading zeros to timestamps because we will later sort + # these using a string-based comparison + $st_mtime = sprintf("%.10d", $st_mtime); + $st_atime = sprintf("%.10d", $st_atime); + $st_ctime = sprintf("%.10d", $st_ctime); + $st_crtime = sprintf("%.10d", $st_crtime); + + # Put all the times in one big array along with the inode and + # name (they are used in the final sorting) + + # If the date on the file is too old, don't put it in the array + my $post = ",$st_ino,$file"; + + if ($out_seconds) { + $timestr2macstr{"$st_mtime$post"} .= "m" + if ( + ($st_mtime >= $in_seconds) + && ($st_mtime < $out_seconds) + && ( (!(exists $timestr2macstr{"$st_mtime$post"})) + || ($timestr2macstr{"$st_mtime$post"} !~ /m/)) + ); + + $timestr2macstr{"$st_atime$post"} .= "a" + if ( + ($st_atime >= $in_seconds) + && ($st_atime < $out_seconds) + && ( (!(exists $timestr2macstr{"$st_atime$post"})) + || ($timestr2macstr{"$st_atime$post"} !~ /a/)) + ); + + $timestr2macstr{"$st_ctime$post"} .= "c" + if ( + ($st_ctime >= $in_seconds) + && ($st_ctime < $out_seconds) + && ( (!(exists $timestr2macstr{"$st_ctime$post"})) + || ($timestr2macstr{"$st_ctime$post"} !~ /c/)) + ); + + $timestr2macstr{"$st_crtime$post"} .= "b" + if ( + ($st_crtime >= $in_seconds) + && ($st_crtime < $out_seconds) + && ( (!(exists $timestr2macstr{"$st_crtime$post"})) + || ($timestr2macstr{"$st_crtime$post"} !~ /b/)) + ); + } + else { + $timestr2macstr{"$st_mtime$post"} .= "m" + if ( + ($st_mtime >= $in_seconds) + && ( (!(exists $timestr2macstr{"$st_mtime$post"})) + || ($timestr2macstr{"$st_mtime$post"} !~ /m/)) + ); + + $timestr2macstr{"$st_atime$post"} .= "a" + if ( + ($st_atime >= $in_seconds) + && ( (!(exists $timestr2macstr{"$st_atime$post"})) + || ($timestr2macstr{"$st_atime$post"} !~ /a/)) + ); + + $timestr2macstr{"$st_ctime$post"} .= "c" + if ( + ($st_ctime >= $in_seconds) + && ( (!(exists $timestr2macstr{"$st_ctime$post"})) + || ($timestr2macstr{"$st_ctime$post"} !~ /c/)) + ); + + $timestr2macstr{"$st_crtime$post"} .= "b" + if ( + ($st_crtime >= $in_seconds) + && ( (!(exists $timestr2macstr{"$st_crtime$post"})) + || ($timestr2macstr{"$st_crtime$post"} !~ /b/)) + ); + } + + # if the UID or GID is not in the array then add it. + # these are filled if the -p or -g options are given + $uid2names{$st_uid} = $st_uid + unless (defined $uid2names{$st_uid}); + $gid2names{$st_gid} = $st_gid + unless (defined $gid2names{$st_gid}); + + # + # put /'s between multiple UID/GIDs + # + $uid2names{$st_uid} =~ s@\s@/@g; + $gid2names{$st_gid} =~ s@\s@/@g; + + $file2other{$file} = + "$st_ls:$uid2names{$st_uid}:$gid2names{$st_gid}:$st_size"; + } + + close BODY; +} # end of read_body + +sub print_header { + return if ($header == 0); + + print "The Sleuth Kit mactime Timeline\n"; + + print "Input Source: "; + if ($BODY eq "") { + print "STDIN\n"; + } + else { + print "$BODY\n"; + } + + print "Time: $TIME\t\t" if ($TIME ne ""); + + if ($ENV{TZ} eq "") { + print "\n"; + } + else { + print "Timezone: $ENV{TZ}\n"; + } + + print "passwd File: $PASSWD" if ($PASSWD ne ""); + if ($GROUP ne "") { + print "\t" if ($PASSWD ne ""); + print "group File: $GROUP"; + } + print "\n" if (($PASSWD ne "") || ($GROUP ne "")); + + print "\n"; +} + +# +# Print the time line +# +sub print_tl { + + my $prev_day = ""; # has the format of 'day day_week mon year' + my $prev_hour = ""; # has just the hour and is used for hourly index + my $prev_cnt = 0; + my $old_date_string = ""; + + my $delim = ":"; + if ($COMMA != 0) { + print "Date,Size,Type,Mode,UID,GID,Meta,File Name\n"; + $delim = ","; + } + + # Cycle through the files and print them in sorted order. + # Note that we sort using a string comparison because the keys + # also contain the inode and file name + for my $key (sort { $a cmp $b } keys %timestr2macstr) { + my $time; + my $inode; + my $file; + + if ($key =~ /^(\d+),([\d-]+),(.*)$/) { + $time = $1; + $inode = $2; + $file = $3; + } + else { + next; + } + + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst); + if ($iso8601) { + ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = + gmtime($time); + } + else { + ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = + localtime($time); + } + + # the month here is 0-11, not 1-12, like what we want + $mon++; + + print +"\t($sec,$min,$hour,MDay: $mday,M: $mon,$year,$wday,$yday,$isdst) = ($time)\n" + if $debug; + + # + # cosmetic change to make it look like unix dates + # + $mon = "0$mon" if $mon < 10; + $mday = "0$mday" if $mday < 10; + $hour = "0$hour" if $hour < 10; + $min = "0$min" if $min < 10; + $sec = "0$sec" if $sec < 10; + + my $yeart = $year + 1900; + + # How do we print the date? + # + my $date_string; + if ($iso8601) { + if ($time == 0) { + $date_string = "0000-00-00T00:00:00Z"; + } + else { + $date_string = +"$yeart-$mon-${mday}T$hour:$min:${sec}Z"; + } + } + else { + if ($time == 0) { + $date_string = "Xxx Xxx 00 0000 00:00:00"; + } + elsif ($month_num) { + $date_string = + "$digit_to_day{$wday} $mon $mday $yeart $hour:$min:$sec"; + } + else { + $date_string = +"$digit_to_day{$wday} $digit_to_month{$mon} $mday $yeart $hour:$min:$sec"; + } + } + + # + # However, we only print the date if it's different from the one + # above. We need to fill the empty space with blanks, though. + # + if ($old_date_string eq $date_string) { + if ($iso8601) { + $date_string = " "; + } + else { + $date_string = " "; + } + $prev_cnt++ + if ($INDEX ne ""); + } + else { + $old_date_string = $date_string; + + # Indexing code + if ($INDEX ne "") { + + # First time it is run + if ($prev_day eq "") { + $prev_day = "$mday $wday $mon $yeart"; + $prev_hour = $hour; + $prev_cnt = 0; + } + + # A new day, so print the results + elsif ($prev_day ne "$mday $wday $mon $yeart") { + my @prev_vals = split(/ /, $prev_day); + + my $date_str; + if ($month_num) { + $date_str = + "$digit_to_day{$prev_vals[1]} " + . "$prev_vals[2] " + . "$prev_vals[0] ${prev_vals[3]}"; + } + else { + $date_str = + "$digit_to_day{$prev_vals[1]} " + . "$digit_to_month{$prev_vals[2]} " + . "$prev_vals[0] ${prev_vals[3]}"; + } + + $date_str .= " $prev_hour:00:00" + if ($INDEX_TYPE == $INDEX_HOUR); + + print INDEX "${date_str}${delim} $prev_cnt\n"; + + # Reset + $prev_cnt = 0; + $prev_day = "$mday $wday $mon $yeart"; + $prev_hour = $hour; + + } + + # Same day, but new hour + elsif (($INDEX_TYPE == $INDEX_HOUR) && ($prev_hour != $hour)) { + my @prev_vals = split(/ /, $prev_day); + + if ($month_num) { + print INDEX "$digit_to_day{$prev_vals[1]} " + . "$prev_vals[2] " + . "$prev_vals[0] ${prev_vals[3]} " + . "$prev_hour:00:00${delim} $prev_cnt\n"; + } + else { + print INDEX "$digit_to_day{$prev_vals[1]} " + . "$digit_to_month{$prev_vals[2]} " + . "$prev_vals[0] ${prev_vals[3]} " + . "$prev_hour:00:00${delim} $prev_cnt\n"; + } + + # Reset + $prev_cnt = 0; + $prev_hour = $hour; + } + $prev_cnt++; + } + } + + # + # Muck around with the [mac]times string to make it pretty. + # + my $mactime_tmp = $timestr2macstr{$key}; + my $mactime = ""; + if ($mactime_tmp =~ /m/) { + $mactime = "m"; + } + else { + $mactime = "."; + } + + if ($mactime_tmp =~ /a/) { + $mactime .= "a"; + } + else { + $mactime .= "."; + } + + if ($mactime_tmp =~ /c/) { + $mactime .= "c"; + } + else { + $mactime .= "."; + } + + if ($mactime_tmp =~ /b/) { + $mactime .= "b"; + } + else { + $mactime .= "."; + } + + my ($ls, $uids, $groups, $size) = split(/:/, $file2other{$file}); + + print "FILE: $file MODES: $ls U: $uids G: $groups S: $size\n" + if $debug; + + if ($COMMA == 0) { + printf("%s %8s %3s %s %-8s %-8s %-8s %s\n", + $date_string, $size, $mactime, $ls, $uids, $groups, $inode, + $file); + } + else { + # escape any quotes in filename + my $file_tmp = $file; + $file_tmp =~ s/\"/\"\"/g; + printf("%s,%s,%s,%s,%s,%s,%s,\"%s\"\n", + $old_date_string, $size, $mactime, $ls, $uids, $groups, $inode, + $file_tmp); + } + } + + # Finish the index page for the last entry + if (($INDEX ne "") && ($prev_cnt > 0)) { + my @prev_vals = split(/ /, $prev_day); + + my $date_str; + if ($month_num) { + $date_str = + "$digit_to_day{$prev_vals[1]} " + . "$prev_vals[2] " + . "$prev_vals[0] ${prev_vals[3]}"; + } + else { + $date_str = + "$digit_to_day{$prev_vals[1]} " + . "$digit_to_month{$prev_vals[2]} " + . "$prev_vals[0] ${prev_vals[3]}"; + } + + $date_str .= " $prev_hour:00:00" + if ($INDEX_TYPE == $INDEX_HOUR); + + print INDEX "${date_str}${delim} $prev_cnt\n"; + close INDEX; + } +} + +# +# Routines for reading and caching user and group information. These +# are used in multiple programs... it caches the info once, then hopefully +# won't be used again. +# +# Steve Romig, May 1991. +# +# Provides a bunch of routines and a bunch of arrays. Routines +# (and their usage): +# +# load_passwd_info($use_getent, $file_name) +# +# loads user information into the %uname* and %uid* arrays +# (see below). +# +# If $use_getent is non-zero: +# get the info via repeated 'getpwent' calls. This can be +# *slow* on some hosts, especially if they are running as a +# YP (NIS) client. +# If $use_getent is 0: +# if $file_name is "", then get the info from reading the +# results of "ypcat passwd" and from /etc/passwd. Otherwise, +# read the named file. The file should be in passwd(5) +# format. +# +# load_group_info($use_gentent, $file_name) +# +# is similar to load_passwd_info. +# +# Information is stored in several convenient associative arrays: +# +# %uid2names Assoc array, indexed by uid, value is list of +# user names with that uid, in form "name name +# name...". +# +# %gid2members Assoc array, indexed by gid, value is list of +# group members in form "name name name..." +# +# %gname2gid Assoc array, indexed by group name, value is +# matching gid. +# +# %gid2names Assoc array, indexed by gid, value is the +# list of group names with that gid in form +# "name name name...". +# +# You can also use routines named the same as the arrays - pass the index +# as the arg, get back the value. If you use this, get{gr|pw}{uid|gid|nam} +# will be used to lookup entries that aren't found in the cache. +# +# To be done: +# probably ought to add routines to deal with full names. +# maybe there ought to be some anal-retentive checking of password +# and group entries. +# probably ought to cache get{pw|gr}{nam|uid|gid} lookups also. +# probably ought to avoid overwriting existing entries (eg, duplicate +# names in password file would collide in the tables that are +# indexed by name). +# +# Disclaimer: +# If you use YP and you use netgroup entries such as +# +@servers:::::: +# +:*:::::/usr/local/utils/messages +# then loading the password file in with &load_passwd_info(0) will get +# you mostly correct YP stuff *except* that it won't do the password and +# shell substitutions as you'd expect. You might want to use +# &load_passwd_info(1) instead to use getpwent calls to do the lookups, +# which would be more correct. +# +# +# minor changes to make it fit with the TCT program, 9/25/99, - dan +# A whole lot removed to clean it up for TSK - July 2008 - Brian +# + +package main; + +my $passwd_loaded = 0; # flags to use to avoid reloading everything +my $group_loaded = 0; # unnecessarily... + +# +# Update user information for the user named $name. We cache the password, +# uid, login group, home directory and shell. +# + +sub add_pw_info { + my ($name, $tmp, $uid) = @_; + + if ((defined $name) && ($name ne "")) { + + if ((defined $uid) && ($uid ne "")) { + if (defined($uid2names{$uid})) { + $uid2names{$uid} .= " $name"; + } + else { + $uid2names{$uid} = $name; + } + } + } +} + +# +# Update group information for the group named $name. We cache the gid +# and the list of group members. +# + +sub add_gr_info { + my ($name, $tmp, $gid) = @_; + + if ((defined $name) && ($name ne "")) { + + if ((defined $gid) && ($gid ne "")) { + if (defined($gid2names{$gid})) { + $gid2names{$gid} .= " $name"; + } + else { + $gid2names{$gid} = $name; + } + } + } +} + +sub load_passwd_info { + my ($file_name) = @_; + my (@pw_info); + + if ($passwd_loaded) { + return; + } + + $passwd_loaded = 1; + + open(FILE, $file_name) + || die "can't open $file_name"; + + while () { + chop; + + if ($_ !~ /^\+/) { + &add_pw_info(split(/:/)); + } + } + close(FILE); +} + +sub load_group_info { + my ($file_name) = @_; + my (@gr_info); + + if ($group_loaded) { + return; + } + + $group_loaded = 1; + + open(FILE, $file_name) + || die "can't open $file_name"; + + while () { + chop; + if ($_ !~ /^\+/) { + &add_gr_info(split(/:/)); + } + } + close(FILE); +} + +# +# Split a time machine record. +# +sub tm_split { + my ($line) = @_; + my (@fields); + + for (@fields = split(/\|/, $line)) { + s/%([A-F0-9][A-F0-9])/pack("C", hex($1))/egis; + } + return @fields; +} +1; + diff --git a/Timeline/src/org/sleuthkit/autopsy/timeline/Timeline.java b/Timeline/src/org/sleuthkit/autopsy/timeline/Timeline.java index 74aafa2b59..781da5158a 100644 --- a/Timeline/src/org/sleuthkit/autopsy/timeline/Timeline.java +++ b/Timeline/src/org/sleuthkit/autopsy/timeline/Timeline.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.timeline; -import com.sun.javafx.application.PlatformImpl; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; @@ -86,6 +85,7 @@ import org.openide.util.actions.Presenter; import org.openide.util.lookup.Lookups; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.core.Installer; import org.sleuthkit.autopsy.corecomponents.DataContentPanel; import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.coreutils.Logger; @@ -98,7 +98,6 @@ import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.coreutils.ExecUtil; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @@ -147,11 +146,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, public Timeline() { super(); - org.sleuthkit.autopsy.core.Installer coreInstaller = - ModuleInstall.findObject(org.sleuthkit.autopsy.core.Installer.class, false); - if (coreInstaller != null) { - fxInited = coreInstaller.isJavaFxInited(); - } + fxInited = Installer.isJavaFxInited(); } @@ -211,7 +206,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, //JavaFX thread //JavaFX components MUST be run in the JavaFX thread, otherwise massive amounts of exceptions will be thrown and caught. Liable to freeze up and crash. //Components can be declared whenever, but initialization and manipulation must take place here. - PlatformImpl.runLater(new Runnable() { + Platform.runLater(new Runnable() { @Override public void run() { try { @@ -1044,18 +1039,23 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, } private String makeMacTime(String pathToBodyFile) { + String cmdpath = ""; String macpath = ""; + String[] mactimeArgs; final String machome = macRoot.getAbsolutePath(); pathToBodyFile = PlatformUtil.getOSFilePath(pathToBodyFile); if (PlatformUtil.isWindowsOS()) { macpath = machome + java.io.File.separator + "mactime.exe"; - macpath = PlatformUtil.getOSFilePath(macpath); + cmdpath = PlatformUtil.getOSFilePath(cmdpath); + mactimeArgs = new String[]{"-b", pathToBodyFile, "-d", "-y"}; } else { - macpath = "perl " + machome + java.io.File.separator + "mactime.pl"; + cmdpath = "perl"; + macpath = machome + java.io.File.separator + "mactime.pl"; + mactimeArgs = new String[]{macpath, "-b", pathToBodyFile, "-d", "-y"}; } String macfile = moduleDir.getAbsolutePath() + java.io.File.separator + mactimeFileName; - String[] mactimeArgs = new String[]{"-b", pathToBodyFile, "-d", "-y"}; + String output = ""; ExecUtil execUtil = new ExecUtil(); @@ -1063,7 +1063,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, try { //JavaSystemCaller.Exec.execute("\"" + command + "\""); writer = new FileWriter(macfile); - execUtil.execute(writer, macpath, mactimeArgs); + execUtil.execute(writer, cmdpath, mactimeArgs); } catch (InterruptedException ie) { logger.log(Level.WARNING, "Mactime process was interrupted by user", ie); return null; diff --git a/branding_autopsy/icon.icns b/branding_autopsy/icon.icns new file mode 100644 index 0000000000..2d55fafc0d Binary files /dev/null and b/branding_autopsy/icon.icns differ diff --git a/build.xml b/build.xml index 8d423b2c5c..b6161a7c2c 100644 --- a/build.xml +++ b/build.xml @@ -86,7 +86,7 @@ - +