/* * Autopsy Forensic Browser * * Copyright 2011-2015 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sleuthkit.autopsy.filesearch; import java.awt.Component; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.SimpleTimeZone; import java.util.TimeZone; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JSeparator; import javax.swing.ListCellRenderer; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; /** * Filters file date properties (modified/created/etc.. times) * * @author pmartel */ class DateSearchFilter extends AbstractFileSearchFilter<DateSearchPanel> { private static final String NONE_SELECTED_MESSAGE = NbBundle.getMessage(DateSearchFilter.class, "DateSearchFilter.noneSelectedMsg.text"); private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy"); private static final String SEPARATOR = "SEPARATOR"; //NON-NLS /** * New DateSearchFilter with the default panel */ DateSearchFilter() { this(new DateSearchPanel(DATE_FORMAT, DateSearchFilter.createTimeZoneList())); } private DateSearchFilter(DateSearchPanel panel) { super(panel); Case.addPropertyChangeListener(this.new CasePropertyChangeListener()); } @Override public boolean isEnabled() { return this.getComponent().getDateCheckBox().isSelected(); } @Override public String getPredicate() throws FilterValidationException { String query = "NULL"; DateSearchPanel panel = this.getComponent(); // first, get the selected timeZone from the dropdown list String tz = this.getComponent().getTimeZoneComboBox().getSelectedItem().toString(); String tzID = tz.substring(tz.indexOf(" ") + 1); // 1 index after the space is the ID TimeZone selectedTZ = TimeZone.getTimeZone(tzID); // // convert the date from the selected timezone to get the GMT long fromDate = 0; String startDateValue = panel.getDateFromTextField().getText(); Calendar startDate = null; try { DateFormat sdf = new SimpleDateFormat("MM/dd/yyyy"); sdf.setTimeZone(selectedTZ); // get the time in the selected timezone Date temp = sdf.parse(startDateValue); startDate = Calendar.getInstance(new SimpleTimeZone(0, "GMT")); //NON-NLS startDate.setTime(temp); // convert to GMT } catch (ParseException ex) { // for now, no need to show the error message to the user here } if (!startDateValue.equals("")) { if (startDate != null) { fromDate = startDate.getTimeInMillis() / 1000; // divided by 1000 because we want to get the seconds, not miliseconds } } long toDate = 0; String endDateValue = panel.getDateToTextField().getText(); Calendar endDate = null; try { DateFormat sdf = new SimpleDateFormat("MM/dd/yyyy"); sdf.setTimeZone(selectedTZ); // get the time in the selected timezone Date temp2 = sdf.parse(endDateValue); endDate = Calendar.getInstance(new SimpleTimeZone(0, "GMT")); //NON-NLS endDate.setTime(temp2); // convert to GMT endDate.set(Calendar.HOUR, endDate.get(Calendar.HOUR) + 24); // get the next 24 hours } catch (ParseException ex) { // for now, no need to show the error message to the user here } if (!endDateValue.equals("")) { if (endDate != null) { toDate = endDate.getTimeInMillis() / 1000; // divided by 1000 because we want to get the seconds, not miliseconds } } // If they put the dates in backwards, help them out. if (fromDate > toDate) { long temp = toDate; toDate = fromDate; fromDate = temp; } final boolean modifiedChecked = panel.getModifiedCheckBox().isSelected(); final boolean changedChecked = panel.getChangedCheckBox().isSelected(); final boolean accessedChecked = panel.getAccessedCheckBox().isSelected(); final boolean createdChecked = panel.getAccessedCheckBox().isSelected(); if (modifiedChecked || changedChecked || accessedChecked || createdChecked) { if (modifiedChecked) { query += " OR (mtime BETWEEN " + fromDate + " AND " + toDate + ")"; //NON-NLS } if (changedChecked) { query += " OR (ctime BETWEEN " + fromDate + " AND " + toDate + ")"; //NON-NLS } if (accessedChecked) { query += " OR (atime BETWEEN " + fromDate + " AND " + toDate + ")"; //NON-NLS } if (createdChecked) { query += " OR (crtime BETWEEN " + fromDate + " AND " + toDate + ")"; //NON-NLS } } else { throw new FilterValidationException(NONE_SELECTED_MESSAGE); } return query; } private void updateTimeZoneList() { this.getComponent().setTimeZones(DateSearchFilter.createTimeZoneList()); } private static List<String> createTimeZoneList() { List<String> timeZones = new ArrayList<>(); if (Case.isCaseOpen()) { // get the latest case Case currentCase = Case.getCurrentCase(); // get the most updated case Set<TimeZone> caseTimeZones = currentCase.getTimeZones(); Iterator<TimeZone> iterator = caseTimeZones.iterator(); while (iterator.hasNext()) { TimeZone zone = iterator.next(); int offset = zone.getRawOffset() / 1000; int hour = offset / 3600; int minutes = (offset % 3600) / 60; String item = String.format("(GMT%+d:%02d) %s", hour, minutes, zone.getID()); //NON-NLS timeZones.add(item); } if (caseTimeZones.size() > 0) { timeZones.add(SEPARATOR); } // load and add all timezone String[] ids = SimpleTimeZone.getAvailableIDs(); for (String id : ids) { TimeZone zone = TimeZone.getTimeZone(id); int offset = zone.getRawOffset() / 1000; int hour = offset / 3600; int minutes = (offset % 3600) / 60; String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id); //NON-NLS timeZones.add(item); } } return timeZones; } @Override public void addActionListener(ActionListener l) { getComponent().addActionListener(l); } @Override public boolean isValid() { return this.getComponent().isValidSearch(); } /** * Inner class to put the separator inside the combo box. */ static class ComboBoxRenderer extends JLabel implements ListCellRenderer<String> { JSeparator separator; ComboBoxRenderer() { setOpaque(true); setBorder(new EmptyBorder(1, 1, 1, 1)); separator = new JSeparator(JSeparator.HORIZONTAL); } @Override public Component getListCellRendererComponent(JList<? extends String> list, String value, int index, boolean isSelected, boolean cellHasFocus) { String str = (value == null) ? "" : value; if (SEPARATOR.equals(str)) { return separator; } if (isSelected) { setBackground(list.getSelectionBackground()); setForeground(list.getSelectionForeground()); } else { setBackground(list.getBackground()); setForeground(list.getForeground()); } setFont(list.getFont()); setText(str); return this; } } private class CasePropertyChangeListener implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent evt) { switch (Case.Events.valueOf(evt.getPropertyName())) { case CURRENT_CASE: Object newValue = evt.getNewValue(); if (null != newValue) { /** * Opening a new case. */ SwingUtilities.invokeLater(DateSearchFilter.this::updateTimeZoneList); } break; case DATA_SOURCE_ADDED: case DATA_SOURCE_DELETED: /** * Checking for a current case is a stop gap measure until a * different way of handling the closing of cases is worked * out. Currently, remote events may be received for a case * that is already closed. */ try { Case.getCurrentCase(); SwingUtilities.invokeLater(DateSearchFilter.this::updateTimeZoneList); } catch (IllegalStateException notUsed) { /** * Case is closed, do nothing. */ } break; } } } }