mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Remove ContentNode.getContent()
This commit is contained in:
parent
dfcf854109
commit
c842c9a6b5
@ -23,6 +23,7 @@ import java.awt.Cursor;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import javax.swing.JTextPane;
|
import javax.swing.JTextPane;
|
||||||
|
import org.openide.nodes.Node;
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
import org.sleuthkit.autopsy.datamodel.ContentNode;
|
import org.sleuthkit.autopsy.datamodel.ContentNode;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||||
@ -274,10 +275,14 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
@Override
|
@Override
|
||||||
public void setNode(ContentNode selectedNode) {
|
public void setNode(ContentNode selectedNode) {
|
||||||
if (selectedNode != null) {
|
if (selectedNode != null) {
|
||||||
this.setDataView(selectedNode.getContent(), 0, false);
|
Content content = ((Node) selectedNode).getLookup().lookup(Content.class);
|
||||||
} else {
|
if (content != null) {
|
||||||
this.setDataView(null, 0, true);
|
this.setDataView(content, 0, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setDataView(null, 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -31,6 +31,7 @@ import org.openide.nodes.Node;
|
|||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
import org.sleuthkit.autopsy.datamodel.ContentNode;
|
import org.sleuthkit.autopsy.datamodel.ContentNode;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.TskException;
|
import org.sleuthkit.datamodel.TskException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,7 +91,10 @@ public class DataContentViewerPicture extends javax.swing.JPanel implements Data
|
|||||||
if (selectedNode != null) {
|
if (selectedNode != null) {
|
||||||
try {
|
try {
|
||||||
// read the byte of the image file
|
// read the byte of the image file
|
||||||
byte[] dataSource = selectedNode.getContent().read(0, selectedNode.getContent().getSize());
|
|
||||||
|
// TODO: ContentNode fix - get rid of cast to Node
|
||||||
|
Content content = ((Node) selectedNode).getLookup().lookup(Content.class);
|
||||||
|
byte[] dataSource = content.read(0, content.getSize());
|
||||||
|
|
||||||
// create the input stream for the content
|
// create the input stream for the content
|
||||||
InputStream is = new ByteArrayInputStream(dataSource);
|
InputStream is = new ByteArrayInputStream(dataSource);
|
||||||
|
@ -22,6 +22,7 @@ import java.awt.Component;
|
|||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import org.openide.nodes.Node;
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
import org.sleuthkit.autopsy.datamodel.ContentNode;
|
import org.sleuthkit.autopsy.datamodel.ContentNode;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||||
@ -268,10 +269,14 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC
|
|||||||
@Override
|
@Override
|
||||||
public void setNode(ContentNode selectedNode) {
|
public void setNode(ContentNode selectedNode) {
|
||||||
if (selectedNode != null) {
|
if (selectedNode != null) {
|
||||||
this.setDataView(selectedNode.getContent(), 0, false);
|
Content content = ((Node) selectedNode).getLookup().lookup(Content.class);
|
||||||
} else {
|
if (content != null) {
|
||||||
this.setDataView(null, 0, true);
|
this.setDataView(content, 0, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setDataView(null, 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -49,7 +49,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
|
|
||||||
private transient ExplorerManager em = new ExplorerManager();
|
private transient ExplorerManager em = new ExplorerManager();
|
||||||
private String firstColumnLabel = "Name";
|
private String firstColumnLabel = "Name";
|
||||||
private boolean isImageNode;
|
|
||||||
|
|
||||||
/** Creates new form DataResultViewerTable */
|
/** Creates new form DataResultViewerTable */
|
||||||
public DataResultViewerTable() {
|
public DataResultViewerTable() {
|
||||||
@ -59,11 +58,10 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
|
|
||||||
// only allow one item to be selected at a time
|
// only allow one item to be selected at a time
|
||||||
ov.getOutline().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
ov.getOutline().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
|
||||||
// don't show the root node
|
// don't show the root node
|
||||||
ov.getOutline().setRootVisible(false);
|
ov.getOutline().setRootVisible(false);
|
||||||
|
|
||||||
this.isImageNode = false;
|
|
||||||
this.em.addPropertyChangeListener(this);
|
this.em.addPropertyChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,13 +96,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
private void tableScrollPanelComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_tableScrollPanelComponentResized
|
private void tableScrollPanelComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_tableScrollPanelComponentResized
|
||||||
if (this.tableScrollPanel.getWidth() < 700 && isImageNode) {
|
|
||||||
((OutlineView) this.tableScrollPanel).getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
|
||||||
} else {
|
|
||||||
if (isImageNode) {
|
|
||||||
((OutlineView) this.tableScrollPanel).getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}//GEN-LAST:event_tableScrollPanelComponentResized
|
}//GEN-LAST:event_tableScrollPanelComponentResized
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JScrollPane tableScrollPanel;
|
private javax.swing.JScrollPane tableScrollPanel;
|
||||||
@ -211,53 +202,47 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
|
|
||||||
|
|
||||||
// show the horizontal scroll panel and show all the content & header
|
// show the horizontal scroll panel and show all the content & header
|
||||||
if (!(selectedNode.getContent() instanceof Image)) {
|
|
||||||
this.isImageNode = false;
|
|
||||||
int totalColumns = props.length;
|
|
||||||
|
|
||||||
//int scrollWidth = ttv.getWidth();
|
int totalColumns = props.length;
|
||||||
int scrollWidth = ov.getWidth();
|
|
||||||
int minWidth = scrollWidth / totalColumns;
|
|
||||||
int margin = 4;
|
|
||||||
int startColumn = 1;
|
|
||||||
ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
|
||||||
|
|
||||||
// get the fontmetrics
|
//int scrollWidth = ttv.getWidth();
|
||||||
//FontMetrics metrics = ttv.getGraphics().getFontMetrics();
|
int scrollWidth = ov.getWidth();
|
||||||
FontMetrics metrics = ov.getGraphics().getFontMetrics();
|
int minWidth = scrollWidth / totalColumns;
|
||||||
|
int margin = 4;
|
||||||
|
int startColumn = 1;
|
||||||
|
ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
||||||
|
|
||||||
// get first 100 rows values for the table
|
// get the fontmetrics
|
||||||
Object[][] content = null;
|
//FontMetrics metrics = ttv.getGraphics().getFontMetrics();
|
||||||
try {
|
FontMetrics metrics = ov.getGraphics().getFontMetrics();
|
||||||
content = selectedNode.getRowValues(100);
|
|
||||||
} catch (SQLException ex) {
|
// get first 100 rows values for the table
|
||||||
// TODO: potential exception is being ignored (see below), should be handled
|
Object[][] content = null;
|
||||||
|
try {
|
||||||
|
content = selectedNode.getRowValues(100);
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
// TODO: potential exception is being ignored (see below), should be handled
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (content != null) {
|
||||||
|
// for the "Name" column
|
||||||
|
int nodeColWidth = getMaxColumnWidth(0, metrics, margin, 40, firstColumnLabel, content); // Note: 40 is the width of the icon + node lines. Change this value if those values change!
|
||||||
|
ov.getOutline().getColumnModel().getColumn(0).setPreferredWidth(nodeColWidth);
|
||||||
|
|
||||||
|
// get the max for each other column
|
||||||
|
for (int colIndex = startColumn; colIndex < totalColumns; colIndex++) {
|
||||||
|
int colWidth = getMaxColumnWidth(colIndex, metrics, margin, 8, props, content);
|
||||||
|
ov.getOutline().getColumnModel().getColumn(colIndex).setPreferredWidth(colWidth);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there's no content just auto resize all columns
|
||||||
if (content != null) {
|
if (!(content.length > 0)) {
|
||||||
// for the "Name" column
|
// turn on the auto resize
|
||||||
int nodeColWidth = getMaxColumnWidth(0, metrics, margin, 40, firstColumnLabel, content); // Note: 40 is the width of the icon + node lines. Change this value if those values change!
|
|
||||||
ov.getOutline().getColumnModel().getColumn(0).setPreferredWidth(nodeColWidth);
|
|
||||||
|
|
||||||
// get the max for each other column
|
|
||||||
for (int colIndex = startColumn; colIndex < totalColumns; colIndex++) {
|
|
||||||
int colWidth = getMaxColumnWidth(colIndex, metrics, margin, 8, props, content);
|
|
||||||
ov.getOutline().getColumnModel().getColumn(colIndex).setPreferredWidth(colWidth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there's no content just auto resize all columns
|
|
||||||
if (!(content.length > 0)) {
|
|
||||||
// turn on the auto resize
|
|
||||||
ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.isImageNode = true;
|
|
||||||
// turn on the auto resize for image result
|
|
||||||
ov.getOutline().getColumnModel().getColumn(0).setPreferredWidth(175);
|
|
||||||
ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
|
ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Node emptyNode = new AbstractNode(Children.LEAF);
|
Node emptyNode = new AbstractNode(Children.LEAF);
|
||||||
em.setRootContext(emptyNode); // make empty node
|
em.setRootContext(emptyNode); // make empty node
|
||||||
|
@ -90,16 +90,6 @@ abstract class AbstractContentNode<T extends Content> extends AbstractNode imple
|
|||||||
return content.read(offset, len);
|
return content.read(offset, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the content of this node.
|
|
||||||
*
|
|
||||||
* @return content the content of this node (can be image, volume, directory, or file)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Content getContent() {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final ShortNameVisitor shortName = new ShortNameVisitor();
|
private static final ShortNameVisitor shortName = new ShortNameVisitor();
|
||||||
|
|
||||||
private static final GetPathVisitor getDisplayPath = new GetPathVisitor(shortName);
|
private static final GetPathVisitor getDisplayPath = new GetPathVisitor(shortName);
|
||||||
|
@ -22,7 +22,6 @@ import java.sql.SQLException;
|
|||||||
import org.openide.nodes.FilterNode;
|
import org.openide.nodes.FilterNode;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.openide.util.Lookup;
|
import org.openide.util.Lookup;
|
||||||
import org.sleuthkit.datamodel.Content;
|
|
||||||
|
|
||||||
public class ContentFilterNode extends FilterNode implements ContentNode {
|
public class ContentFilterNode extends FilterNode implements ContentNode {
|
||||||
|
|
||||||
@ -43,11 +42,6 @@ public class ContentFilterNode extends FilterNode implements ContentNode {
|
|||||||
return ((ContentNode) super.getOriginal()).getRowValues(rows);
|
return ((ContentNode) super.getOriginal()).getRowValues(rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Content getContent() {
|
|
||||||
return ((ContentNode) super.getOriginal()).getContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getDisplayPath() {
|
public String[] getDisplayPath() {
|
||||||
return ((ContentNode) super.getOriginal()).getDisplayPath();
|
return ((ContentNode) super.getOriginal()).getDisplayPath();
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
package org.sleuthkit.autopsy.datamodel;
|
package org.sleuthkit.autopsy.datamodel;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import org.sleuthkit.datamodel.Content;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface class that all Data nodes inherit from.
|
* Interface class that all Data nodes inherit from.
|
||||||
@ -38,13 +37,6 @@ public interface ContentNode {
|
|||||||
*/
|
*/
|
||||||
public Object[][] getRowValues(int rows) throws SQLException;
|
public Object[][] getRowValues(int rows) throws SQLException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the content of this node.
|
|
||||||
*
|
|
||||||
* @return content the content of this node (can be image, volume, directory, or file)
|
|
||||||
*/
|
|
||||||
public Content getContent();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns full path to this node.
|
* Returns full path to this node.
|
||||||
*
|
*
|
||||||
|
@ -74,27 +74,34 @@ public class DataResultFilterNode extends FilterNode implements ContentNode {
|
|||||||
|
|
||||||
List<Action> actions = new ArrayList<Action>();
|
List<Action> actions = new ArrayList<Action>();
|
||||||
|
|
||||||
|
// TODO: ContentNode fix - restore right-click actions
|
||||||
|
// TODO: ContentVisitor instead of instanceof
|
||||||
|
|
||||||
|
Content nodeContent = this.currentNode.getLookup().lookup(Content.class);
|
||||||
|
|
||||||
|
|
||||||
// right click action(s) for image node
|
// right click action(s) for image node
|
||||||
if (this.currentNode instanceof ImageNode) {
|
if (this.currentNode instanceof ImageNode) {
|
||||||
actions.add(new NewWindowViewAction("View in New Window", (ImageNode) this.currentNode));
|
actions.add(new NewWindowViewAction("View in New Window", (ImageNode) this.currentNode));
|
||||||
actions.addAll(ShowDetailActionVisitor.getActions(((ImageNode) this.currentNode).getContent()));
|
actions.addAll(ShowDetailActionVisitor.getActions(nodeContent));
|
||||||
} // right click action(s) for volume node
|
} // right click action(s) for volume node
|
||||||
else if (this.currentNode instanceof VolumeNode) {
|
else if (this.currentNode instanceof VolumeNode) {
|
||||||
actions.add(new NewWindowViewAction("View in New Window", (VolumeNode) this.currentNode));
|
actions.add(new NewWindowViewAction("View in New Window", (VolumeNode) this.currentNode));
|
||||||
//new ShowDetailActionVisitor("Volume Details", this.currentNode.getName(), (VolumeNode) this.currentNode),
|
//new ShowDetailActionVisitor("Volume Details", this.currentNode.getName(), (VolumeNode) this.currentNode),
|
||||||
actions.addAll(ShowDetailActionVisitor.getActions(((VolumeNode) this.currentNode).getContent()));
|
actions.addAll(ShowDetailActionVisitor.getActions(nodeContent));
|
||||||
actions.add(new ChangeViewAction("View", 0, (ContentNode) currentNode));
|
actions.add(new ChangeViewAction("View", 0, (ContentNode) currentNode));
|
||||||
} // right click action(s) for directory node
|
} // right click action(s) for directory node
|
||||||
else if (this.currentNode instanceof DirectoryNode) {
|
else if (this.currentNode instanceof DirectoryNode) {
|
||||||
actions.add(new NewWindowViewAction("View in New Window", (DirectoryNode) this.currentNode));
|
actions.add(new NewWindowViewAction("View in New Window", (DirectoryNode) this.currentNode));
|
||||||
actions.add(new ChangeViewAction("View", 0, (ContentNode) currentNode));
|
actions.add(new ChangeViewAction("View", 0, (ContentNode) currentNode));
|
||||||
actions.add(new ExtractAction("Extract Directory", (DirectoryNode) this.currentNode));
|
// TODO: ContentNode fix - reimplement ExtractAction
|
||||||
|
//actions.add(new ExtractAction("Extract Directory", (DirectoryNode) this.currentNode));
|
||||||
} // right click action(s) for the file node
|
} // right click action(s) for the file node
|
||||||
else if (this.currentNode instanceof FileNode) {
|
else if (this.currentNode instanceof FileNode) {
|
||||||
actions.add(new ExternalViewerAction("Open File in External Viewer", (FileNode) this.currentNode));
|
actions.add(new ExternalViewerAction("Open File in External Viewer", (FileNode) this.currentNode));
|
||||||
actions.add(new NewWindowViewAction("View in New Window", (FileNode) this.currentNode));
|
actions.add(new NewWindowViewAction("View in New Window", (FileNode) this.currentNode));
|
||||||
actions.add(new ExtractAction("Extract", (FileNode) this.currentNode));
|
// TODO: ContentNode fix - reimplement ExtractAction
|
||||||
|
//actions.add(new ExtractAction("Extract", (FileNode) this.currentNode));
|
||||||
actions.add(new ChangeViewAction("View", 0, (ContentNode) currentNode));
|
actions.add(new ChangeViewAction("View", 0, (ContentNode) currentNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,11 +201,6 @@ public class DataResultFilterNode extends FilterNode implements ContentNode {
|
|||||||
return ((ContentNode) currentNode).getRowValues(rows);
|
return ((ContentNode) currentNode).getRowValues(rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Content getContent() {
|
|
||||||
return ((ContentNode) currentNode).getContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getDisplayPath() {
|
public String[] getDisplayPath() {
|
||||||
return ((ContentNode) currentNode).getDisplayPath();
|
return ((ContentNode) currentNode).getDisplayPath();
|
||||||
|
@ -46,11 +46,14 @@ class DirectoryTreeFilterChildren extends FilterNode.Children {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Node[] createNodes(Node arg0) {
|
protected Node[] createNodes(Node arg0) {
|
||||||
|
|
||||||
|
//TODO: ContentNode fix - replace with ContentVisitor
|
||||||
|
|
||||||
// filter out the FileNode and the "." and ".." directories
|
// filter out the FileNode and the "." and ".." directories
|
||||||
if (arg0 != null && (arg0 instanceof ImageNode
|
if (arg0 != null && (arg0 instanceof ImageNode
|
||||||
|| arg0 instanceof VolumeNode || (arg0 instanceof DirectoryNode
|
|| arg0 instanceof VolumeNode || (arg0 instanceof DirectoryNode
|
||||||
&& !((Directory) ((DirectoryNode) arg0).getContent()).getName().equals(".")
|
&& !((DirectoryNode) arg0).getDisplayName().equals("."))
|
||||||
&& !((Directory) ((DirectoryNode) arg0).getContent()).getName().equals("..")))) {
|
&& !((DirectoryNode) arg0).getDisplayName().equals(".."))) {
|
||||||
return new Node[]{this.copyNode(arg0)};
|
return new Node[]{this.copyNode(arg0)};
|
||||||
} else {
|
} else {
|
||||||
return new Node[]{};
|
return new Node[]{};
|
||||||
|
@ -30,8 +30,8 @@ import javax.swing.JOptionPane;
|
|||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.datamodel.FileNode;
|
|
||||||
import org.sleuthkit.autopsy.logging.Log;
|
import org.sleuthkit.autopsy.logging.Log;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.TskException;
|
import org.sleuthkit.datamodel.TskException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,7 +41,7 @@ import org.sleuthkit.datamodel.TskException;
|
|||||||
public class ExternalViewerAction extends AbstractAction {
|
public class ExternalViewerAction extends AbstractAction {
|
||||||
|
|
||||||
private byte[] content;
|
private byte[] content;
|
||||||
private FileNode fileNode;
|
private Content contentObject;
|
||||||
private String fileName;
|
private String fileName;
|
||||||
private String extension;
|
private String extension;
|
||||||
// for error handling
|
// for error handling
|
||||||
@ -49,12 +49,12 @@ public class ExternalViewerAction extends AbstractAction {
|
|||||||
private String className = this.getClass().toString();
|
private String className = this.getClass().toString();
|
||||||
|
|
||||||
/** the constructor */
|
/** the constructor */
|
||||||
public ExternalViewerAction(String title, FileNode fileNode) {
|
public ExternalViewerAction(String title, Node fileNode) {
|
||||||
super(title);
|
super(title);
|
||||||
this.fileNode = fileNode;
|
this.contentObject = fileNode.getLookup().lookup(Content.class);
|
||||||
|
|
||||||
long size = fileNode.getContent().getSize();
|
long size = contentObject.getSize();
|
||||||
String fullFileName = ((Node)fileNode).getDisplayName();
|
String fullFileName = fileNode.getDisplayName();
|
||||||
if (fullFileName.contains(".") && size > 0) {
|
if (fullFileName.contains(".") && size > 0) {
|
||||||
String tempFileName = fullFileName.substring(0, fullFileName.indexOf("."));
|
String tempFileName = fullFileName.substring(0, fullFileName.indexOf("."));
|
||||||
String tempExtension = fullFileName.substring(fullFileName.indexOf("."));
|
String tempExtension = fullFileName.substring(fullFileName.indexOf("."));
|
||||||
@ -63,7 +63,7 @@ public class ExternalViewerAction extends AbstractAction {
|
|||||||
} else {
|
} else {
|
||||||
this.fileName = fullFileName;
|
this.fileName = fullFileName;
|
||||||
this.extension = "";
|
this.extension = "";
|
||||||
this.setEnabled(false); // fix this later (right now only extract a file with extension)
|
this.setEnabled(false); //TODO: fix this later (right now only extract a file with extension)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ public class ExternalViewerAction extends AbstractAction {
|
|||||||
// the menu should be disabled if we can't read the content (for example: on zero-sized file).
|
// the menu should be disabled if we can't read the content (for example: on zero-sized file).
|
||||||
// Therefore, it should never throw the TSKException.
|
// Therefore, it should never throw the TSKException.
|
||||||
try {
|
try {
|
||||||
this.content = fileNode.getContent().read(0, fileNode.getContent().getSize());
|
this.content = contentObject.read(0, contentObject.getSize());
|
||||||
} catch (TskException ex) {
|
} catch (TskException ex) {
|
||||||
Logger.getLogger(this.className).log(Level.WARNING, "Error: can't read the content of the file.", ex);
|
Logger.getLogger(this.className).log(Level.WARNING, "Error: can't read the content of the file.", ex);
|
||||||
}
|
}
|
||||||
|
@ -1,218 +1,225 @@
|
|||||||
/*
|
// TODO: ContentNode fix - reimplement ExtractAction
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2011 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.directorytree;
|
|
||||||
|
|
||||||
import org.sleuthkit.autopsy.datamodel.FileNode;
|
|
||||||
import java.io.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import javax.swing.JFileChooser;
|
|
||||||
import java.io.File;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.filechooser.FileFilter;
|
|
||||||
import org.openide.nodes.Node;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.GeneralFilter;
|
|
||||||
import org.sleuthkit.autopsy.datamodel.ContentNode;
|
|
||||||
import org.sleuthkit.autopsy.datamodel.DirectoryNode;
|
|
||||||
import org.sleuthkit.autopsy.logging.Log;
|
|
||||||
import org.sleuthkit.datamodel.TskException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is an action class to extract and save the bytes given as a file.
|
|
||||||
*
|
|
||||||
* @author jantonius
|
|
||||||
*/
|
|
||||||
public final class ExtractAction extends AbstractAction {
|
|
||||||
|
|
||||||
private JFileChooser fc = new JFileChooser();
|
|
||||||
private byte[] source;
|
|
||||||
private ContentNode contentNode;
|
|
||||||
private String fileName;
|
|
||||||
private String extension;
|
|
||||||
// for error handling
|
|
||||||
private JPanel caller;
|
|
||||||
private String className = this.getClass().toString();
|
|
||||||
|
|
||||||
/** the constructor */
|
|
||||||
public ExtractAction(String title, ContentNode contentNode) {
|
|
||||||
super(title);
|
|
||||||
|
|
||||||
String fullFileName = ((Node)contentNode).getDisplayName();
|
|
||||||
|
|
||||||
if (fullFileName.equals(".")) {
|
|
||||||
// . folders are not associated with their children in the database,
|
|
||||||
// so get original
|
|
||||||
Node parentNode = ((Node) contentNode).getParentNode();
|
|
||||||
this.contentNode = (ContentNode) parentNode;
|
|
||||||
fullFileName = parentNode.getDisplayName();
|
|
||||||
} else {
|
|
||||||
this.contentNode = contentNode;
|
|
||||||
}
|
|
||||||
long size = contentNode.getContent().getSize();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
///*
|
||||||
* Checks first if the the selected it file or directory. If it's a file,
|
// * Autopsy Forensic Browser
|
||||||
* check if the file size is bigger than 0. If it's a directory, check
|
// *
|
||||||
* if it's not referring to the parent directory. Disables the menu otherwise.
|
// * Copyright 2011 Basis Technology Corp.
|
||||||
*/
|
// * Contact: carrier <at> sleuthkit <dot> org
|
||||||
if ((contentNode instanceof FileNode && size > 0) || (contentNode instanceof DirectoryNode && !fullFileName.equals(".."))) {
|
// *
|
||||||
if (contentNode instanceof FileNode && fullFileName.contains(".")) {
|
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
String tempFileName = fullFileName.substring(0, fullFileName.indexOf("."));
|
// * you may not use this file except in compliance with the License.
|
||||||
String tempExtension = fullFileName.substring(fullFileName.indexOf("."));
|
// * You may obtain a copy of the License at
|
||||||
this.fileName = tempFileName;
|
// *
|
||||||
this.extension = tempExtension;
|
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||||
} else {
|
// *
|
||||||
this.fileName = fullFileName;
|
// * Unless required by applicable law or agreed to in writing, software
|
||||||
this.extension = "";
|
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
}
|
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
} else {
|
// * See the License for the specific language governing permissions and
|
||||||
this.fileName = fullFileName;
|
// * limitations under the License.
|
||||||
this.extension = "";
|
// */
|
||||||
this.setEnabled(false); // can't extract zero-sized file or ".." directory
|
//package org.sleuthkit.autopsy.directorytree;
|
||||||
}
|
//
|
||||||
|
//import org.sleuthkit.autopsy.datamodel.FileNode;
|
||||||
}
|
//import java.io.*;
|
||||||
|
//import java.awt.event.ActionEvent;
|
||||||
/**
|
//import javax.swing.JFileChooser;
|
||||||
* Converts and saves the bytes into the file.
|
//import java.io.File;
|
||||||
*
|
//import java.awt.Component;
|
||||||
* @param e the action event
|
//import java.util.logging.Level;
|
||||||
*/
|
//import java.util.logging.Logger;
|
||||||
@Override
|
//import javax.swing.AbstractAction;
|
||||||
public void actionPerformed(ActionEvent e) {
|
//import javax.swing.JPanel;
|
||||||
Log.noteAction(this.getClass());
|
//import javax.swing.filechooser.FileFilter;
|
||||||
|
//import org.openide.nodes.Node;
|
||||||
// set the filter for FileNode
|
//import org.sleuthkit.autopsy.casemodule.GeneralFilter;
|
||||||
if (contentNode instanceof FileNode && !extension.equals("")) {
|
//import org.sleuthkit.autopsy.datamodel.DirectoryNode;
|
||||||
//FileFilter filter = new ExtensionFileFilter(extension.substring(1).toUpperCase() + " File (*" + extension + ")", new String[]{extension.substring(1)});
|
//import org.sleuthkit.autopsy.logging.Log;
|
||||||
String[] fileExt = {extension};
|
//import org.sleuthkit.datamodel.Content;
|
||||||
FileFilter filter = new GeneralFilter(fileExt, extension.substring(1).toUpperCase() + " File (*" + extension + ")", false);
|
//import org.sleuthkit.datamodel.TskException;
|
||||||
fc.setFileFilter(filter);
|
//
|
||||||
}
|
///**
|
||||||
|
// * This is an action class to extract and save the bytes given as a file.
|
||||||
|
// *
|
||||||
fc.setSelectedFile(new File(this.fileName));
|
// * @author jantonius
|
||||||
|
// */
|
||||||
int returnValue = fc.showSaveDialog((Component) e.getSource());
|
//public final class ExtractAction extends AbstractAction {
|
||||||
if (returnValue == JFileChooser.APPROVE_OPTION) {
|
//
|
||||||
String path = fc.getSelectedFile().getPath() + extension;
|
// private JFileChooser fc = new JFileChooser();
|
||||||
|
// private byte[] source;
|
||||||
try {
|
// private Content content;
|
||||||
// file extraction
|
// private String fileName;
|
||||||
if (contentNode instanceof FileNode) {
|
// private String extension;
|
||||||
extractFile(path, (FileNode) contentNode);
|
// // for error handling
|
||||||
}
|
// private JPanel caller;
|
||||||
|
// private String className = this.getClass().toString();
|
||||||
// directory extraction
|
//
|
||||||
if (contentNode instanceof DirectoryNode) {
|
// /** the constructor */
|
||||||
extractDirectory(path, (DirectoryNode) contentNode);
|
// public ExtractAction(String title, Node contentNode) {
|
||||||
}
|
// super(title);
|
||||||
} catch (Exception ex) {
|
//
|
||||||
Logger.getLogger(this.className).log(Level.WARNING, "Error: Couldn't extract file/directory.", ex);
|
// String fullFileName = contentNode.getDisplayName();
|
||||||
}
|
//
|
||||||
|
//
|
||||||
}
|
// if (fullFileName.equals(".")) {
|
||||||
|
// // . folders are not associated with their children in the database,
|
||||||
}
|
// // so get original
|
||||||
|
// Node parentNode = contentNode.getParentNode();
|
||||||
/**
|
// contentNode = parentNode;
|
||||||
* Extracts the content of the given fileNode into the given path.
|
// fullFileName = parentNode.getDisplayName();
|
||||||
*
|
// }
|
||||||
* @param givenPath the path to extract the file
|
//
|
||||||
* @param fileNode the file node that contain the file
|
//
|
||||||
*/
|
// this.content = contentNode.getLookup().lookup(Content.class);
|
||||||
private void extractFile(String givenPath, FileNode fileNode) throws Exception {
|
//
|
||||||
try {
|
// long size = content.getSize();
|
||||||
if (fileNode.getContent().getSize() > 0) {
|
//
|
||||||
try {
|
//
|
||||||
this.source = fileNode.getContent().read(0, fileNode.getContent().getSize());
|
//
|
||||||
} catch (TskException ex) {
|
// /**
|
||||||
throw new Exception("Error: can't read the content of the file.", ex);
|
// * Checks first if the the selected it file or directory. If it's a file,
|
||||||
}
|
// * check if the file size is bigger than 0. If it's a directory, check
|
||||||
} else {
|
// * if it's not referring to the parent directory. Disables the menu otherwise.
|
||||||
this.source = new byte[0];
|
// */
|
||||||
}
|
// if ((contentNode instanceof FileNode && size > 0) || (contentNode instanceof DirectoryNode && !fullFileName.equals(".."))) {
|
||||||
|
// if (contentNode instanceof FileNode && fullFileName.contains(".")) {
|
||||||
String path = givenPath;
|
// String tempFileName = fullFileName.substring(0, fullFileName.indexOf("."));
|
||||||
|
// String tempExtension = fullFileName.substring(fullFileName.indexOf("."));
|
||||||
File file = new File(path);
|
// this.fileName = tempFileName;
|
||||||
if (file.exists()) {
|
// this.extension = tempExtension;
|
||||||
file.delete();
|
// } else {
|
||||||
}
|
// this.fileName = fullFileName;
|
||||||
file.createNewFile();
|
// this.extension = "";
|
||||||
// convert char to byte
|
// }
|
||||||
byte[] dataSource = new byte[source.length];
|
// } else {
|
||||||
for (int i = 0; i < source.length; i++) {
|
// this.fileName = fullFileName;
|
||||||
dataSource[i] = (byte) source[i];
|
// this.extension = "";
|
||||||
}
|
// this.setEnabled(false); // can't extract zero-sized file or ".." directory
|
||||||
FileOutputStream fos = new FileOutputStream(file);
|
// }
|
||||||
//fos.write(dataSource);
|
//
|
||||||
fos.write(dataSource);
|
// }
|
||||||
fos.close();
|
//
|
||||||
} catch (IOException ex) {
|
// /**
|
||||||
throw new Exception("Error while trying to extract the file.", ex);
|
// * Converts and saves the bytes into the file.
|
||||||
}
|
// *
|
||||||
}
|
// * @param e the action event
|
||||||
|
// */
|
||||||
/**
|
// @Override
|
||||||
* Extracts the content of the given directoryNode into the given path.
|
// public void actionPerformed(ActionEvent e) {
|
||||||
*
|
// Log.noteAction(this.getClass());
|
||||||
* @param givenPath the path to extract the directory
|
//
|
||||||
* @param dirNode the directory node that contain the directory
|
// // set the filter for File
|
||||||
*/
|
// if (content instanceof org.sleuthkit.datamodel.File && !extension.equals("")) {
|
||||||
private void extractDirectory(String givenPath, DirectoryNode dirNode) throws Exception {
|
// //FileFilter filter = new ExtensionFileFilter(extension.substring(1).toUpperCase() + " File (*" + extension + ")", new String[]{extension.substring(1)});
|
||||||
String path = givenPath;
|
// String[] fileExt = {extension};
|
||||||
File dir = new File(path);
|
// FileFilter filter = new GeneralFilter(fileExt, extension.substring(1).toUpperCase() + " File (*" + extension + ")", false);
|
||||||
if (!dir.exists()) {
|
// fc.setFileFilter(filter);
|
||||||
dir.mkdir();
|
// }
|
||||||
}
|
//
|
||||||
|
// fc.setSelectedFile(new File(this.fileName));
|
||||||
int totalChildren = dirNode.getChildren().getNodesCount();
|
//
|
||||||
for (int i = 0; i < totalChildren; i++) {
|
// int returnValue = fc.showSaveDialog((Component) e.getSource());
|
||||||
Node childNode = dirNode.getChildren().getNodeAt(i);
|
// if (returnValue == JFileChooser.APPROVE_OPTION) {
|
||||||
|
// String path = fc.getSelectedFile().getPath() + extension;
|
||||||
if (childNode instanceof FileNode) {
|
//
|
||||||
FileNode fileNode = (FileNode) childNode;
|
//
|
||||||
String tempPath = path + File.separator + ((Node)fileNode).getDisplayName();
|
// // TODO: convert this to a Content Visitor
|
||||||
try {
|
// try {
|
||||||
extractFile(tempPath, fileNode);
|
// // file extraction
|
||||||
} catch (Exception ex) {
|
// if (content instanceof org.sleuthkit.datamodel.File) {
|
||||||
throw ex;
|
// extractFile(path, (org.sleuthkit.datamodel.File) content);
|
||||||
}
|
// }
|
||||||
}
|
//
|
||||||
|
// // directory extraction
|
||||||
if (childNode instanceof DirectoryNode) {
|
// if (content instanceof org.sleuthkit.datamodel.File) {
|
||||||
DirectoryNode dirNode2 = (DirectoryNode) childNode;
|
// extractDirectory(path, (org.sleuthkit.datamodel.Directory) content);
|
||||||
String dirNode2Name = ((Node)dirNode2).getDisplayName();
|
// }
|
||||||
|
// } catch (Exception ex) {
|
||||||
if (!dirNode2Name.trim().equals(".") && !dirNode2Name.trim().equals("..")) {
|
// Logger.getLogger(this.className).log(Level.WARNING, "Error: Couldn't extract file/directory.", ex);
|
||||||
String tempPath = path + File.separator + dirNode2Name;
|
// }
|
||||||
extractDirectory(tempPath, dirNode2);
|
//
|
||||||
}
|
// }
|
||||||
}
|
//
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
}
|
// * Extracts the content of the given fileNode into the given path.
|
||||||
}
|
// *
|
||||||
|
// * @param givenPath the path to extract the file
|
||||||
|
// * @param file the file node that contain the file
|
||||||
|
// */
|
||||||
|
// private void extractFile(String givenPath, org.sleuthkit.datamodel.File file) throws Exception {
|
||||||
|
// try {
|
||||||
|
// if (file.getSize() > 0) {
|
||||||
|
// try {
|
||||||
|
// this.source = file.read(0, file.getSize());
|
||||||
|
// } catch (TskException ex) {
|
||||||
|
// throw new Exception("Error: can't read the content of the file.", ex);
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// this.source = new byte[0];
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// String path = givenPath;
|
||||||
|
//
|
||||||
|
// File outputFile = new File(path);
|
||||||
|
// if (outputFile.exists()) {
|
||||||
|
// outputFile.delete();
|
||||||
|
// }
|
||||||
|
// outputFile.createNewFile();
|
||||||
|
// // convert char to byte
|
||||||
|
// byte[] dataSource = new byte[source.length];
|
||||||
|
// for (int i = 0; i < source.length; i++) {
|
||||||
|
// dataSource[i] = (byte) source[i];
|
||||||
|
// }
|
||||||
|
// FileOutputStream fos = new FileOutputStream(outputFile);
|
||||||
|
// //fos.write(dataSource);
|
||||||
|
// fos.write(dataSource);
|
||||||
|
// fos.close();
|
||||||
|
// } catch (IOException ex) {
|
||||||
|
// throw new Exception("Error while trying to extract the file.", ex);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Extracts the content of the given directoryNode into the given path.
|
||||||
|
// *
|
||||||
|
// * @param givenPath the path to extract the directory
|
||||||
|
// * @param dirNode the directory node that contain the directory
|
||||||
|
// */
|
||||||
|
// private void extractDirectory(String givenPath, DirectoryNode dirNode) throws Exception {
|
||||||
|
// String path = givenPath;
|
||||||
|
// File dir = new File(path);
|
||||||
|
// if (!dir.exists()) {
|
||||||
|
// dir.mkdir();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// int totalChildren = dirNode.getChildren().getNodesCount();
|
||||||
|
// for (int i = 0; i < totalChildren; i++) {
|
||||||
|
// Node childNode = dirNode.getChildren().getNodeAt(i);
|
||||||
|
//
|
||||||
|
// if (childNode instanceof FileNode) {
|
||||||
|
// FileNode fileNode = (FileNode) childNode;
|
||||||
|
// String tempPath = path + File.separator + ((Node)fileNode).getDisplayName();
|
||||||
|
// try {
|
||||||
|
// extractFile(tempPath, fileNode);
|
||||||
|
// } catch (Exception ex) {
|
||||||
|
// throw ex;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (childNode instanceof DirectoryNode) {
|
||||||
|
// DirectoryNode dirNode2 = (DirectoryNode) childNode;
|
||||||
|
// String dirNode2Name = ((Node)dirNode2).getDisplayName();
|
||||||
|
//
|
||||||
|
// if (!dirNode2Name.trim().equals(".") && !dirNode2Name.trim().equals("..")) {
|
||||||
|
// String tempPath = path + File.separator + dirNode2Name;
|
||||||
|
// extractDirectory(tempPath, dirNode2);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
@ -28,7 +28,6 @@ import java.sql.SQLException;
|
|||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import org.openide.nodes.FilterNode;
|
import org.openide.nodes.FilterNode;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.sleuthkit.datamodel.Content;
|
|
||||||
import org.sleuthkit.autopsy.directorytree.ChangeViewAction;
|
import org.sleuthkit.autopsy.directorytree.ChangeViewAction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,7 +73,8 @@ public class DataResultFilterNode extends FilterNode implements ContentNode {
|
|||||||
} // right click action(s) for the file node
|
} // right click action(s) for the file node
|
||||||
else if (this.currentNode instanceof FileNode) {
|
else if (this.currentNode instanceof FileNode) {
|
||||||
return new Action[]{
|
return new Action[]{
|
||||||
new ExtractAction("Extract", (FileNode) this.currentNode),
|
// TODO: ContentNode fix - reimplement ExtractAction
|
||||||
|
// new ExtractAction("Extract", (FileNode) this.currentNode),
|
||||||
new ChangeViewAction("View", 0, (ContentNode) currentNode),
|
new ChangeViewAction("View", 0, (ContentNode) currentNode),
|
||||||
new OpenParentFolderAction("Open Parent Directory", ((ContentNode) currentNode).getSystemPath())
|
new OpenParentFolderAction("Open Parent Directory", ((ContentNode) currentNode).getSystemPath())
|
||||||
};
|
};
|
||||||
@ -99,11 +99,6 @@ public class DataResultFilterNode extends FilterNode implements ContentNode {
|
|||||||
return ((ContentNode) currentNode).getRowValues(rows);
|
return ((ContentNode) currentNode).getRowValues(rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Content getContent() {
|
|
||||||
return ((ContentNode) currentNode).getContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getDisplayPath() {
|
public String[] getDisplayPath() {
|
||||||
return ((ContentNode) currentNode).getDisplayPath();
|
return ((ContentNode) currentNode).getDisplayPath();
|
||||||
|
@ -77,12 +77,7 @@ class SearchNode extends AbstractNode implements ContentNode {
|
|||||||
}
|
}
|
||||||
return objs;
|
return objs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Content getContent() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getDisplayPath() {
|
public String[] getDisplayPath() {
|
||||||
return new String[]{"KeyWord Search Result:"};
|
return new String[]{"KeyWord Search Result:"};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user