Merge branch 'develop' of github.com:sleuthkit/autopsy into develop

This commit is contained in:
Brian Carrier 2014-11-03 10:59:43 -05:00
commit 978d642286
174 changed files with 5823 additions and 2167 deletions

View File

@ -1,4 +1,4 @@
Last Updated: March 23, 2013
Last Updated: Sept 17, 2014
This file outlines what it takes to build Autopsy from source.
@ -100,22 +100,29 @@ BACKGROUND:
Here are some notes to shed some light on what is going on during
the build process.
- The Sleuth Kit Java datamodel JAR file has native libraries
that are copied into it.
- The Sleuth Kit Java datamodel JAR file has native JNI libraries
that are copied into it. These JNI libraries have dependencies on
libewf and zlib. On non-Windows platforms, the JNI library also has
a dependency on libtsk (on Windows, it is compiled into libtsk_jni).
- NetBeans uses ant to build Autopsy. The build target copies the
TSK datamodel JAR file into the project. If you want to use the
debug version of the TSK dll, then there is a different ant target
in TSK to copy the debug versions of the dlls.
in the TSK datamodel to copy the debug versions of the dlls.
- On a Windows system, the ant target copies all needed libraries
to the autopsy folder. On a Unix system, the ant taget copies only
the JNI library and then relies on the other libraries (libtsk,
libewf, zilb, etc.) to be installed on the system in their standard
locations (i.e. /usr/local).
- On a Windows system, the compile-time ant target copies the
dependency libraries into the Autopsy code structure so that they can
be found when Autopsy is run and packaged. At run-time, the native
library inside of the JAR file will be extracted and used.
- On a Unix system, the ant taget copies only the JNI library and
then relies on the other libraries (libtsk, libewf, zilb, etc.) to
be installed on the system in their standard locations (i.e.
/usr/local).
- Everytime that you do a source code update of TSK, make sure you
rebuild both the dll and the JAR file.
rebuild both the libtsk_dll, the JAR file, and then rebuild Autopsy
so that it copies the latest data model JAR file.
---------------

View File

@ -196,6 +196,7 @@
<package>org.sleuthkit.autopsy.filesearch</package>
<package>org.sleuthkit.autopsy.ingest</package>
<package>org.sleuthkit.autopsy.menuactions</package>
<package>org.sleuthkit.autopsy.modules.filetypeid</package>
<package>org.sleuthkit.autopsy.modules.hashdatabase</package>
<package>org.sleuthkit.autopsy.report</package>
<package>org.sleuthkit.datamodel</package>

Binary file not shown.

View File

@ -0,0 +1,4 @@
TestDisk and PhotoRec are written and maintained by Christophe GRENIER <grenier@cgsecurity.org>
TestDisk logos has been created by Simone Brandt and by Dmitri Zdorov in 2001,
PhotoRec logo by Marcel Bruins in 2006.

View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -0,0 +1,3 @@
TestDisk & PhotoRec , http://www.cgsecurity.org
Copyright (C) 1998-2014 Christophe GRENIER <grenier@cgsecurity.org>

View File

@ -0,0 +1,390 @@
Current news
== 7.0-WIP ==
== 6.14 ==
* The log file generated by the Windows version (cygwin) reports bad sectors in a more readable fashion, example
<code>ReadFile Data error (cyclic redundancy check)</code>.
* As openssl isn't used, don't link with this cryptographic library (Debian tries to avoid mixing GPL code and openssl)
=== TestDisk ===
==== Improvements ====
* <code>testdisk /list</code> now displays the disk model, serial number, firmware version and hpa or dco presence if detected
* Recover WBFS (Wii Backup File System) partition
* Make FAT RebuildBS works when there is a single FAT table
* Interface: Display the partition table type if autodetected
* Interface: modified warning about mismatching geometry between FAT or NTFS boot sector and HD geometry information ([http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=651756 Debian #651756])
* Interface: Remove "Allow partial last cylinder" option
==== Bug fixes ====
* Fix crc in EFI backup GPT
* Rewrote how TestDisk aligns partition on cylinder or 1MB boundary. It avoids to create partition entry where the partition ends after the end of the disk.
=== PhotoRec ===
==== Improvements ====
* Improve Olympus .orf recovery
* Improve WP Mac/WP5/WP6 Corel Documents .wpd files recovery
* Fix thumbs.db recovery, avoid some false positive with .doc
* Interface: if less than 10 file families are enabled, display the results even if zero has been found yet
New file formats:
* .aep After Effects
* .axx AxCrypt
* .dp Designer, a Photobook Designer Software
* .lzh archive
* .mmap MindManager
* .plt Gerber Graphix Advantage
* .prproj Adobe Premiere project
* .psb Adobe Photoshop Image
* .pts PTGui, panoramic stitching software
* .qcp The QCP File Format and Media Types for Speech Data (RFC3625)
* .shn Shorten audio file
* .snt Windows Sticky Notes
* .ttd TinyTag Data
* .wallet Armory bitcoin wallet
* .wim Windows imaging (WIM) image
==== Bug fixes ====
* Fix an endless loop during .caf file recovery
* Fix tiff recovery including some raw file formats, 64-bit version wasn't affected
== 6.13 ==
Fix UAC manifests for Windows, so users don't need to use right-click "Run As Administrator"
TestDisk
- Fix image creation, image.dd file wasn't created (Regression introduced in 6.12)
- Detect Vmware VMFS partition
- Locate lost GFS2 partition but not yet the size
- Log HDD serial number and firmware revision
- List NTFS Alternate Data Streams (ADS)
PhotoRec
- Session recovery restarts at the previous location
- Better MPEG recovery, there should be less concatenated videos.
- Better JPG recovery, there should be less cases where thumbnails were recovered instead of the picture itself.
- Handle large avi files using "AVIX" or mov files using 64-bit chunk size.
- Rename recovered pdf using the title (not perfect)
- Major cleanup of PhotoRec core code
6.12
fidentify, a little utility sharing PhotoRec signature database, is now build by default. It identifies the type of data contained in a file and reports the extension as seen by PhotoRec. It is similar to the Unix file command. Add compatibility with
- libewf 20110312
- ncurses 5.8
- ntfs-3g
Fix detection of Encase 6 .ewf files
TestDisk
- Convert the directory name when it can't be created (Fix for Windows/Cygwin version)
- Better HPA/DCO detection: handle the case where native_max is null.
- Image Creation is now faster than previous version when there are bad sectors
- List and copy (experimental) files from exFAT filesystem
- Improved NTFS undelete interface
PhotoRec
- PhotoRec checks for EFBIG (file too large) error when writing files. It's usefull to avoid erronous message about "no free space available" when recovering to a FAT filesystem.
- Recover files from exFAT unallocated space
- Use doc/xls/ppt title to name recovered Office document, use first filename in zip archive...
- Possibility to add your own extension/custom signature to PhotoRec
- Generated a report.xml file using Digital Forensics XML
6.11
TestDisk & PhotoRec 6.11 should use less CPU.
This new TestDisk version can undelete files for NTFS filesystem and recover deleted exFAT and ext4.
PhotoRec
- Performance improvement when scanning for numerous file type
- Fix several bugs including an endless loop, several memory leaks and several out-of-bound memory access
- 50 file formats have been added
6.10
TestDisk & PhotoRec 6.10 comes with severals improvements:
- Report disk manufacturer and model under Windows and Linux (Only Linux was
supported in 6.9)
- Under Linux, /dev/mapper/* and /dev/md? are now listed with the harddisks.
- Now both OS and compiler versions are recorded in the log file.
This new TestDisk version can
- undelete files and directories for FAT filesystem,
- undelete files for ext2 filesystem,
- copy files from ext2/ext3 partitions. These feature was already available
for FAT and NTFS.
PhotoRec
- Load and save FileOpts settings: remember which file types to recover
- For JPEG files, extract the time/date from Exif header and set the
file time. It's now easier to sort the recovered jpg.
- PhotoRec can now identify and recover 38 additional file formats.
6.9
TestDisk & PhotoRec 6.9 comes with numerous improvements:
- They are compatible with ntfsprogs 2.0, the latest library version for
accessing NTFS partitions, e2fsprogs 1.40.6, the latest library for accessing
ext2/ext3 partitions.
- EFI GUID Partition Table is now supported. EFI GPT is mainly used on Itanium,
MacBook and Mac Pro.
- Both utilities can use sudo if the user is not root, this functionnality will
be enabled for at least MacOSX, so users won't have to go into a command line.
- Improved Windows disk support, most internal USB card reader should now work.
- Disk model (ie. ATA ST3120026AS, _NEC DVD_RW ND-4550A...) are now reported
under Linux.
TestDisk
- New file system support has been added: encrypted LUKS, Mac HFSX, Linux Raid
md 1.0/1.1/1.2 (0.9 was already supported).
- It displays unicode filenames correctly, and can handle unicode filesnames
while copying files from an NTFS partition when supported by the underlying
libraries.
- It's now possible to copy files from a lost FAT partition found by TestDisk
(Already possible for NTFS).
- In the Advanced menu, TestDisk can create a raw/dd-like image of a partition.
PhotoRec
- For ext2/ext3 file systems, PhotoRec 6.9 can search in the whole space or in
unallocated space only. This feature was already available for FAT and NTFS
filesystem.
- It has better session support which allows a recovery to be stopped and
restarted later.
- New file formats are supported: Acronis True Image .TIB, AutoCAD's .DWG and
PowerTab .PTB, Cineon image file/SMTPE DPX .DPX, Comic Life .comicdoc, HP
Photosmart Photo Printing Album .albm, KeepAssX .KDB, Maya .MB and .MP,
Microsoft OneNOte .one file, Microsoft Visual Studio Resource file .RES,
Microsoft VB's .CLS, Outlook .MSG, QuickBook .FST, SketchUp .SKP, Vmware .vmdk,
WinSpec .SPE, Windows Enhanced MetaFile .emf, MS Windows Link .lnk, Internet
Explorer index.dat, Macintosh Picture .pct, SunPCI Disk Image,XBOX GTA San
Andreas Save File, Final Cut Pro .fcp and Digital Speech Standard .dss.
6.8
Version 6.8 is mainly a bugfix but some small improvements has been made to
both TestDisk and PhotoRec:
- Partition type is now autodetected.
- TestDisk and PhotoRec can now be used under screen, the screen manager with
VT100/ANSI terminal emulation.
TestDisk
HFS detection has been improved to avoid false positive.
TestDisk logs potential NTFS partition location from MFT & MFTMirr location
while rebuilding NTFS boot sector.
PhotoRec
Some important bugfixes (false positive problem, implementation bugs) have been
made in PhotoRec 6.8. JPG bruteforce recovery has been improved a little bit,
you can use it to recover more fragmented jpeg but it's very time consuming and
not 100% reliable, so it's still disabled by default.
.7z, .cab, .rar and .tar recovery have been improved, .dta and .spss file
formats have been added.
6.7
Both utilities are faster than previous versions: a better disk caching is
used in PhotoRec, a better way to handle the sector to scan is used in
TestDisk. Windows version of TestDisk and PhotoRec doesn't use
pread()/pwrite() cygwin implementation (A bug seems present).
TestDisk
TestDisk 6.7 handles partition created under Microsoft Vista.
Traditionally partitions were created on cylinder boundaries. Under Vista,
they are now independent of the disk geometry: partitions are aligned to 1
MB boundary, TestDisk now handles that. It also fixes how the advanced
menu works.
PhotoRec
PhotoRec 6.7 uses less CPU. It also adds support for 3ds max, Archive
.ace, CD Audio .cda, FastTrackerII Extended Module .xm, Linux archive .a,
Linux/Unix ELF binary, Mac OS .emlx mail format, Macomedia Compressed
Flash .swc, Macromedia .flv, Macromedia Freehand 5 (.fh5) & 10 (.fh10) and
InDesign .indd, Matroska .mkv, MP3 with ID3 header, MS cabinet archive
.cab, MSOffice "Open" XML .docx, .pptx, .xlsx, MS executable (PE), MS
Windows Metafile .wmf, NJStar .njx, Quickbooks .qbb and .qbw, Real Audio
.rm, registry config file .reg, RPM package .rpm, Windows registry header
detection and Event Log .evt
6.6
General Improvements
- Encase Expert Witness Compression Format is now supported, so Computer
Forensic Experts can use TestDisk and PhotoRec more easily.
- Under new Vista OS, harddisks are now being reported again.
TestDisk
- If LBA48 support seems missing (HD bigger than 130 GB not supported by
the OS), TestDisk will warn the user. This problem is common when a user
reinstalls his OS and forgets to install the latest service patches.
- NTFS: TestDisk should be able to list files from NTFS partitions even if
some filesystem corruption has occurred; more importantly, it will allow
the user to copy whatever file data it can as well.
- FAT: Improved heuristics to find the first FAT area during boot sector
rebuild.
PhotoRec
- A new method for handling fragmented data is now used, making recovery
more reliable and faster.
- PhotoRec can be set to search for files in FAT16/FAT32 unallocated space
only; which avoids wasting time recovering files that are still
accessible, making the recovery of 'lost files' much faster and more
efficient.
- New file formats have been added: .3g2 (Mov video family), .aif Apple
Audio, .all .cpr Cubase Song, .blend Blender, .cam Casio QV Digital Camera
Image, .flac Free Lossless Audio Codec, .mdf Microsoft SQL, .swf
Macromedia Flash and .vcf VCard (not confused with .txt anymore)
6.5
TestDisk
- A screen has been added to control the log file creation.
- It's now possible from TestDisk file listing to copy files from NTFS
partition to a selected directory.
- NTFS MFT can also be repaired in more cases.
- A compilation problem has been fixed with old version of libntfs.
- Documentation has been fixed, it now displays correctly with IE.
PhotoRec
- New file formats have been added: AppleWorks .cwk, DIF Digital Video .dv,
DjVu .djv, Finale .mus, Incredimail .imm, .imb, iTunes mhbd, MIDI .mid,
MS Backup, Real Media .rm & .ram, Reason .rns, ruby .rb, .xml
- File size detection for .bmp, .pdf, .gif, Office document has been improved.
- A endless loop bug and a memory leak have been fixed.
- The I/O cache engine now caches read failure, it will speedup some recovery.
6.4
Support for ReiserFS 4 has been added in TestDisk
PhotoRec detects new files formats and better recovery of jpeg, txt/html, zip...
Fixes numerous bugs.
6.3
Fix for 64 bits architecture and improved interface.
Photorec adds support for Quicken, OpenDocument, mp3, ogg,
StarOffice, Encapsulated PostScript, PostScript, gif, gz, mov file format
or better detection.
6.2
New User Interface
Disk cache and read ahead to improve performance
Endianess portability fix
6.1
TestDisk
Fix for Mac version
Remove an erroneous message after writing a partition without logical partition
6.0
TestDisk
Fix for Dos version and some cosmetic change
5.9
TestDisk
- User Interface has been improved
- Mac partition map is now supported
- FAT: better check for directory attribut
- FAT: fix a bug in expert mode
- FAT: fix directory listing time using timezone
- Linux Raid: fix regression from 5.8, Raid 1 is again detected.
- NTFS boot sector: copy boot sector over backup boot sector now works
Photorec
- Photorec now works on ext2/ext3 filesystem (Check Options)
- add support for Papyrus, 7zip, text file, OGG audio files, RAR archive recovery
- add a default file size limit of 2GB
- add support for ZIP files starting with PK00 (packed to removable disk)
5.8
TestDisk
- Add support for HFS+, UFS2 filesystem and Intel Solaris superblock
- Linux Raid: Add some code to detect Raid 5 earlier
- FAT: Update the cleaning FAT function to repair FAT table (Expert mode only)
- EXT2: e2fsprogs-1.36 parses the device name given in ext2fs_open, give a string instead of the io_channel.
- NTFS: Fix a bug in NTFS rebuilding introduced in 5.7
- Fix a memory freed problem when detecting if a partition can be Primary, Logical...
Photorec
- Recognize php header
- MPG format is using streaming
5.7
TestDisk
- Replace standard MBR i386 boot sector code by a GPL one
- HFS detection has been improved to avoid false positive
- FAT: add support for one FAT only (instead of usual 2) in directory listing
and boot sector rebuilding (need expert mode)
- FAT: Support for FAT without FAT12 (ie DOS 3.30), FAT16 or FAT32 mark.
- FAT12/16: Add the possibility to initialize FAT root directory (Delete everything,
Expert mode only)
- FAT: fix for directory listing in boot sector rebuilding.
- FAT32: Fix for last FAT sector while cleaning the FAT (Expert mode only)
- EXT2/EXT3: while listing filename permit to list file where inode information is unavaible.
- Doesn't halt if TestDisk can't create the log file
- configure.ac, compile.sh: remove --enable-debug option
- Modify Linux RPM spec file to get non-empty debug-info rpm
- Directory listing: little UI modification
- Dos version: fix read/write error message
Photorec 5.7
- add support for a bunch of other file format
- incremental directory name for recovery
- a lot of code cleaning
5.6
TestDisk
New features:
- Can list files from NTFS partition found using backup boot sector
- Display a warning if TestDisk think the logical geometry (CHS) is wrong
- Handle filesystem image (In Options, Partition type: None)
- Filesystem: HFS (minimal testing) and LVM2 support
- NetBSD and FreeBSD support
Improvements:
- Win32: Windows version is now as fast as other version.
Using FileRead() instead of read() is about 10 times faster.
Fixs:
- HD Geometry: the number of cylinders can be bigger than 65535
- BSD slice: display content even if crc is wrong
- FAT boot sector rebuilding: better support for FAT16 converted to FAT32
- NTFS boot sector rebuilding: better handle copy of MFT
- Win32: Windows version always creates a log file (was introduced in 5.3 but
boggus since 5.4)
Photorec 5.6
- Use libjpeg for a better recovery of lost jpeg files.
- Konica/Minolta raw (MRW) pictures recovery
- Canon raw (CRW) pictures recovery
- Minimal support for Sigma/Foveon: .X3F, Rollei (RDC), Fuji (RAF)
5.5
Fix FAT32 recovery using backup boot sector
Remove some debugging code
Upgrade to libntfs 1.9.4
Fix RPM spec file to get a working reiserfs support
5.4
New features:
- Option to backup unknown partition header.
- Save/Load current partition list
- handle sector size != 512
- XFS and CramFS support
Improvements:
- Interface has been improved.
- log libreiserfs errors in the log file
- can choose to minimize or maximize the extended partition before writing
- FAT32 root cluster rebuild improved (use first free cluster, mark it as used or/and EOC)
Fixs:
- ReiserFS and EXT2/EXT3 directory listing fix
- fix a bug with BeFS partition recovery
5.3
New features:
- When rebuilding FAT boot sector in expert mode, lets the user choose FAT locations.
Improvements:
- Exit if TestDisk can't create log file.
- Windows version always create log file.
Bug corrections:
- Fix superblock number displayed when telling the user to use e2fsck.
- Fix extended partition creation.
5.2
TestDisk now compiles and run under NT 4 and Windows 2000.
New features:
- can rebuild NTFS boot sector
- can recover JFS partition
- some advanced FAT32 functions have been added (Expert mode only)
Improvements:
- Can align partition to cylinder boundary or to head boundary.
- Doesn't abort while writing partitions if read failed.
- Doesn't let the user write an empty partition table.

View File

@ -0,0 +1,6 @@
TestDisk & PhotoRec are mainly written by Christophe GRENIER.
Many people further contributed to TestDisk, directly or indirectly, by
reporting problems, helping with the documentation, suggesting various
improvements, sending me gifts using my Amazon whish-list...
Thanks to the thousands of people who have provided support for the project!

View File

@ -0,0 +1 @@
7.0-WIP Sun Oct 5 21:22:27 CEST 2014

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,12 @@
<html>
<head>
<title>TestDisk &amp; PhotoRec documentation</title>
</head>
<body>
TestDisk &amp; PhotoRec documentation can be found online:
<ul>
<li><a href="http://www.cgsecurity.org/wiki/TestDisk">TestDisk</a></li>
<li><a href="http://www.cgsecurity.org/wiki/PhotoRec">PhotoRec</a></li>
</ul>
</body>
</html>

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,143 @@
@echo off &MODE CON: COLS=75 LINES=20 &color 1e
:: All my thanks to hilander999 and Siegfried for their help
:: Original code from hilander999
: edit and modified to fit this plugin by Xtreme
SETLOCAL ENABLEEXTENSIONS
cd /d %~dp0
set INFname=Testdisk.inf
::SET NOW=2
::SET TOPBOX=?
::SET MIDBOX= ????????????????????????????????????????????????????????????????????????¸
::SET NUMROW= 0%% 25%% 50%% 75%% 100%%
echo ===================================== > "%cd%\File_Grabber.log"
echo Testdisk and PhotoRec 6.14 Plugin >> File_Grabber.log
echo Plugin by: Xtreme (Ahmed Hossam) >> File_Grabber.log
echo Collector by: Xtreme >> File_Grabber.log
echo Plugin for: Windows Xpire Rd CD >> File_Grabber.log
echo Website: http://xtreme.boot-land.net >> File_Grabber.log
echo -mirror: http://xtremee.orgfree.com >> File_Grabber.log
echo Copyright © Windows Xpire Tech Center. All rights reserved. >> File_Grabber.log
echo ===================================== >> File_Grabber.log
IF NOT EXIST cd ..\..\63\cygwin GOTO :error1
cls&CALL :BRANDH
echo Please wait till Grabber finish Testdisk and PhotoRec grabbing process...
IF NOT EXIST "%cd%\TestDisk_PE\files" md "TestDisk_PE\files"
IF NOT EXIST "%cd%\TestDisk_PE\files\63" md "TestDisk_PE\files\63"
echo.&echo -Grabbing file: Copy Testdisk files... &FOR %%A IN (
..\..\cygwin1.dll
..\..\photorec_win.exe
..\..\testdisk_win.exe
..\..\fidentify_win.exe
)DO (ECHO. Grabbing file:%%~A >>"%cd%\File_Grabber.log"
Copy /Y "%%~A" TestDisk_PE\files >NUL
if errorlevel 1 (SET ERRORLEVEL=1&echo. *ERROR: File_Grabber can't find %%~A >>"%cd%\File_Grabber.log")
)
FOR %%B IN (
..\..\63\cygwin
)DO (ECHO. Grabbing file:%%~B >>"%cd%\File_Grabber.log"
Copy /Y "%%~B" TestDisk_PE\files\63 >NUL
if errorlevel 1 (SET ERRORLEVEL=1&echo. *ERROR: File_Grabber can't find %%~B >>"%cd%\File_Grabber.log")
)
CALL :PROGRESS
::pause
MODE CON: COLS=76 LINES=23
IF "%ERRORLEVEL%"=="1" (GOTO :error2)else goto :done
GOTO :END
:PROGRESS
::SET /A NOW+=1
::IF %NOW% LEQ 2 GOTO :EOF
cls&CALL :BRANDH
::echo.&echo. File: %~1&echo.&echo.%TOPBOX%&echo.%MIDBOX%&echo.%NUMROW%
::SET TOPBOX=%TOPBOX%U
::SET NOW=1
copy SCRIPTS\StaticINF.dat TestDisk_PE\"%INFname%"
copy testdisk_nu2menu.xml TestDisk_PE\
if exist start.inf del start.inf
GOTO :EOF
:error1
MODE CON: COLS=78 LINES=28 &COLOR 4F &cls
echo.&echo. >> File_Grabber.log
echo TestDisk and PhotoRes can't be localized on your system >> File_Grabber.log
echo. >> File_Grabber.log
echo You can download TestDisk and PhotoRes from here >> File_Grabber.log
echo.&echo -TestDisk Official website: >> File_Grabber.log
echo http://www.cgsecurity.org/wiki/TestDisk_Download/ >> File_Grabber.log
echo. >> File_Grabber.log
echo For help you can check Help.html >> File_Grabber.log
CALL :BRANDH
echo.&echo. TestDisk and PhotoRes can't be localized on your system
echo.&echo. You need to download TestDisk and DON'T change the download folders structure then try again
echo.&echo. You can download TestDisk and PhotoRes from here
echo.&echo. -TestDisk Official website:
echo http://www.cgsecurity.org/wiki/TestDisk_Download/&echo.
CALL :BRAND2
GOTO :END
:error2
cls&COLOR 4F
CALL :BRANDH
echo.&echo One or more required files was not found.
echo.&echo. Check the log for details...&echo. "%cd%\File_Grabber.log"&echo.
CALL :BRAND2
pause
IF EXIST %systemroot%\system32\notepad.exe (start %systemroot%\system32\notepad.exe "%cd%\File_Grabber.log")
endlocal
exit
:done
cls&CALL :BRANDH
echo.&echo. TestDisk and PhotoRes Files have been succesfully collected
echo.&echo. You can now start build your PE version &echo.
CALL :BRAND2
GOTO :END
:BRANDH
MODE CON: COLS=75 LINES=35 &color 1e
echo.
ECHO ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
ECHO º º
ECHO º TestDisk and PhotoRes plugin Files Collector º
ECHO º º
ECHO º Plugin by Xtreme ( Xtremesony_xp@yahoo.com ) º
ECHO º º
ECHO ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
echo.
GOTO :EOF
:BRAND2
echo. - For more help and update you Can join us in our group
echo. http://groups.yahoo.com/group/Windows-Xpire/
echo.
echo. - For more BartPE Plugin go to our website:
echo. http://xtreme.boot-land.net
echo. (mirror) http://xtremee.orgfree.com
echo.
echo. - Send all comments about plugin to Xtreme Xtremesony_xp@yahoo.com
echo.
echo.
ECHO ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
ECHO º Copyright (C) Windows Xpire Tech Center º
ECHO ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
GOTO :EOF
:END
ENDLOCAL
echo.
PAUSE&EXIT

View File

@ -0,0 +1,69 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Help| TestDisk</title>
<style type="text/css">
<!--
.style10 {font-family: Tahoma; font-size: 10px; }
.style11 {font-family: Verdana, Arial, Helvetica, sans-serif}
-->
</style>
</head>
<body>
<i>PE Builder v3 plugin</i>
<hr>
<h1 align="center"><strong>TestDisk and PhotoRec </strong><br>
</h1>
<hr>
<h4 class="style11">Overview</h4>
<div>
<p class="style11"><a name="Description"></a><strong>TestDisk</strong> is a <em>powerful</em> free data recovery software! It was primarily designed to help recover lost partitions and/or make non-booting disks bootable again <em>when</em> these symptoms are caused by <em>faulty software</em>, certain types of <em>viruses</em> or <em>human error</em> (such as <em>accidentally</em> deleting a Partition Table). Partition table recovery using TestDisk is really easy. </p>
<p class="style11"><a name="Description"></a><strong>PhotoRec</strong> is file data recovery software designed to recover lost files including video, documents and archives from Hard Disks and CDRom and lost pictures (thus, its 'Photo Recovery' name) from digital camera memory. PhotoRec ignores the filesystem and goes after the underlying data, so it will still work even if your media's filesystem has been severely damaged or re-formatted.</p>
<h4 class="style11">System Requirements</h4>
<div class="style11">
<ul>
<li>BartPE v3.x and XPE (recommended).</li>
<li>Your VGA Card Driver (recommended). </li>
<li>Windows XP SP2 on your host OS. </li>
</ul>
</div>
<h4 class="style11">Instructions</h4>
<div>
<ul class="style11">
<li>1- Download TestDisk and PhotoRec from <a href="http://www.cgsecurity.org/wiki/TestDisk_Download">Here</a></li>
<li>2- Uncompress the download.</li>
<li>3- Run the <em>Get_Files.cmd</em> to collect the needed files.</li>
<li>4- Copy this folder to Plugin directory.</li>
<li>5- Start your build and enjoy !</li>
</ul>
<p class="style11"><strong>Support</strong></p>
<ul>
<li class="style11">Forum Plugin Support <a href="http://www.911cd.net/forums/index.php?showtopic=21139">Here</a> </li>
<li class="style11">More Plugins <a href="http://xtreme.boot-land.net/" set="yes" linkindex="3"><strong>Here</strong></a> (<a href="http://xtremee.orgfree.com/">mirror</a>). </li>
<li class="style11">You can <a href="mailto:Xtremesony_xp@yahoo.com">mail Xtreme</a> for any help about the plugin.</li>
<li class="style11">For help about TestDisk and PhotoRec, contact <a href="http://www.cgsecurity.org/wiki/Support">CG Security support</a>.</li>
</ul>
</div>
</div>
<div><div></div>
</div>
<div><div></div>
</div>
<div><div></div>
</div>
<div><div></div>
</div>
<div></div>
<div><div></div>
</div>
<div><div></div>
</div>
<div><div></div>
</div>
<p>&nbsp;</p>
<hr>
<p align="center" class="style10"> All trademarks mentioned on this page are the property of their respective owners </p>
<p align="center" class="style10">&copy; 2007-2008 Windows Xpire Tech Center <br>
</p>
</body>
</html>

View File

@ -0,0 +1,6 @@
@echo off
rd files /s /q
del File_Grabber.log
del *.inf
copy SCRIPTS\Start_INF.dat start.inf
pause

View File

@ -0,0 +1,19 @@
TestDisk and PhotoRec v6.14 Plugin
=====================================
Plugin by: Xtreme - Ahmed Hossam
Plugin for: Christophe GRENIER (www.cgsecurity.org)
Website: http://xtreme.boot-land.net
-mirror: http://xtremee.orgfree.com
=====================================
How to use the plugin ?
* 1- Run the Get_Files.cmd to collect the needed files.
* 2- Copy TestDisk_PE folder to Plugin directory.
* 3- Start your build and enjoy !
How to clean ?
* Delete the BUILD folder
-------------------------------------------------------------------------
Copyright © Windows Xpire Tech Center.
--> It is published under GNU Public License 2 or later.
-------------------------------------------------------------------------

View File

@ -0,0 +1,20 @@
; Testdisk.inf
; PE Builder v3 plugin INF file for Testdisk and PhotoRec 6.14
; Plugin by Ahmed Hossam/Xtreme <Xtremesony_xp@yahoo.com>
; Made For Christophe GRENIER (www.cgsecurity.org)
; © Windows Xpire Tech Center
; Windows Xpire Tech Center officially represented by Xtreme
; CodeName: Radw
[Version]
Signature= "$Windows NT$"
[PEBuilder]
Name="(Xtreme) Disk Tools: TestDisk and PhotoRec -Press CONFIG to Enable-"
Enable=1
Help=Help.htm
config=Get_Files.cmd
[SourceDisksFiles]
"please run autoHelp to configure your plugin"=a,,1

View File

@ -0,0 +1,36 @@
; Testdisk.inf
; PE Builder v3 plugin INF file for Testdisk and PhotoRec 6.14
; Plugin by Ahmed Hossam/Xtreme <Xtremesony_xp@yahoo.com>
; Made For Christophe GRENIER (www.cgsecurity.org)
; © Windows Xpire Tech Center
; Windows Xpire Tech Center is represented by Xtreme
; CodeName: Radw
[Version]
Signature= "$Windows NT$"
[PEBuilder]
Name="(Xtreme) Disk Tools: TestDisk and PhotoRec -Press CONFIG to Enable-"
Enable=1
Help=Help.htm
config=Get_Files.cmd
[WinntDirectories]
a="Programs\testdisk",2
b="Programs\testdisk\63",2
[SourceDisksFiles]
files\testdisk_win.exe=a,,1
files\photorec_win.exe=a,,1
files\fidentify_win.exe=a,,1
files\cygwin1.dll=a,,1
files\63\cygwin=b,,1
[Software.AddReg]
0x2, "Sherpya\XPEinit\Programs","Disk Tools\TestDisk - Fix MBR and Recover lost partitions","%systemdrive%\Programs\testdisk\testdisk_win.exe"
0x2, "Sherpya\XPEinit\Programs","Disk Tools\PhotoRec - Recover lost files (Doc, Pic and Video)","%systemdrive%\Programs\testdisk\photorec_win.exe"
0x2, "Sherpya\XPEinit\Programs","Disk Tools\files Identify","%systemdrive%\Programs\testdisk\fidentify_win.exe"
; Remove the comment ";" from the below lines if you are going to use NU2 menu
;[Append]
;nu2menu.xml, testdisk_nu2menu.xml

View File

@ -0,0 +1,20 @@
; Testdisk.inf
; PE Builder v3 plugin INF file for Testdisk and PhotoRec 6.14
; Plugin by Ahmed Hossam/Xtreme <Xtremesony_xp@yahoo.com>
; Made For Christophe GRENIER (www.cgsecurity.org)
; © Windows Xpire Tech Center
; Windows Xpire Tech Center officially represented by Xtreme
; CodeName: Radw
[Version]
Signature= "$Windows NT$"
[PEBuilder]
Name="(Xtreme) Disk Tools: TestDisk and PhotoRec -Press CONFIG to Enable-"
Enable=1
Help=Help.htm
config=Get_Files.cmd
[SourceDisksFiles]
"please run autoHelp to configure your plugin"=a,,1

View File

@ -0,0 +1,7 @@
<!-- Nu2Menu entry for " TestDisk and PhotoRec v6.14" -->
<NU2MENU>
<MENU ID="Programs">
<MITEM TYPE="ITEM" DISABLED="@Not(@FileExists(@GetProgramDrive()\Programs\testdisk\testdisk_win.exe))" CMD="RUN" FUNC="@GetProgramDrive()\Programs\testdisk\testdisk_win.exe" PARM="1">Testdisk - Fix MBR and Recover lost partitions</MITEM>
<MITEM TYPE="ITEM" DISABLED="@Not(@FileExists(@GetProgramDrive()\Programs\testdisk\photorec_win.exe))" CMD="RUN" FUNC="@GetProgramDrive()\Programs\testdisk\photorec_win.exe" PARM="1">PhotoRec - Recover lost files (Doc, Pic and Video)</MITEM>
</MENU>
</NU2MENU>

View File

@ -0,0 +1,66 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Help| TestDisk</title>
<style type="text/css">
<!--
.style10 {font-family: Tahoma; font-size: 10px; }
.style11 {font-family: Verdana, Arial, Helvetica, sans-serif}
-->
</style>
</head>
<body>
<i>WinBuilder v075b1 </i>
<hr>
<h1 align="center"><strong>TestDisk and PhotoRec </strong><br>
</h1>
<hr>
<h4 class="style11">Overview</h4>
<div>
<p class="style11"><a name="Description"></a><strong>TestDisk</strong> is a <em>powerful</em> free data recovery software! It was primarily designed to help recover lost partitions and/or make non-booting disks bootable again <em>when</em> these symptoms are caused by <em>faulty software</em>, certain types of <em>viruses</em> or <em>human error</em> (such as <em>accidentally</em> deleting a Partition Table). Partition table recovery using TestDisk is really easy. </p>
<p class="style11"><a name="Description"></a><strong>PhotoRec</strong> is file data recovery software designed to recover lost files including video, documents and archives from Hard Disks and CDRom and lost pictures (thus, its 'Photo Recovery' name) from digital camera memory. PhotoRec ignores the filesystem and goes after the underlying data, so it will still work even if your media's filesystem has been severely damaged or re-formatted.</p>
<h4 class="style11">System Requirements</h4>
<div class="style11">
<ul>
<li>WinBuilder v075 [beta1] from <a href="http://winbuilder.net/download.php?list.12">Here</a>.</li>
<li>Your VGA Card Driver (recommended). </li>
<li>Windows XP SP2 on your host OS.</li>
</ul>
</div>
<h4 class="style11">Instructions</h4>
<div>
<ul class="style11">
<li>1- Download TestDisk and PhotoRec from <a href="http://www.cgsecurity.org/wiki/TestDisk_Download">Here</a></li>
<li>2- Uncompress the download.</li>
<li>3- Copy TestDisk.script to the WinBuilder App project folder.</li>
<li>4- Start your build and enjoy !</li>
</ul>
<p class="style11"><strong>Support</strong></p>
<ul><li class="style11">More Plugins <a href="http://xtreme.boot-land.net/" set="yes" linkindex="3"><strong>Here</strong></a> (<a href="http://xtremee.orgfree.com/">mirror</a>). </li>
<li class="style11">You can <a href="mailto:Xtremesony_xp@yahoo.com">mail Xtreme</a> for any help about the plugin.</li>
<li class="style11">For help about TestDisk and PhotoRec, contact <a href="http://www.cgsecurity.org/wiki/Support">CG Security support</a>.</li>
</ul>
</div>
</div>
<div><div></div>
</div>
<div><div></div>
</div>
<div><div></div>
</div>
<div><div></div>
</div>
<div></div>
<div><div></div>
</div>
<div><div></div>
</div>
<div><div></div>
</div>
<p>&nbsp;</p>
<hr>
<p align="center" class="style10"> All trademarks mentioned on this page are the property of their respective owners </p>
<p align="center" class="style10">&copy; 2007-2008 Windows Xpire Tech Center <br>
</p>
</body>
</html>

View File

@ -0,0 +1,20 @@
TestDisk and PhotoRec v6.14 Plugin
=====================================
Plugin by: Xtreme - Ahmed Hossam
Plugin for: Christophe GRENIER (www.cgsecurity.org)
Website: http://xtreme.boot-land.net
-mirror: http://xtremee.orgfree.com
=====================================
How to use the plugin ?
* 1- Download WinBuilder v075 [beta_1]
From here: http://winbuilder.net/download.php?list.12
* 2- Copy TestDisk.script to the WinBuilder App project folder.
* 3- Start your build and enjoy !
How to clean ?
* Delete the BUILD folder
-------------------------------------------------------------------------
Copyright © Windows Xpire Tech Center.
--> It is published under GNU Public License 2 or later.
-------------------------------------------------------------------------

View File

@ -0,0 +1,52 @@
[main]
Title=TestDisk & PhotoRec 6.14
Description=A powerful free data recovery software utility.
Selected=True
Level=5
Version=1
Author=Xtreme
Contact=http://www.boot-land.net/forums/
Credits=Christophe GRENIER (http://www.cgsecurity.org)
Date=Friday, May 16,2008
[Interface]
pTextLabel1=" Thanks a lot for Galapo for his support and helping me learning WinBuilder Script.",1,1,19,25,463,54,8,Normal
pBevel1=pBevel1,1,12,12,18,455,63
[variables]
%ProgramTitle%=TestDisk
%ProgramEXE%=testdisk_win.exe
%ProgramFolder%=TestDisk
[process]
StrFormat,path,%scriptdir%,%one_folder_up%
StrFormat,CTrim,"%one_folder_up%","\",%one_folder_up%
StrFormat,path,%one_folder_up%,%two_folders_up%
StrFormat,CTrim,"%two_folders_up%","\",%two_folders_up%
If,NotExistFile,"%two_folders_up%\testdisk_win.exe",Run,%ScriptFile%,Halt
DirMake,"%Target_Prog%\%ProgramFolder%"
DirMake,"%Target_Prog%\%ProgramFolder%\63"
FileCopy,"%two_folders_up%\cygwin1.dll","%Target_Prog%\%ProgramFolder%"
FileCopy,"%two_folders_up%\fidentify_win.exe","%Target_Prog%\%ProgramFolder%"
FileCopy,"%two_folders_up%\photorec_win.exe","%Target_Prog%\%ProgramFolder%"
FileCopy,"%two_folders_up%\readme.txt","%Target_Prog%\%ProgramFolder%"
FileCopy,"%two_folders_up%\testdisk_win.exe","%Target_Prog%\%ProgramFolder%"
DirCopy,"%two_folders_up%\63\*.*","%Target_Prog%\%ProgramFolder%\63"
::DirCopy,"%two_folders_up%\TestDisk\*.*","%Target_Prog%\%ProgramFolder%"
Add_Shortcut,StartMenu,"%TestDisk & PhotoRec"
Add_Shortcut,StartMenu,"TestDisk & PhotoRec","%PE_Programs%\%ProgramFolder%\photorec_win.exe","PhotoRec"
Add_Shortcut,StartMenu,"TestDisk & PhotoRec","%PE_Programs%\%ProgramFolder%\testdisk_win.exe","Testdisk"
Add_Shortcut,StartMenu,"TestDisk & PhotoRec","%PE_Programs%\%ProgramFolder%\fidentify_win.exe","Files Identify"
[Halt]
Message,"testdisk_win.exe could not be located in the following folder: '%two_folders_up%'. Program will not be available in the PE.",Error
Exit,""
[EncodedFile-AuthorEncoded-testdisklogo-clear-100.gif]
lines=0
0=eJy1lfs/0wsDgL+b7y5mZi5jLtnmFhmfqcj9HRVa6SA0utBoNF3EEpMajUnurYhNbqtFLk3rLXLe704lFCWXI0V0wpxUqJC30857/oj3+e358fnp8d/h5+p2KBaIBYbVgFqtXllZmZ2dnZiY6O/v7+zsVIx0NvUrpFIpBEGiTrFIJBJ05gsEgvT09ARFekxMTKQiISQkhCGNZDAYblKGk5OTnciNInWjUChEsRORSMSLKGgxBRATAREe+D9D+Q7CAEAToP4j/zTRkWpIkBhANSnqrk/qOep+jfHCD5BFtozeSnn68TjCFP/VTgqHX8zh9bEtnx1sAQaaG40yNzA9j19pjD+FiIAhUoS5HHQaEzyWyjuUcXYP/wgnMSzngliSx8wsBtIvaZJD9LQQV/fnhxdWnY/MlMjDEbja+h1kQznGiGT+8NHjrifdD0km2Jaq51q3KrXbS7eRDcJQZr+Zk9aBMN2JnumZbpIOTwt3agCYqtvPTRk+cx0fRhjvIRGax7olpgHzGjmeMBWVmbqjDwC0bQPutFYzHvLyU66WVheRlFlqOV41WVLtIzDS0fYl35buBOscX9lbZAN8mNL7z6Gb9MFN6i0LRw0ZaevrFCEPNkgoa20393D8PlMkDEVD2MYbcs54r6Lx95Zo5UGvpbi5wa1Yo6oMg1PzztI78w++h/Dn6u7vU91P+BAe8Oqe9tGIgQ7DgQ77QfayM2KlNDr1ufk+XbyxluPjXfVmuMToLOQJ6wHNP4J8YGawQYxlVpq1QVSFIX43ujN5FoVDjtRIS01kAXdL6rOivUwPPLdHJo14n/Y4LYbr2RdaHF11zkXGLc/9SJyqISBJ8a+9VYMLgWUO9nBHkx+R3oDOo03ngAUb5HdVe2WG32MYD6O3mY7gBCn8r3BPWsmiDYdu5owkHL+smBQY2MgGWES2rCaKEaZSTwn37ncdiUpxi7dg67osG8AXbQPo58XVrrV5F6iTEiPYe+ZbI2tD195gu4rbE+tuhTrRi5zHtqGDNx7czjJlWLBryRFYyO9SuNfNrhjNwAjJ4fm/bIfzfXtBTGmC7F2v2b+2RyDg9+muw+GmsUFsDZzniKmFx8Q7zYR/PzpCNjZ1nWQDSLMFOpjv0qvSQBGD3lcJJ79hKjj3NAEx87frsQA2Hj+R1HVGX1nQmmp8/Zp3Xqp2Aha9rWEHaRvUwCmVjm0oPrujWAh7k8pXgvJ+92Ja3yNud8G9NzU3rdO1OQykS8H7EhoKqOXLVo0/KE82zZWXgLh5W3pR+bdSNQo+c4bmY/g4LX1XE0ZuMF4pqf9ATxl3TM5EvYqsgWRLvK3mgKiZQgfw1TeqDqwt7TM9vSWpCwKtSnghablY+Pg5Awhktpt6culd+slBVsBk4C1cbNVXVNLO80w7SzRBm9zQtY7O4cMJ1JWCZM065zxQzyeIbq6Bz5lOrAvlK4dC9x77NsyMyziFYBiOa8IPfna3CTyUbW1V1dAwZhdmdX16+XXiE4eVN4lP3VgeR2bHczRiTqhW3vJRjquTgiLXL1O565tX3+l9kxwWkv121zQWXXOBpsvjmr/PiE9/+j57Y/FPqwKex71lcb2HFAPTQLe2Fjbr0Okw2kyC+vT0C9+2XqHs0AaXnEibV7NCkJzmHqJ8qpCbt1kuuIx2nAjuLmPBr61dttdXiaRNwbwzdj0ti/NfcX4kFuPtL3q/2u4d+nXEAnswLT2sp+1E7Jy2Z+PetQzwXPmmVS2sYT/BpnegiFvxkvBz82HpWwe/ncsa/M8GwXyMezYBc5X0Oh6U/dfrj4jKBiwLDOtEvQz45ceU76i3k+9Pio+9cTVdA/Lj73q2mxCddxWjjjXWM6FhZMXYbJPylstNiTu9uFN2Z3Rr+xMtPYPyWakBD/kgiwNz5o7rEnKysYyLRsbAEGBBLz1+oUuDgCY+WeqC4HmFNkMb9W4/Wy1Z2K+G3Gh7hmPQKrr8UclhW/vRB54LwKajt/W3mOyqmEFlJb0SFi61B0I5Q8iVvlZvy451/Q0QH5dKLs6iCjuIJbS54iLtmUrs2nurbJ/c8ySdaCiD7NZ4mBtoHV5uvpI05xXOWg8shuS4u7bpRO6eh7OJwMcJtv6s9mfRwIaaqIlNrWlhAA4JlTt9cM2cOqSEGcACFlmxklJlTNyXl88JSPydIEOriKR4ZndSbM92HAY0g9HB9+cdqGaApOarv9/+srrRMJr/Uj1gXn2hJP2TFjDm5k8YRgMIcGex6VbmE/xMqxnB4cB/8m2Tr/gEXzyuvw71ZYzxsy2ImBnK+vr6Pt8BvgC8EIca200LAn88DOHB/SvIqUUBMWtUOFZRiGq8wYdDg/8bRVFGqNFTzhwMj7xUyVvY1o9fGdpcjebAgwqPQTXZGY5CZ8ZfIAy3HmbtAPqSu6Gt2OhbXIGqwSbNWDdGGVzmChUvi65HyWGkEwV50XfTyuQVS3g19Zj7AJfg3Uij2UUuy8v7qOyO+8wbtkfEOmu1Uaxk5Mmry3dkgU2cx9JKoNlBlnBEaM/eqxwbW76rCGyc25zb7oFopTkKfFfPvhZN6mFwjYHNSWWVb3oeLPInRGJu3OIb6uWP7aN9zUzMk/FPvN8dfADA42/UG1J5eJyTKkktLknJLM7OyU/P103OSU0s0jU0MNBLz0xjGAXDH1xnh9Cr2LHLx92yk2ZgAgBOAAyvxhMHbwEAAAACAAAANgAAAKoHAAAAAAAAAQAAAAAAAAAAAAAA
[AuthorEncoded]
testdisklogo-clear-100.gif=2Kb,2Kb
Logo=testdisklogo-clear-100.gif

View File

@ -0,0 +1,23 @@
The Windows version of TestDisk & PhotoRec should work under
- Windows NT 4
- Windows 2000
- Windows XP
- Windows 2003
- Windows Vista
- Windows Server 2008
- Windows 7
On Windows 64-bit, WoW64 (Windows 32-bit On Windows 64-bit) is required to run
these 32-bit executables.
For Windows 64-bit without WoW64, use the Windows 64-bit version of TestDisk
& PhotoRec.
If you are using an older version of Windows, run the DOS version of TestDisk.
You can download it from http://www.cgsecurity.org/wiki/TestDisk_Download
TestDisk doesn't need to be installed, you only need to
- extract the files
- run testdisk_win.exe or photorec_win.exe
TestDisk & PhotoRec documentation can be found online:
- http://www.cgsecurity.org/wiki/TestDisk
- http://www.cgsecurity.org/wiki/PhotoRec

View File

@ -0,0 +1,25 @@
#!/usr/bin/env python
import os
import os.path
import shutil
import sys
source = sys.argv[1]
destination = sys.argv[2]
while not os.path.exists(source):
source = raw_input('Enter a valid source directory\n')
while not os.path.exists(destination):
destination = raw_input('Enter a valid destination directory\n')
for root, dirs, files in os.walk(source, topdown=False):
for file in files:
extension = os.path.splitext(file)[1][1:].upper()
destinationPath = os.path.join(destination,extension)
if not os.path.exists(destinationPath):
os.mkdir(destinationPath)
if os.path.exists(os.path.join(destinationPath,file)):
print 'WARNING: this file was not copied :' + os.path.join(root,file)
else:
shutil.copy2(os.path.join(root,file), destinationPath)

Binary file not shown.

View File

@ -18,7 +18,6 @@
*/
package org.sleuthkit.autopsy.casemodule;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -33,39 +32,37 @@ import org.sleuthkit.datamodel.Content;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Thread that will add logical files to database, and then kick-off ingest
* modules. Note: the add logical files task cannot currently be reverted as
* the add image task can. This is a separate task from AddImgTask because
* it is much simpler and does not require locks, since the underlying file
* manager methods acquire the locks for each transaction when adding
* logical files.
*/
class AddLocalFilesTask implements Runnable {
* Thread that will add logical files to database, and then kick-off ingest
* modules. Note: the add logical files task cannot currently be reverted as the
* add image task can. This is a separate task from AddImgTask because it is
* much simpler and does not require locks, since the underlying file manager
* methods acquire the locks for each transaction when adding logical files.
*/
class AddLocalFilesTask implements Runnable {
private final Logger logger = Logger.getLogger(AddLocalFilesTask.class.getName());
private final String dataSourcePath;
private final DataSourceProcessorProgressMonitor progressMonitor;
private final DataSourceProcessorCallback callbackObj;
private final Case currentCase;
// synchronization object for cancelRequested
private final Object lock = new Object();
private final Object lock = new Object();
// true if the process was requested to stop
private volatile boolean cancelRequested = false;
private boolean hasCritError = false;
private final List<String> errorList = new ArrayList<>();
private final List<Content> newContents = Collections.synchronizedList(new ArrayList<Content>());
private final List<Content> newContents = Collections.synchronizedList(new ArrayList<Content>());
public AddLocalFilesTask(String dataSourcePath, DataSourceProcessorProgressMonitor aProgressMonitor, DataSourceProcessorCallback cbObj) {
currentCase = Case.getCurrentCase();
this.dataSourcePath = dataSourcePath;
this.callbackObj = cbObj;
this.progressMonitor = aProgressMonitor;
@ -80,15 +77,15 @@ import org.sleuthkit.datamodel.TskCoreException;
*/
@Override
public void run() {
errorList.clear();
final LocalFilesAddProgressUpdater progUpdater = new LocalFilesAddProgressUpdater(progressMonitor);
try {
progressMonitor.setIndeterminate(true);
progressMonitor.setProgress(0);
final FileManager fileManager = currentCase.getServices().getFileManager();
String[] paths = dataSourcePath.split(LocalFilesPanel.FILES_SEP);
List<String> absLocalPaths = new ArrayList<>();
@ -100,72 +97,68 @@ import org.sleuthkit.datamodel.TskCoreException;
logger.log(Level.WARNING, "Errors occurred while running add logical files. ", ex); //NON-NLS
hasCritError = true;
errorList.add(ex.getMessage());
}
// handle done
}
// handle done
postProcess();
}
private void postProcess() {
if (cancelRequested() || hasCritError) {
logger.log(Level.WARNING, "Handling errors or interruption that occured in logical files process"); //NON-NLS
}
if (!errorList.isEmpty()) {
//data error (non-critical)
logger.log(Level.WARNING, "Handling non-critical errors that occured in logical files process"); //NON-NLS
//data error (non-critical)
logger.log(Level.WARNING, "Handling non-critical errors that occured in logical files process"); //NON-NLS
}
if (!(cancelRequested() || hasCritError)) {
progressMonitor.setProgress(100);
progressMonitor.setIndeterminate(false);
progressMonitor.setIndeterminate(false);
}
// invoke the callBack, unless the caller cancelled
if (!cancelRequested()) {
doCallBack();
}
}
/*
* Call the callback with results, new content, and errors, if any
*/
private void doCallBack()
{
DataSourceProcessorCallback.DataSourceProcessorResult result;
/*
* Call the callback with results, new content, and errors, if any
*/
private void doCallBack() {
DataSourceProcessorCallback.DataSourceProcessorResult result;
if (hasCritError) {
result = DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS;
}
else if (!errorList.isEmpty()) {
result = DataSourceProcessorCallback.DataSourceProcessorResult.NONCRITICAL_ERRORS;
}
else {
result = DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS;
}
if (hasCritError) {
result = DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS;
} else if (!errorList.isEmpty()) {
result = DataSourceProcessorCallback.DataSourceProcessorResult.NONCRITICAL_ERRORS;
} else {
result = DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS;
}
// invoke the callback, passing it the result, list of new contents, and list of errors
callbackObj.done(result, errorList, newContents);
}
/*
* cancel the files addition, if possible
*/
// invoke the callback, passing it the result, list of new contents, and list of errors
callbackObj.done(result, errorList, newContents);
}
/*
* cancel the files addition, if possible
*/
public void cancelTask() {
synchronized(lock) {
synchronized (lock) {
cancelRequested = true;
}
}
}
private boolean cancelRequested() {
synchronized (lock) {
return cancelRequested;
return cancelRequested;
}
}
/**
* Updates the wizard status with logical file/folder
*/
@ -173,18 +166,18 @@ import org.sleuthkit.datamodel.TskCoreException;
private int count = 0;
private final DataSourceProcessorProgressMonitor progressMonitor;
LocalFilesAddProgressUpdater(DataSourceProcessorProgressMonitor progressMonitor) {
this.progressMonitor = progressMonitor;
}
@Override
public void fileAdded(final AbstractFile newFile) {
if (count++ % 10 == 0) {
if (count++ % 10 == 0) {
progressMonitor.setProgressText(
NbBundle.getMessage(this.getClass(), "AddLocalFilesTask.localFileAdd.progress.text",
newFile.getParentPath(), newFile.getName()));
newFile.getParentPath(), newFile.getName()));
}
}
}

View File

@ -165,6 +165,7 @@ ImageFilePanel.moduleErr=Module Error
ImageFilePanel.moduleErr.msg=A module caused an error listening to ImageFilePanel updates. See log to determine which module. Some data could be incomplete.
LocalDiskDSProcessor.dsType.text=Local Disk
LocalDiskPanel.localDiskModel.loading.msg=Loading local disks...
LocalDiskPanel.localDiskModel.nodrives.msg=No Accessible Drives
LocalDiskPanel.moduleErr=Module Error
LocalDiskPanel.moduleErr.msg=A module caused an error listening to LocalDiskPanel updates. See log to determine which module. Some data could be incomplete.
LocalDiskPanel.errLabel.disksNotDetected.text=Disks were not detected. On some systems it requires admin privileges (or "Run as administrator").

View File

@ -16,13 +16,25 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="autopsyLogo" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="29" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="editorPanel" min="-2" max="-2" attributes="0"/>
<Component id="closeButton" min="-2" pref="73" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jSeparator1" min="-2" pref="5" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="newCaseButton" alignment="2" min="-2" max="-2" attributes="1"/>
<Component id="openRecentButton" alignment="2" min="-2" pref="70" max="-2" attributes="1"/>
<Component id="openCaseButton" alignment="2" min="-2" max="-2" attributes="1"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="103" alignment="1" groupAlignment="0" attributes="0">
<Component id="createNewLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="openRecentLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="openLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="closeButton" alignment="1" min="-2" pref="73" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -32,176 +44,34 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="autopsyLogo" alignment="0" min="-2" pref="257" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="editorPanel" min="-2" max="-2" attributes="1"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="2" attributes="0">
<Component id="newCaseButton" alignment="2" min="-2" pref="56" max="-2" attributes="0"/>
<Component id="createNewLabel" alignment="2" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="openRecentButton" alignment="2" min="-2" pref="70" max="-2" attributes="0"/>
<Component id="openRecentLabel" alignment="2" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="openCaseButton" alignment="2" min="-2" pref="58" max="-2" attributes="0"/>
<Component id="openLabel" alignment="2" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
<Component id="closeButton" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="jSeparator1" alignment="0" max="32767" attributes="0"/>
<Component id="autopsyLogo" alignment="0" min="-2" pref="257" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="closeButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.closeButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="editorPanel">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="newCaseButton" min="-2" max="-2" attributes="1"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="createNewLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="openRecentButton" min="-2" pref="70" max="-2" attributes="1"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="openRecentLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="openCaseButton" min="-2" max="-2" attributes="1"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="openLabel" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace pref="60" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="createNewLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="newCaseButton" alignment="2" min="-2" pref="56" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="openRecentLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="openRecentButton" alignment="2" min="-2" pref="70" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="openLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="openCaseButton" alignment="2" min="-2" pref="58" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="25" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="newCaseButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.newCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="null" type="code"/>
</Property>
<Property name="borderPainted" type="boolean" value="false"/>
<Property name="contentAreaFilled" type="boolean" value="false"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[70, 70]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newCaseButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="openRecentButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_open_recent.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openRecentButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="null" type="code"/>
</Property>
<Property name="borderPainted" type="boolean" value="false"/>
<Property name="contentAreaFilled" type="boolean" value="false"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[70, 70]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openRecentButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="createNewLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="13" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.createNewLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="openRecentLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="13" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openRecentLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="openCaseButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="null" type="code"/>
</Property>
<Property name="borderPainted" type="boolean" value="false"/>
<Property name="contentAreaFilled" type="boolean" value="false"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[1, 1, 1, 1]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[70, 70]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openCaseButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="openLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="13" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="autopsyLogo">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
@ -215,5 +85,113 @@
<AuxValue name="JavaCodeGenerator_CreateCodePost" type="java.lang.String" value="this.autopsyLogo.setText(&quot;&quot;);"/>
</AuxValues>
</Component>
<Component class="javax.swing.JButton" name="newCaseButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.newCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="null" type="code"/>
</Property>
<Property name="borderPainted" type="boolean" value="false"/>
<Property name="contentAreaFilled" type="boolean" value="false"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[64, 64]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newCaseButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="openRecentButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_open_recent.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openRecentButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="null" type="code"/>
</Property>
<Property name="borderPainted" type="boolean" value="false"/>
<Property name="contentAreaFilled" type="boolean" value="false"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[64, 64]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openRecentButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="createNewLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="13" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.createNewLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="openRecentLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="13" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openRecentLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="openCaseButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="null" type="code"/>
</Property>
<Property name="borderPainted" type="boolean" value="false"/>
<Property name="contentAreaFilled" type="boolean" value="false"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[1, 1, 1, 1]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[64, 64]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openCaseButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="openLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="13" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="closeButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.closeButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JSeparator" name="jSeparator1">
<Properties>
<Property name="orientation" type="int" value="1"/>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -20,6 +20,9 @@
package org.sleuthkit.autopsy.casemodule;
import java.awt.*;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
@ -64,16 +67,16 @@ public class CueBannerPanel extends javax.swing.JPanel {
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
closeButton = new javax.swing.JButton();
editorPanel = new javax.swing.JPanel();
autopsyLogo = new javax.swing.JLabel();
this.autopsyLogo.setText("");
newCaseButton = new javax.swing.JButton();
openRecentButton = new javax.swing.JButton();
createNewLabel = new javax.swing.JLabel();
openRecentLabel = new javax.swing.JLabel();
openCaseButton = new javax.swing.JButton();
openLabel = new javax.swing.JLabel();
autopsyLogo = new javax.swing.JLabel();
this.autopsyLogo.setText("");
closeButton = new javax.swing.JButton();
jSeparator1 = new javax.swing.JSeparator();
closeButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.closeButton.text")); // NOI18N
@ -82,7 +85,7 @@ public class CueBannerPanel extends javax.swing.JPanel {
newCaseButton.setBorder(null);
newCaseButton.setBorderPainted(false);
newCaseButton.setContentAreaFilled(false);
newCaseButton.setPreferredSize(new java.awt.Dimension(70, 70));
newCaseButton.setPreferredSize(new java.awt.Dimension(64, 64));
newCaseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
newCaseButtonActionPerformed(evt);
@ -94,7 +97,7 @@ public class CueBannerPanel extends javax.swing.JPanel {
openRecentButton.setBorder(null);
openRecentButton.setBorderPainted(false);
openRecentButton.setContentAreaFilled(false);
openRecentButton.setPreferredSize(new java.awt.Dimension(70, 70));
openRecentButton.setPreferredSize(new java.awt.Dimension(64, 64));
openRecentButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
openRecentButtonActionPerformed(evt);
@ -113,7 +116,7 @@ public class CueBannerPanel extends javax.swing.JPanel {
openCaseButton.setBorderPainted(false);
openCaseButton.setContentAreaFilled(false);
openCaseButton.setMargin(new java.awt.Insets(1, 1, 1, 1));
openCaseButton.setPreferredSize(new java.awt.Dimension(70, 70));
openCaseButton.setPreferredSize(new java.awt.Dimension(64, 64));
openCaseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
openCaseButtonActionPerformed(evt);
@ -123,46 +126,10 @@ public class CueBannerPanel extends javax.swing.JPanel {
openLabel.setFont(openLabel.getFont().deriveFont(Font.PLAIN, 13));
openLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openLabel.text")); // NOI18N
javax.swing.GroupLayout editorPanelLayout = new javax.swing.GroupLayout(editorPanel);
editorPanel.setLayout(editorPanelLayout);
editorPanelLayout.setHorizontalGroup(
editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(editorPanelLayout.createSequentialGroup()
.addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(editorPanelLayout.createSequentialGroup()
.addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(createNewLabel))
.addGroup(editorPanelLayout.createSequentialGroup()
.addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(openRecentLabel))
.addGroup(editorPanelLayout.createSequentialGroup()
.addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(openLabel)))
.addContainerGap(60, Short.MAX_VALUE))
);
editorPanelLayout.setVerticalGroup(
editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(editorPanelLayout.createSequentialGroup()
.addGap(32, 32, 32)
.addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(createNewLabel)
.addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(openRecentLabel)
.addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(0, 0, Short.MAX_VALUE)
.addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(openLabel)
.addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(25, Short.MAX_VALUE))
);
autopsyLogo.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/welcome_logo.png"))); // NOI18N NON-NLS
autopsyLogo.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.autopsyLogo.text")); // NOI18N
jSeparator1.setOrientation(javax.swing.SwingConstants.VERTICAL);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
@ -171,23 +138,44 @@ public class CueBannerPanel extends javax.swing.JPanel {
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(autopsyLogo)
.addGap(29, 29, 29)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(editorPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(closeButton, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap())
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 5, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(createNewLabel)
.addComponent(openRecentLabel)
.addComponent(openLabel))
.addComponent(closeButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(15, 15, 15))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(autopsyLogo, javax.swing.GroupLayout.PREFERRED_SIZE, 257, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addGroup(layout.createSequentialGroup()
.addComponent(editorPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(closeButton)))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(createNewLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(openRecentLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(openLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(closeButton))
.addComponent(jSeparator1)
.addComponent(autopsyLogo, javax.swing.GroupLayout.PREFERRED_SIZE, 257, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
@ -235,7 +223,7 @@ public class CueBannerPanel extends javax.swing.JPanel {
private javax.swing.JLabel autopsyLogo;
private javax.swing.JButton closeButton;
private javax.swing.JLabel createNewLabel;
private javax.swing.JPanel editorPanel;
private javax.swing.JSeparator jSeparator1;
private javax.swing.JButton newCaseButton;
private javax.swing.JButton openCaseButton;
private javax.swing.JLabel openLabel;

View File

@ -66,7 +66,7 @@
<Component id="noFatOrphansCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="descLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<EmptySpace pref="13" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>

View File

@ -177,7 +177,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
.addComponent(noFatOrphansCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(descLabel)
.addContainerGap(33, Short.MAX_VALUE))
.addContainerGap(13, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents

View File

@ -61,7 +61,7 @@
<Component id="noFatOrphansCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="descLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<EmptySpace pref="21" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>

View File

@ -167,7 +167,7 @@ final class LocalDiskPanel extends JPanel {
.addComponent(noFatOrphansCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(descLabel)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap(21, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
@ -307,13 +307,13 @@ final class LocalDiskPanel extends JPanel {
private volatile boolean loadingDisks = false;
List<LocalDisk> physical = new ArrayList<>();
List<LocalDisk> physicalDrives = new ArrayList<>();
List<LocalDisk> partitions = new ArrayList<>();
//private String SELECT = "Select a local disk:";
private String LOADING = NbBundle.getMessage(this.getClass(), "LocalDiskPanel.localDiskModel.loading.msg");
private String NO_DRIVES = NbBundle.getMessage(this.getClass(), "LocalDiskPanel.localDiskModel.nodrives.msg");
LocalDiskThread worker = null;
private void loadDisks() {
@ -326,7 +326,7 @@ final class LocalDiskPanel extends JPanel {
// Clear the lists
errorLabel.setText("");
disks = new ArrayList<>();
physical = new ArrayList<>();
physicalDrives = new ArrayList<>();
partitions = new ArrayList<>();
diskComboBox.setEnabled(false);
ready = false;
@ -357,17 +357,41 @@ final class LocalDiskPanel extends JPanel {
@Override
public Object getSelectedItem() {
return ready ? selected : LOADING;
if (ready) {
if (disks.isEmpty()) {
return NO_DRIVES;
}
return selected;
}
else {
return LOADING;
}
}
@Override
public int getSize() {
return ready ? disks.size() : 1;
if (ready) {
if (disks.isEmpty()) {
return 1;
}
return disks.size();
}
else {
return 1;
}
}
@Override
public Object getElementAt(int index) {
return ready ? disks.get(index) : LOADING;
if (ready) {
if (disks.isEmpty()) {
return NO_DRIVES;
}
return disks.get(index);
}
else {
return LOADING;
}
}
@Override
@ -383,7 +407,7 @@ final class LocalDiskPanel extends JPanel {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JPanel panel = new JPanel(new BorderLayout());
JLabel label = new JLabel();
if (index == physical.size() - 1) {
if ((index == physicalDrives.size() - 1) && (physicalDrives.size() > 0)) {
panel.add(new JSeparator(JSeparator.HORIZONTAL), BorderLayout.SOUTH);
}
@ -395,13 +419,13 @@ final class LocalDiskPanel extends JPanel {
label.setForeground(list.getForeground());
}
String localDiskString = value.toString();
if (localDiskString.equals(LOADING)) {
label.setText(LOADING);
label.setFont(label.getFont().deriveFont(Font.ITALIC));
label.setBackground(Color.GRAY);
} else {
if (value != null) {
String localDiskString = value.toString();
label.setText(value.toString());
if ((localDiskString.equals(LOADING)) || (localDiskString.equals(NO_DRIVES))) {
label.setFont(label.getFont().deriveFont(Font.ITALIC));
label.setBackground(Color.GRAY);
}
}
label.setOpaque(true);
label.setBorder(new EmptyBorder(2, 2, 2, 2));
@ -417,14 +441,14 @@ final class LocalDiskPanel extends JPanel {
@Override
protected Object doInBackground() throws Exception {
// Populate the lists
physical = PlatformUtil.getPhysicalDrives();
physicalDrives = PlatformUtil.getPhysicalDrives();
partitions = PlatformUtil.getPartitions();
return null;
}
private void displayErrors() {
if (physical.isEmpty() && partitions.isEmpty()) {
if (physicalDrives.isEmpty() && partitions.isEmpty()) {
if (PlatformUtil.isWindowsOS()) {
errorLabel.setText(
NbBundle.getMessage(this.getClass(), "LocalDiskPanel.errLabel.disksNotDetected.text"));
@ -437,7 +461,7 @@ final class LocalDiskPanel extends JPanel {
"LocalDiskPanel.errLabel.drivesNotDetected.toolTipText"));
}
diskComboBox.setEnabled(false);
} else if (physical.isEmpty()) {
} else if (physicalDrives.isEmpty()) {
errorLabel.setText(
NbBundle.getMessage(this.getClass(), "LocalDiskPanel.errLabel.someDisksNotDetected.text"));
errorLabel.setToolTipText(NbBundle.getMessage(this.getClass(),
@ -459,17 +483,18 @@ final class LocalDiskPanel extends JPanel {
if (!this.isCancelled()) {
enableNext = false;
displayErrors();
ready = true;
worker = null;
loadingDisks = false;
disks.addAll(physical);
disks.addAll(physicalDrives);
disks.addAll(partitions);
if (disks.size() > 0) {
diskComboBox.setEnabled(true);
diskComboBox.setSelectedIndex(0);
}
ready = true;
} else {
logger.log(Level.INFO, "Loading local disks was canceled, which should not be possible."); //NON-NLS
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

View File

@ -1,28 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Case Properties Window</title>
<link rel="stylesheet" href="nbdocs:/org/sleuthkit/autopsy/core/docs/ide.css" type="text/css">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<h2>Case Properties Window</h2>
<p>
Case Properties Window is where you can check some information about the currently opened case
(case name, case creation date, case directory, and images in this case).
</p>
<p>In this window, you can also do the following things:</p>
<ul>
<li>Change/update the case name</li>
<li>Delete the current case</li>
</ul>
<h2>How to Open Case Properties Window</h2>
<p>To open the "Case Properties" window, go to "File" and then select "Case Properties..."</p>
<h2>Example</h2>
<p>Here's an example of the "Case Properties" window:</p>
<img src="CasePropertiesHelp.png" alt="Case Properties Help" />
</body>
</html>

View File

@ -1,25 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Creating A Case</title>
<link rel="stylesheet" href="nbdocs:/org/sleuthkit/autopsy/core/docs/ide.css" type="text/css">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<h2>Creating a Case</h2>
<p>There are several ways to create a new case:</p>
<ul>
<li>Go to "File" and select "New Case..."</li>
<li>Press "Ctrl + N" on the keyboard</li>
</ul>
<p>
The "New Case" wizard dialog will open and you will need to enter the case name and base directory.
Each case will have its own directory and the path of the directory is created by combining the "base directory" with the "case name".
If the directory already exists, you will need to either delete the existing directory or choose a different combination of names.
</p>
<h2>Example:</h2>
<p> Here's an example of the "New Case" wizard dialog:</p>
<img src="NewCaseWizardHelp.png" alt="New Case Wizard Help" />
</body>
</html>

View File

@ -42,6 +42,7 @@ import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskFileRange;
import org.sleuthkit.datamodel.VirtualDirectory;
import org.sleuthkit.datamodel.CarvedFileContainer;
/**
* Abstraction to facilitate access to files and directories.
@ -197,8 +198,7 @@ public class FileManager implements Closeable {
}
/**
* Adds a carved file to the VirtualDirectory '$CarvedFiles' in the volume
* or file system given by systemId.
* Adds a carved file to the VirtualDirectory '$CarvedFiles' in the volume or image given by systemId.
*
* @param carvedFileName the name of the carved file (containing appropriate
* extension)
@ -221,6 +221,24 @@ public class FileManager implements Closeable {
}
/**
* Adds a collection of carved files to the VirtualDirectory '$CarvedFiles' in the volume or image given by
* systemId. Creates $CarvedFiles if it does not exist already.
*
* @param filesToAdd a list of CarvedFileContainer files to add as carved files
* @return List<LayoutFile> This is a list of the files added to the database
* @throws org.sleuthkit.datamodel.TskCoreException
*/
public List<LayoutFile> addCarvedFiles(List<CarvedFileContainer> filesToAdd) throws TskCoreException {
if (tskCase == null) {
throw new TskCoreException(NbBundle.getMessage(this.getClass(), "FileManager.addCarvedFile.exception.msg"));
}
else {
return tskCase.addCarvedFiles(filesToAdd);
}
}
/**
*
* Interface for receiving notifications on folders being added via a
* callback
*/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -16,7 +16,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.corecomponentinterfaces;
import java.beans.PropertyChangeListener;
@ -31,11 +30,11 @@ public interface DataExplorer extends PropertyChangeListener {
/**
* Gets the TopComponent for rendering this DateExplorer
*
*
* @return the DataExplorer's TopComponent
*/
public TopComponent getTopComponent();
public boolean hasMenuOpenAction();
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2013 Basis Technology Corp.
* Copyright 2011-2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*s
* Licensed under the Apache License, Version 2.0 (the "License");
@ -21,22 +21,20 @@ package org.sleuthkit.autopsy.corecomponents;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Level;
import javax.imageio.ImageIO;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.openide.nodes.Node;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.AbstractFile.MimeMatchEnum;
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
/**
@ -47,14 +45,15 @@ import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
})
public class DataContentViewerMedia extends javax.swing.JPanel implements DataContentViewer {
private static final String[] AUDIO_EXTENSIONS = new String[]{".mp3", ".wav", ".wma"}; //NON-NLS
private static final Set<String> AUDIO_EXTENSIONS = new TreeSet<>(Arrays.asList(".mp3", ".wav", ".wma")); //NON-NLS
private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName());
private AbstractFile lastFile;
//UI
private final MediaViewVideoPanel videoPanel;
private final String[] videoExtensions; // get them from the panel
private String[] imageExtensions; // use javafx supported
private final List<String> supportedMimes;
private final SortedSet<String> videoExtensions; // get them from the panel
private final SortedSet<String> imageExtensions;
private final SortedSet<String> videoMimes;
private final SortedSet<String> imageMimes;
private final MediaViewImagePanel imagePanel;
private boolean videoPanelInited;
private boolean imagePanelInited;
@ -70,34 +69,24 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
// get the right panel for our platform
videoPanel = MediaViewVideoPanel.createVideoPanel();
videoPanelInited = videoPanel.isInited();
videoExtensions = new TreeSet<>(Arrays.asList(videoPanel.getExtensions()));
videoMimes = new TreeSet<>(videoPanel.getMimeTypes());
imagePanel = new MediaViewImagePanel();
videoPanelInited = videoPanel.isInited();
imagePanelInited = imagePanel.isInited();
videoExtensions = videoPanel.getExtensions();
supportedMimes = videoPanel.getMimeTypes();
imageMimes = new TreeSet<>(imagePanel.getMimeTypes());
imageExtensions = new TreeSet<>(imagePanel.getExtensions());
customizeComponents();
logger.log(Level.INFO, "Created MediaView instance: " + this); //NON-NLS
}
private void customizeComponents() {
//initialize supported image types
//TODO use mime-types instead once we have support
String[] fxSupportedImagesSuffixes = ImageIO.getReaderFileSuffixes();
imageExtensions = new String[fxSupportedImagesSuffixes.length];
//logger.log(Level.INFO, "Supported image formats by javafx image viewer: ");
for (int i = 0; i < fxSupportedImagesSuffixes.length; ++i) {
String suffix = fxSupportedImagesSuffixes[i];
//logger.log(Level.INFO, "suffix: " + suffix);
imageExtensions[i] = "." + suffix;
}
add(imagePanel, IMAGE_VIEWER_LAYER);
add(videoPanel, VIDEO_VIEWER_LAYER);
switchPanels(false);
showVideoPanel(false);
}
/**
@ -137,19 +126,12 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
final Dimension dims = DataContentViewerMedia.this.getSize();
//logger.info("setting node on media viewer"); //NON-NLS
if (imagePanelInited && containsExt(file.getName(), imageExtensions)) {
if (imagePanelInited && isImageSupported(file)) {
imagePanel.showImageFx(file, dims);
this.switchPanels(false);
} else if (imagePanelInited && ImageUtils.isJpegFileHeader(file)) {
imagePanel.showImageFx(file, dims);
this.switchPanels(false);
} else if (videoPanelInited
&& containsMimeType(selectedNode,supportedMimes)&&(containsExt(file.getName(), videoExtensions) || containsExt(file.getName(), AUDIO_EXTENSIONS))) {
this.showVideoPanel(false);
} else if (videoPanelInited && isVideoSupported(file)) {
videoPanel.setupVideo(file, dims);
switchPanels(true);
this.showVideoPanel(true);
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Exception while setting node", e); //NON-NLS
@ -161,7 +143,7 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
*
* @param showVideo true if video panel, false if image panel
*/
private void switchPanels(boolean showVideo) {
private void showVideoPanel(boolean showVideo) {
CardLayout layout = (CardLayout) this.getLayout();
if (showVideo) {
layout.show(this, VIDEO_VIEWER_LAYER);
@ -197,6 +179,57 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
lastFile = null;
}
/**
*
* @param file
* @return True if a video file that can be displayed
*/
private boolean isVideoSupported(AbstractFile file) {
String name = file.getName().toLowerCase();
if ((containsExt(name, AUDIO_EXTENSIONS) || containsExt(name, videoExtensions)) &&
(!videoMimes.isEmpty() && file.isMimeType(videoMimes) == MimeMatchEnum.TRUE)) {
return true;
}
return false;
}
/**
*
* @param file
* @return True if an image file that can be displayed
*/
private boolean isImageSupported(AbstractFile file) {
String name = file.getName().toLowerCase();
// blackboard
if (!imageMimes.isEmpty()) {
MimeMatchEnum mimeMatch = file.isMimeType(imageMimes);
if (mimeMatch == MimeMatchEnum.TRUE) {
return true;
}
else if (mimeMatch == MimeMatchEnum.FALSE) {
return false;
}
}
// extension
if (containsExt(name, imageExtensions)) {
return true;
}
// our own signature checks for important types
else if (ImageUtils.isJpegFileHeader(file)) {
return true;
}
else if (ImageUtils.isPngFileHeader(file)) {
return true;
}
//for gstreamer formats, check if initialized first, then
//support audio formats, and video formats
return false;
}
@Override
public boolean isSupported(Node node) {
if (node == null) {
@ -211,23 +244,15 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
if (file.getSize() == 0) {
return false;
}
String name = file.getName().toLowerCase();
if (imagePanelInited) {
if (containsExt(name, imageExtensions)) {
if (isImageSupported(file))
return true;
}
else if (ImageUtils.isJpegFileHeader(file)) {
return true;
}
//for gstreamer formats, check if initialized first, then
//support audio formats, and video formats
}
if (videoPanelInited && videoPanel.isInited()) {
if ((containsExt(name, AUDIO_EXTENSIONS)
|| containsExt(name, videoExtensions))&& containsMimeType(node,supportedMimes)) {
if (isVideoSupported(file))
return true;
}
}
return false;
@ -252,34 +277,12 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
}
private static boolean containsExt(String name, String[] exts) {
private static boolean containsExt(String name, Set<String> exts) {
int extStart = name.lastIndexOf(".");
String ext = "";
if (extStart != -1) {
ext = name.substring(extStart, name.length()).toLowerCase();
}
return Arrays.asList(exts).contains(ext);
}
private static boolean containsMimeType(Node node, List<String> mimeTypes) {
if (mimeTypes.isEmpty()) {
//this should return true for 32 bit autopsy with the gstreamer MimeTypes list being empty,
//since mimetype detection is for java fx only. For 64 bit java fx, the code continues for Mimetype detection.
return true;
}
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
try {
ArrayList<BlackboardAttribute> genInfoAttributes = file.getGenInfoAttributes(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG);
if (genInfoAttributes.isEmpty() == false) {
for (BlackboardAttribute batt : genInfoAttributes) {
if (mimeTypes.contains(batt.getValueString())) {
return true;
}
}
return false;
}
} catch (TskCoreException ex) {
return false;
}
return false;
return exts.contains(ext);
}
}

View File

@ -80,9 +80,7 @@ import org.sleuthkit.datamodel.TskData;
public class FXVideoPanel extends MediaViewVideoPanel {
private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".mpg", ".mpeg"}; //NON-NLS
static private final List<String> supportedMimes = Arrays.asList("audio/x-aiff", "video/x-javafx", "video/x-flv", "application/vnd.apple.mpegurl", " audio/mpegurl", "audio/mpeg", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS
private static final List<String> MIMETYPES = Arrays.asList("audio/x-aiff", "video/x-javafx", "video/x-flv", "application/vnd.apple.mpegurl", " audio/mpegurl", "audio/mpeg", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS
private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName());
private boolean fxInited = false;
@ -825,6 +823,6 @@ public class FXVideoPanel extends MediaViewVideoPanel {
@Override
public List<String> getMimeTypes() {
return supportedMimes;
return MIMETYPES;
}
}

View File

@ -52,8 +52,6 @@ import org.gstreamer.elements.RGBDataSink;
import org.gstreamer.swing.VideoComponent;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.Cancellable;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
@ -71,8 +69,8 @@ import org.sleuthkit.datamodel.TskData;
@ServiceProvider(service = FrameCapture.class)
})
public class GstVideoPanel extends MediaViewVideoPanel {
private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; //NON-NLS
private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; //NON-NLS
private static final List<String> MIMETYPES = Arrays.asList("video/quicktime", "audio/mpeg", "audio/x-mpeg", "video/mpeg", "video/x-mpeg", "audio/mpeg3", "audio/x-mpeg-3", "video/x-flv", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS
private static final Logger logger = Logger.getLogger(GstVideoPanel.class.getName());
private boolean gstInited;
@ -88,8 +86,8 @@ public class GstVideoPanel extends MediaViewVideoPanel {
private boolean autoTracking = false; // true if the slider is moving automatically
private final Object playbinLock = new Object(); // lock for synchronization of gstPlaybin2 player
private AbstractFile currentFile;
private Set<String> badVideoFiles = Collections.synchronizedSet(new HashSet<String>());
static private final List<String> supportedMimes = Arrays.asList();
private final Set<String> badVideoFiles = Collections.synchronizedSet(new HashSet<String>());
/**
* Creates new form MediaViewVideoPanel
*/
@ -806,6 +804,6 @@ public class GstVideoPanel extends MediaViewVideoPanel {
@Override
public List<String> getMimeTypes() {
return supportedMimes;
return MIMETYPES;
}
}

View File

@ -23,6 +23,9 @@ import java.awt.EventQueue;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
@ -33,7 +36,6 @@ import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javax.imageio.ImageIO;
import javax.swing.SwingUtilities;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
@ -47,26 +49,30 @@ import org.sleuthkit.datamodel.ReadContentInputStream;
* used with JavaFx image viewer only.
*/
public class MediaViewImagePanel extends javax.swing.JPanel {
private JFXPanel fxPanel;
private ImageView fxImageView;
private static final Logger logger = Logger.getLogger(MediaViewImagePanel.class.getName());
private boolean fxInited = false;
private final List<String> supportedExtensions;
static private final List<String> supportedMimes = Arrays.asList("image/jpeg", "image/png", "image/gif", "image/bmp");
/**
* Creates new form MediaViewImagePanel
*/
public MediaViewImagePanel() {
initComponents();
fxInited = org.sleuthkit.autopsy.core.Installer.isJavaFxInited();
if (fxInited) {
setupFx();
}
supportedExtensions = new ArrayList<>();
//logger.log(Level.INFO, "Supported image formats by javafx image viewer: ");
for (String suffix : ImageIO.getReaderFileSuffixes()) {
//logger.log(Level.INFO, "suffix: " + suffix);
supportedExtensions.add("." + suffix);
}
}
public boolean isInited() {
@ -99,13 +105,8 @@ import org.sleuthkit.datamodel.ReadContentInputStream;
// setVisible(true);
}
});
}
});
}
public void reset() {
@ -207,6 +208,22 @@ import org.sleuthkit.datamodel.ReadContentInputStream;
});
}
/**
* returns supported mime types
* @return
*/
public List<String> getMimeTypes() {
return supportedMimes;
}
/**
* returns supported extensions (each starting with .)
* @return
*/
public List<String> getExtensions() {
return supportedExtensions;
}
/**
* This method is called from within the constructor to initialize the form.
@ -222,4 +239,4 @@ import org.sleuthkit.datamodel.ReadContentInputStream;
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Copyright 2013-2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -23,22 +23,110 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Writer;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
/**
* Takes care of forking a process and reading output / error streams to either a
* string buffer or directly to a file writer
* BC: @@@ This code scares me in a multi-threaded env. I think the arguments should be passed into the constructor
* and different run methods that either return the string or use the redirected writer.
* Executes a command line using an operating system process with a configurable
* timeout and pluggable logic to kill or continue the process on timeout.
*/
public final class ExecUtil {
public final class ExecUtil {
private static final long DEFAULT_TIMEOUT = 5;
private static final TimeUnit DEFAULT_TIMEOUT_UNITS = TimeUnit.SECONDS;
/**
* The execute() methods do a wait with a timeout on the executing process
* and query the terminator each time the timeout expires to determine
* whether or not to kill the process or allow it to continue.
*/
public interface ProcessTerminator {
/**
* An implementation of this interface is called by the run() methods at
* every timeout to determine whether or not to kill the running
* process.
*
* @return True or false.
*/
boolean shouldTerminateProcess();
}
/**
* Runs a process without a timeout and terminator.
*
* @param processBuilder A process builder used to configure and construct
* the process to be run.
* @return the exit value of the process
* @throws SecurityException if a security manager exists and vetoes any
* aspect of running the process.
* @throws IOException if an I/O error occurs.
*/
public static int execute(ProcessBuilder processBuilder) throws SecurityException, IOException {
return ExecUtil.execute(processBuilder, 30, TimeUnit.DAYS, new ProcessTerminator() {
/**
* @inheritDoc
*/
@Override
public boolean shouldTerminateProcess() {
return false;
}
});
}
/**
* Runs a process using the default timeout and a custom terminator.
*
* @param processBuilder A process builder used to configure and construct
* the process to be run.
* @param terminator The terminator.
* @return the exit value of the process
* @throws SecurityException if a security manager exists and vetoes any
* aspect of running the process.
* @throws IOException if an I/O error occurs.
*/
public static int execute(ProcessBuilder processBuilder, ProcessTerminator terminator) throws SecurityException, IOException {
return ExecUtil.execute(processBuilder, ExecUtil.DEFAULT_TIMEOUT, ExecUtil.DEFAULT_TIMEOUT_UNITS, terminator);
}
/**
* Runs a process using a custom terminator.
*
* @param processBuilder A process builder used to configure and construct
* the process to be run.
* @param timeOut The duration of the timeout.
* @param units The units for the timeout.
* @param terminator The terminator.
* @return the exit value of the process
* @throws SecurityException if a security manager exists and vetoes any
* aspect of running the process.
* @throws IOException if an I/o error occurs.
*/
public static int execute(ProcessBuilder processBuilder, long timeOut, TimeUnit units, ProcessTerminator terminator) throws SecurityException, IOException {
Process process = processBuilder.start();
try {
do {
process.waitFor(timeOut, units);
if (process.isAlive() && terminator.shouldTerminateProcess()) {
process.destroyForcibly();
}
} while (process.isAlive());
} catch (InterruptedException ex) {
if (process.isAlive()) {
process.destroyForcibly();
}
Logger.getLogger(ExecUtil.class.getName()).log(Level.INFO, "Thread interrupted while running {0}", processBuilder.command().get(0));
Thread.currentThread().interrupt();
}
return process.exitValue();
}
private static final Logger logger = Logger.getLogger(ExecUtil.class.getName());
private Process proc = null;
private final String command = null;
private ExecUtil.StreamToStringRedirect errorStringRedirect = null;
private ExecUtil.StreamToStringRedirect outputStringRedirect = null;
private ExecUtil.StreamToWriterRedirect outputWriterRedirect = null;
private int exitValue = -100;
/**
* Execute a process. Redirect asynchronously stdout to a string and stderr
@ -49,6 +137,7 @@ import java.util.logging.Level;
* @param params parameters of the command
* @return string buffer with captured stdout
*/
@Deprecated
public synchronized String execute(final String aCommand, final String... params) throws IOException, InterruptedException {
// build command array
String[] arrayCommand = new String[params.length + 1];
@ -63,26 +152,25 @@ import java.util.logging.Level;
}
final Runtime rt = Runtime.getRuntime();
logger.log(Level.INFO, "Executing " + arrayCommandToLog.toString()); //NON-NLS
logger.log(Level.INFO, "Executing {0}", arrayCommandToLog.toString()); //NON-NLS
proc = rt.exec(arrayCommand);
//stderr redirect
errorStringRedirect = new ExecUtil.StreamToStringRedirect(proc.getErrorStream(), "ERROR"); //NON-NLS
errorStringRedirect.start();
errorStringRedirect.start();
//stdout redirect
outputStringRedirect = new ExecUtil.StreamToStringRedirect(proc.getInputStream(), "OUTPUT"); //NON-NLS
outputStringRedirect.start();
//wait for process to complete and capture error core
final int exitVal = proc.waitFor();
logger.log(Level.INFO, aCommand + " exit value: " + exitVal); //NON-NLS
this.exitValue = proc.waitFor();
// wait for output redirectors to finish writing / reading
outputWriterRedirect.join();
outputStringRedirect.join();
errorStringRedirect.join();
return outputStringRedirect.getOutput();
}
@ -95,6 +183,7 @@ import java.util.logging.Level;
* @param params parameters of the command
* @return string buffer with captured stdout
*/
@Deprecated
public synchronized void execute(final Writer stdoutWriter, final String aCommand, final String... params) throws IOException, InterruptedException {
// build command array
@ -110,38 +199,35 @@ import java.util.logging.Level;
}
final Runtime rt = Runtime.getRuntime();
logger.log(Level.INFO, "Executing " + arrayCommandToLog.toString()); //NON-NLS
logger.log(Level.INFO, "Executing {0}", arrayCommandToLog.toString()); //NON-NLS
proc = rt.exec(arrayCommand);
//stderr redirect
errorStringRedirect = new ExecUtil.StreamToStringRedirect(proc.getErrorStream(), "ERROR"); //NON-NLS
errorStringRedirect.start();
errorStringRedirect.start();
//stdout redirect
outputWriterRedirect = new ExecUtil.StreamToWriterRedirect(proc.getInputStream(), stdoutWriter);
outputWriterRedirect.start();
//wait for process to complete and capture error core
final int exitVal = proc.waitFor();
logger.log(Level.INFO, aCommand + " exit value: " + exitVal); //NON-NLS
this.exitValue = proc.waitFor();
logger.log(Level.INFO, "{0} exit value: {1}", new Object[]{aCommand, exitValue}); //NON-NLS
// wait for them to finish writing / reading
outputWriterRedirect.join();
errorStringRedirect.join();
//gc process with its streams
//proc = null;
}
/**
* Interrupt the running process and stop its stream redirect threads
*/
@Deprecated
public synchronized void stop() {
logger.log(Level.INFO, "Stopping Execution of: " + command); //NON-NLS
if (errorStringRedirect != null) {
errorStringRedirect.stopRun();
@ -164,6 +250,17 @@ import java.util.logging.Level;
}
}
/**
* Gets the exit value returned by the subprocess used to execute a command.
*
* @return The exit value or the distinguished value -100 if this method is
* called before the exit value is set.
*/
@Deprecated
synchronized public int getExitValue() {
return this.exitValue;
}
/**
* Asynchronously read the output of a given input stream and write to a
* string to be returned. Any exception during execution of the command is
@ -173,8 +270,8 @@ import java.util.logging.Level;
private static class StreamToStringRedirect extends Thread {
private static final Logger logger = Logger.getLogger(StreamToStringRedirect.class.getName());
private InputStream is;
private StringBuffer output = new StringBuffer();
private final InputStream is;
private final StringBuffer output = new StringBuffer();
private volatile boolean doRun = false;
StreamToStringRedirect(final InputStream anIs, final String aType) {
@ -191,7 +288,7 @@ import java.util.logging.Level;
@Override
public final void run() {
final String SEP = System.getProperty("line.separator");
InputStreamReader isr = null;
InputStreamReader isr;
BufferedReader br = null;
try {
isr = new InputStreamReader(this.is);
@ -243,7 +340,7 @@ import java.util.logging.Level;
private static class StreamToWriterRedirect extends Thread {
private static final Logger logger = Logger.getLogger(StreamToStringRedirect.class.getName());
private InputStream is;
private final InputStream is;
private volatile boolean doRun = false;
private Writer writer = null;
@ -262,7 +359,7 @@ import java.util.logging.Level;
@Override
public final void run() {
final String SEP = System.getProperty("line.separator");
InputStreamReader isr = null;
InputStreamReader isr;
BufferedReader br = null;
try {
isr = new InputStreamReader(this.is);
@ -296,4 +393,4 @@ import java.util.logging.Level;
doRun = false;
}
}
}
}

View File

@ -196,6 +196,32 @@ public class ImageUtils {
return (((fileHeaderBuffer[0] & 0xff) == 0xff) && ((fileHeaderBuffer[1] & 0xff) == 0xd8));
}
public static boolean isPngFileHeader(AbstractFile file) {
if (file.getSize() < 10) {
return false;
}
byte[] fileHeaderBuffer = new byte[8];
int bytesRead;
try {
bytesRead = file.read(fileHeaderBuffer, 0, 8);
} catch (TskCoreException ex) {
//ignore if can't read the first few bytes, not an image
return false;
}
if (bytesRead != 8) {
return false;
}
/*
* Check for the header. Since Java bytes are signed, we cast them
* to an int first.
*/
return (((fileHeaderBuffer[1] & 0xff) == 0x50) && ((fileHeaderBuffer[2] & 0xff) == 0x4E) &&
((fileHeaderBuffer[3] & 0xff) == 0x47) && ((fileHeaderBuffer[4] & 0xff) == 0x0D) &&
((fileHeaderBuffer[5] & 0xff) == 0x0A) && ((fileHeaderBuffer[6] & 0xff) == 0x1A) &&
((fileHeaderBuffer[7] & 0xff) == 0x0A));
}
private static Image generateAndSaveIcon(Content content, int iconSize) {
Image icon = null;

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2012 Basis Technology Corp.
* Copyright 2012-2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -31,6 +31,7 @@ import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.filechooser.FileSystemView;
import org.hyperic.sigar.Sigar;
@ -47,7 +48,7 @@ import org.sleuthkit.datamodel.TskCoreException;
*/
public class PlatformUtil {
private static final String PYTHON_MODULES_SUBDIRECTORY = "python_modules";
private static final String PYTHON_MODULES_SUBDIRECTORY = "python_modules";
private static String javaPath = null;
public static final String OS_NAME_UNKNOWN = NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.nameUnknown");
public static final String OS_VERSION_UNKNOWN = NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.verUnknown");
@ -126,12 +127,11 @@ public class PlatformUtil {
}
File jrePath = new File(getInstallPath() + File.separator + "jre");
if (jrePath != null && jrePath.exists() && jrePath.isDirectory()) {
if (jrePath.exists() && jrePath.isDirectory()) {
System.out.println(
NbBundle.getMessage(PlatformUtil.class,
"PlatformUtil.jrePath.jreDir.msg",
jrePath.getAbsolutePath()));
"PlatformUtil.jrePath.jreDir.msg",
jrePath.getAbsolutePath()));
javaPath = jrePath.getAbsolutePath() + File.separator + "bin" + File.separator + "java"; //NON-NLS
} else {
//else use system installed java in PATH env variable
@ -141,7 +141,6 @@ public class PlatformUtil {
System.out.println(NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.jrePath.usingJavaPath.msg", javaPath));
return javaPath;
}
@ -154,26 +153,25 @@ public class PlatformUtil {
public static File getUserDirectory() {
return Places.getUserDirectory();
}
/**
* Get RCP project dirs
* @return
* Get RCP project dirs
*
* @return
*/
public static List<String> getProjectsDirs() {
List<String> ret = new ArrayList<String>();
List<String> ret = new ArrayList<>();
String projectDir = System.getProperty("netbeans.dirs");
if (projectDir == null) {
return ret;
}
String [] split = projectDir.split(";");
String[] split = projectDir.split(";");
if (split == null || split.length == 0) {
return ret;
}
for (String path : split) {
ret.add(path);
}
return ret;
ret.addAll(Arrays.asList(split));
return ret;
}
/**
@ -214,6 +212,7 @@ public class PlatformUtil {
* @param resourceClass class in the same package as the resourceFile to
* extract
* @param resourceFile resource file name to extract
* @param overWrite true to overwrite an existing resource
* @return true if extracted, false otherwise (if file already exists)
* @throws IOException exception thrown if extract the file failed for IO
* reasons
@ -225,24 +224,21 @@ public class PlatformUtil {
if (resourceFileF.exists() && !overWrite) {
return false;
}
resourceFileF.getParentFile().mkdirs();
InputStream inputStream = resourceClass.getResourceAsStream(resourceFile);
OutputStream out = null;
InputStream in = null;
try {
try (InputStream in = new BufferedInputStream(inputStream)) {
in = new BufferedInputStream(inputStream);
OutputStream outFile = new FileOutputStream(resourceFileF);
out = new BufferedOutputStream(outFile);
int readBytes = 0;
int readBytes;
while ((readBytes = in.read()) != -1) {
out.write(readBytes);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.flush();
out.close();
@ -301,6 +297,21 @@ public class PlatformUtil {
}
}
/**
* Attempts to determine whether the operating system is a 64-bit operating
* system. May not be completely reliable for non-Windows operating systems.
*
* @return True if the operating system is definitely a 64-bit operating
* system, false otherwise.
*/
public static boolean is64BitOS() {
if (System.getProperty("os.name").contains("Windows")) {
return (System.getenv("ProgramFiles(x86)") != null);
} else {
return (System.getProperty("os.arch").contains("64"));
}
}
/**
* Get a list of all physical drives attached to the client's machine. Error
* threshold of 4 non-existent physical drives before giving up.
@ -308,7 +319,7 @@ public class PlatformUtil {
* @return list of physical drives
*/
public static List<LocalDisk> getPhysicalDrives() {
List<LocalDisk> drives = new ArrayList<LocalDisk>();
List<LocalDisk> drives = new ArrayList<>();
// Windows drives
if (PlatformUtil.isWindowsOS()) {
int n = 0;
@ -359,18 +370,18 @@ public class PlatformUtil {
* @return list of local drives and partitions
*/
public static List<LocalDisk> getPartitions() {
List<LocalDisk> drives = new ArrayList<LocalDisk>();
List<LocalDisk> drives = new ArrayList<>();
FileSystemView fsv = FileSystemView.getFileSystemView();
if (PlatformUtil.isWindowsOS()) {
File[] f = File.listRoots();
for (int i = 0; i < f.length; i++) {
String name = fsv.getSystemDisplayName(f[i]);
for (File f1 : f) {
String name = fsv.getSystemDisplayName(f1);
// Check if it is a drive, readable, and not mapped to the network
if (f[i].canRead() && !name.contains("\\\\") && (fsv.isDrive(f[i]) || fsv.isFloppyDrive(f[i]))) {
String path = f[i].getPath();
if (f1.canRead() && !name.contains("\\\\") && (fsv.isDrive(f1) || fsv.isFloppyDrive(f1))) {
String path = f1.getPath();
String diskPath = "\\\\.\\" + path.substring(0, path.length() - 1);
if (canReadDrive(diskPath)) {
drives.add(new LocalDisk(fsv.getSystemDisplayName(f[i]), diskPath, f[i].getTotalSpace()));
drives.add(new LocalDisk(fsv.getSystemDisplayName(f1), diskPath, f1.getTotalSpace()));
}
}
}
@ -452,8 +463,8 @@ public class PlatformUtil {
/**
* Query and get PID of another java process
*
* @param sigarSubQuery a sigar subquery to identify a unique java process among
* other java processes, for example, by class name, use:
* @param sigarSubQuery a sigar subquery to identify a unique java process
* among other java processes, for example, by class name, use:
* Args.*.eq=org.jboss.Main more examples here:
* http://support.hyperic.com/display/SIGAR/PTQL
*
@ -483,9 +494,10 @@ public class PlatformUtil {
/**
* Query and get PIDs of another java processes matching a query
*
* @param sigarSubQuery a sigar subquery to identify a java processes among other
* java processes, for example, by class name, use: Args.*.eq=org.jboss.Main
* more examples here: http://support.hyperic.com/display/SIGAR/PTQL
* @param sigarSubQuery a sigar subquery to identify a java processes among
* other java processes, for example, by class name, use:
* Args.*.eq=org.jboss.Main more examples here:
* http://support.hyperic.com/display/SIGAR/PTQL
*
* @return array of PIDs of a java processes matching the query or null if
* it couldn't be determined
@ -539,7 +551,6 @@ public class PlatformUtil {
* @return virt memory used in bytes or -1 if couldn't be queried
*/
public static synchronized long getProcessVirtualMemoryUsed() {
long pid = getPID();
long virtMem = -1;
try {
@ -547,11 +558,11 @@ public class PlatformUtil {
sigar = org.sleuthkit.autopsy.corelibs.SigarLoader.getSigar();
}
if (sigar == null || pid == -1) {
if (sigar == null || getPID() == -1) {
System.out.println(NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.getProcVmUsed.sigarNotInit.msg"));
return -1;
}
virtMem = sigar.getProcMem(pid).getSize();
virtMem = sigar.getProcMem(getPID()).getSize();
} catch (Exception e) {
System.out.println(NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.getProcVmUsed.gen.msg", e.toString()));
}
@ -574,8 +585,8 @@ public class PlatformUtil {
final MemoryUsage nonHeap = memoryManager.getNonHeapMemoryUsage();
return NbBundle.getMessage(PlatformUtil.class,
"PlatformUtil.getJvmMemInfo.usageText",
heap.toString(), nonHeap.toString());
"PlatformUtil.getJvmMemInfo.usageText",
heap.toString(), nonHeap.toString());
}
/**
@ -589,8 +600,8 @@ public class PlatformUtil {
final long totalMemory = runTime.totalMemory();
final long freeMemory = runTime.freeMemory();
return NbBundle.getMessage(PlatformUtil.class,
"PlatformUtil.getPhysicalMemInfo.usageText",
Long.toString(maxMemory), Long.toString(totalMemory), Long.toString(freeMemory));
"PlatformUtil.getPhysicalMemInfo.usageText",
Long.toString(maxMemory), Long.toString(totalMemory), Long.toString(freeMemory));
}
/**
@ -599,14 +610,9 @@ public class PlatformUtil {
* @return formatted string with all memory usage info
*/
public static String getAllMemUsageInfo() {
// StringBuilder sb = new StringBuilder();
// sb.append(PlatformUtil.getPhysicalMemInfo()).append("\n");
// sb.append(PlatformUtil.getJvmMemInfo()).append("\n");
// sb.append("Process Virtual Memory: ").append(PlatformUtil.getProcessVirtualMemoryUsed());
// return sb.toString();
return NbBundle.getMessage(PlatformUtil.class,
"PlatformUtil.getAllMemUsageInfo.usageText",
PlatformUtil.getPhysicalMemInfo(), PlatformUtil.getJvmMemInfo(),
PlatformUtil.getProcessVirtualMemoryUsed());
"PlatformUtil.getAllMemUsageInfo.usageText",
PlatformUtil.getPhysicalMemInfo(), PlatformUtil.getJvmMemInfo(),
PlatformUtil.getProcessVirtualMemoryUsed());
}
}

View File

@ -442,6 +442,8 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
return "encrypted-file.png"; //NON-NLS
case TSK_EXT_MISMATCH_DETECTED:
return "mismatch-16.png"; //NON-NLS
case TSK_OS_INFO:
return "computer.png"; //NON-NLS
default:
return "artifact-icon.png"; //NON-NLS
}

View File

@ -232,6 +232,9 @@ ReportNode.createdTimeProperty.desc=Time report was created
ReportNode.pathProperty.name=Report File Path
ReportNode.pathProperty.displayName=Report File Path
ReportNode.pathProperty.desc=Local path of the report file
ReportNode.reportNameProperty.name=Report Name
ReportNode.reportNameProperty.displayName=Report Name
ReportNode.reportNameProperty.desc=Name of the report
ReportsListNode.displayName=Reports
ResultsNode.name.text=Results
ResultsNode.createSheet.name.name=Name

View File

@ -331,6 +331,9 @@ public class ExtractedContent implements AutopsyVisitableItem {
return "encrypted-file.png"; //NON-NLS
case TSK_EXT_MISMATCH_DETECTED:
return "mismatch-16.png"; //NON-NLS
case TSK_OS_INFO:
return "computer.png"; //NON-NLS
}
return "artifact-icon.png"; //NON-NLS
}

View File

@ -166,11 +166,15 @@ public final class Reports implements AutopsyVisitableItem {
propertiesSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ReportNode.createdTimeProperty.name"),
NbBundle.getMessage(this.getClass(), "ReportNode.createdTimeProperty.displayName"),
NbBundle.getMessage(this.getClass(), "ReportNode.createdTimeProperty.desc"),
dateFormatter.format(new java.util.Date(this.report.getCreatedTime() * 1000)).toString()));
dateFormatter.format(new java.util.Date(this.report.getCreatedTime() * 1000))));
propertiesSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ReportNode.pathProperty.name"),
NbBundle.getMessage(this.getClass(), "ReportNode.pathProperty.displayName"),
NbBundle.getMessage(this.getClass(), "ReportNode.pathProperty.desc"),
this.report.getPath()));
propertiesSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ReportNode.reportNameProperty.name"),
NbBundle.getMessage(this.getClass(), "ReportNode.reportNameProperty.displayName"),
NbBundle.getMessage(this.getClass(), "ReportNode.reportNameProperty.desc"),
this.report.getReportName()));
return sheet;
}

View File

@ -25,6 +25,7 @@ import org.openide.explorer.view.BeanTreeView;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
/**
*
* @author jantonius

View File

@ -70,7 +70,7 @@ class SampleDataSourceIngestModule implements DataSourceIngestModule {
@Override
public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
if (context.isJobCancelled()) {
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
@ -92,22 +92,22 @@ class SampleDataSourceIngestModule implements DataSourceIngestModule {
}
progressBar.progress(1);
if (context.isJobCancelled()) {
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
// Get files by creation time.
long currentTime = System.currentTimeMillis() / 1000;
long minTime = currentTime - (14 * 24 * 60 * 60); // Go back two weeks.
List<FsContent> otherFiles = sleuthkitCase.findFilesWhere("crtime > " + minTime);
for (FsContent otherFile : otherFiles) {
List<AbstractFile> otherFiles = fileManager.findFiles(dataSource, "crtime > " + minTime);
for (AbstractFile otherFile : otherFiles) {
if (!skipKnownFiles || otherFile.getKnown() != TskData.FileKnown.KNOWN) {
++fileCount;
}
}
progressBar.progress(1);
if (context.isJobCancelled()) {
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}

View File

@ -156,11 +156,9 @@ class SampleFileIngestModule implements FileIngestModule {
@Override
public void shutDown() {
if (!context.isJobCancelled()) {
// This method is thread-safe with per ingest job reference counted
// management of shared data.
reportBlackboardPostCount(context.getJobId());
}
// This method is thread-safe with per ingest job reference counted
// management of shared data.
reportBlackboardPostCount(context.getJobId());
}
synchronized static void addToBlackboardPostCount(long ingestJobId, long countToAdd) {

View File

@ -1,3 +1,32 @@
# Sample module in the public domain. Feel free to use this as a template
# for your modules (and you can remove this header and take complete credit
# and liability)
#
# Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
import jarray
from java.lang import System
from org.sleuthkit.datamodel import SleuthkitCase
@ -15,6 +44,38 @@ from org.sleuthkit.autopsy.casemodule import Case
from org.sleuthkit.autopsy.casemodule.services import Services
from org.sleuthkit.autopsy.casemodule.services import FileManager
# Sample factory that defines basic functionality and features of the module
class SampleJythonIngestModuleFactory(IngestModuleFactoryAdapter):
def getModuleDisplayName(self):
return "Sample Jython Ingest Module"
def getModuleDescription(self):
return "A sample Jython ingest module"
def getModuleVersionNumber(self):
return "1.0"
# Return true if module wants to get passed in a data source
def isDataSourceIngestModuleFactory(self):
return True
# can return null if isDataSourceIngestModuleFactory returns false
def createDataSourceIngestModule(self, ingestOptions):
return SampleJythonDataSourceIngestModule()
# Return true if module wants to get called for each file
def isFileIngestModuleFactory(self):
return True
# can return null if isFileIngestModuleFactory returns false
def createFileIngestModule(self, ingestOptions):
return SampleJythonFileIngestModule()
# Data Source-level ingest module. One gets created per data source.
# Queries for various files.
# If you don't need a data source-level module, delete this class.
class SampleJythonDataSourceIngestModule(DataSourceIngestModule):
def __init__(self):
@ -24,95 +85,78 @@ class SampleJythonDataSourceIngestModule(DataSourceIngestModule):
self.context = context
def process(self, dataSource, progressBar):
if self.context.isJobCancelled():
return IngestModule.ProcessResult.OK
if self.context.isJobCancelled():
return IngestModule.ProcessResult.OK
# There are two tasks to do.
progressBar.switchToDeterminate(2)
# Configure progress bar for 2 tasks
progressBar.switchToDeterminate(2)
autopsyCase = Case.getCurrentCase()
sleuthkitCase = autopsyCase.getSleuthkitCase()
services = Services(sleuthkitCase)
fileManager = services.getFileManager()
autopsyCase = Case.getCurrentCase()
sleuthkitCase = autopsyCase.getSleuthkitCase()
services = Services(sleuthkitCase)
fileManager = services.getFileManager()
#Get count of files with .doc extension.
fileCount = 0;
docFiles = fileManager.findFiles(dataSource, "%.doc")
for docFile in docFiles:
fileCount += 1
progressBar.progress(1)
# Get count of files with "test" in name.
fileCount = 0;
files = fileManager.findFiles(dataSource, "%test%")
for file in files:
fileCount += 1
progressBar.progress(1)
if self.context.isJobCancelled():
return IngestModule.ProcessResult.OK
if self.context.isJobCancelled():
return IngestModule.ProcessResult.OK
# Get files by creation time.
currentTime = System.currentTimeMillis() / 1000
minTime = currentTime - (14 * 24 * 60 * 60) # Go back two weeks.
otherFiles = sleuthkitCase.findFilesWhere("crtime > %d" % minTime)
for otherFile in otherFiles:
fileCount += 1
progressBar.progress(1);
# Get files by creation time.
currentTime = System.currentTimeMillis() / 1000
minTime = currentTime - (14 * 24 * 60 * 60) # Go back two weeks.
otherFiles = sleuthkitCase.findAllFilesWhere("crtime > %d" % minTime)
for otherFile in otherFiles:
fileCount += 1
progressBar.progress(1);
if self.context.isJobCancelled():
return IngestModule.ProcessResult.OK;
if self.context.isJobCancelled():
return IngestModule.ProcessResult.OK;
#Post a message to the ingest messages in box.
message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, "Sample Jython Data Source Ingest Module", "Found %d files" % fileCount)
IngestServices.getInstance().postMessage(message)
#Post a message to the ingest messages in box.
message = IngestMessage.createMessage(IngestMessage.MessageType.DATA,
"Sample Jython Data Source Ingest Module", "Found %d files" % fileCount)
IngestServices.getInstance().postMessage(message)
return IngestModule.ProcessResult.OK;
return IngestModule.ProcessResult.OK;
# File-level ingest module. One gets created per thread.
# Looks at the attributes of the passed in file.
# if you don't need a file-level module, delete this class.
class SampleJythonFileIngestModule(FileIngestModule):
def startUp(self, context):
pass
def process(self, file):
# If the file has a txt extension, post an artifact to the blackboard.
if file.getName().endswith("txt"):
art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT)
att = BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), "Sample Jython File Ingest Module", "Text file")
art.addAttribute(att)
# If the file has a txt extension, post an artifact to the blackboard.
if file.getName().find("test") != -1:
art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT)
att = BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), "Sample Jython File Ingest Module", "Text Files")
art.addAttribute(att)
# Read the contents of the file.
inputStream = ReadContentInputStream(file)
buffer = jarray.zeros(1024, "b")
totLen = 0
len = inputStream.read(buffer)
while (len != -1):
totLen = totLen + len
len = inputStream.read(buffer)
# Read the contents of the file.
inputStream = ReadContentInputStream(file)
buffer = jarray.zeros(1024, "b")
totLen = 0
len = inputStream.read(buffer)
while (len != -1):
totLen = totLen + len
len = inputStream.read(buffer)
# Send the size of the file to the ingest messages in box.
msgText = "Size of %s is %d bytes" % ((file.getName(), totLen))
message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, "Sample Jython File IngestModule", msgText)
ingestServices = IngestServices.getInstance().postMessage(message)
# Send the size of the file to the ingest messages in box.
msgText = "Size of %s is %d bytes" % ((file.getName(), totLen))
message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, "Sample Jython File IngestModule", msgText)
ingestServices = IngestServices.getInstance().postMessage(message)
return IngestModule.ProcessResult.OK
return IngestModule.ProcessResult.OK
def shutDown(self):
pass
class SampleJythonIngestModuleFactory(IngestModuleFactoryAdapter):
def getModuleDisplayName(self):
return "Sample Jython Ingest Module"
def getModuleDescription(self):
return "A sample Jython ingest module"
def getModuleVersionNumber(self):
return "1.0"
def isDataSourceIngestModuleFactory(self):
return True
def createDataSourceIngestModule(self, ingestOptions):
return SampleJythonDataSourceIngestModule()
def isFileIngestModuleFactory(self):
return True
def createFileIngestModule(self, ingestOptions):
return SampleJythonFileIngestModule()

View File

@ -1,8 +1,39 @@
# Sample module in the public domain. Feel free to use this as a template
# for your modules (and you can remove this header and take complete credit
# and liability)
#
# Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
from java.lang import System
from org.sleuthkit.autopsy.casemodule import Case
from org.sleuthkit.datamodel import SleuthkitCase
from org.sleuthkit.autopsy.report import GeneralReportModuleAdapter
# Sample module that writes a file with the number of files
# created in the last 2 weeks.
class SampleGeneralReportModule(GeneralReportModuleAdapter):
def getName(self):
@ -15,7 +46,7 @@ class SampleGeneralReportModule(GeneralReportModuleAdapter):
return "sampleReport.txt"
def generateReport(self, reportPath, progressBar):
# There are two tasks to do.
# Configure progress bar for 2 tasks
progressBar.setIndeterminate(False)
progressBar.start()
progressBar.setMaximumProgress(2)

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

View File

@ -20,6 +20,7 @@ IngestJob.progress.dataSourceIngest.initialDisplayName=Analyzing {0}
IngestJob.progress.dataSourceIngest.displayName={0} for {1}
IngestJob.progress.fileIngest.displayName=Analyzing files from {0}
IngestJob.progress.cancelling={0} (Cancelling...)
IngestJob.cancellationDialog.title=Cancel Ingest
IngestJobConfigurationPanel.processUnallocCheckbox.toolTipText=Processes unallocated space, such as deleted files. Produces more complete results, but it may take longer to process on large images.
IngestJobConfigurationPanel.processUnallocCheckbox.text=Process Unallocated Space
IngestDialog.title.text=Ingest Modules
@ -80,3 +81,7 @@ IngestManager.IngestMessage.ErrorMessageLimitReached.subject=Maximum Errors Post
IngestManager.IngestMessage.ErrorMessageLimitReached.msg=Maximum number ({0}) of error and/or warning messages posted. See log for additional errors/warnings (Help -> Open Log Folder).
IngestManager.IngestMessage.ErrorMessageLimitReached.title=Ingest Manager
IngestModuleFactoryLoader.errorMessages.duplicateDisplayName=Found module named {0}, duplicating the name of another module, module will not be used.
DataSourceIngestCancellationPanel.cancelCurrentModuleRadioButton.text=Cancel current ingest module only
FileIngestCancellationPanel.cancelIngestJobRadioButton.text=Cancel data source ingest and file ingest
FileIngestCancellationPanel.cancelFileIngestRadioButton.text=Cancel file ingest only
DataSourceIngestCancellationPanel.cancelAllModulesRadioButton.text=Cancel all ingest modules

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="cancelRadioButtonsGroup">
</Component>
</NonVisualComponents>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="cancelAllModulesRadioButton" min="-2" max="-2" attributes="0"/>
<Component id="cancelCurrentModuleRadioButton" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelCurrentModuleRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="cancelAllModulesRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JRadioButton" name="cancelCurrentModuleRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="cancelRadioButtonsGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="DataSourceIngestCancellationPanel.cancelCurrentModuleRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelCurrentModuleRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="cancelAllModulesRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="cancelRadioButtonsGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="DataSourceIngestCancellationPanel.cancelAllModulesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelAllModulesRadioButtonActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,113 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.ingest;
/**
* A UI panel that allows a user to make data source ingest cancellation
* requests.
*/
final class DataSourceIngestCancellationPanel extends javax.swing.JPanel {
private boolean cancelAllIngestModules;
/**
* Constructs an instance of the panel.
*/
DataSourceIngestCancellationPanel() {
initComponents();
this.cancelCurrentModuleRadioButton.setSelected(true);
}
/**
* Queries whether the user wants to cancel the ingest job or just the
* currently executing data source ingest module.
*
* @return True if the ingest job is to be canceled, false if only the
* current module is to be canceled.
*/
boolean cancelAllDataSourceIngestModules() {
return this.cancelAllIngestModules;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
cancelRadioButtonsGroup = new javax.swing.ButtonGroup();
cancelCurrentModuleRadioButton = new javax.swing.JRadioButton();
cancelAllModulesRadioButton = new javax.swing.JRadioButton();
cancelRadioButtonsGroup.add(cancelCurrentModuleRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(cancelCurrentModuleRadioButton, org.openide.util.NbBundle.getMessage(DataSourceIngestCancellationPanel.class, "DataSourceIngestCancellationPanel.cancelCurrentModuleRadioButton.text")); // NOI18N
cancelCurrentModuleRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cancelCurrentModuleRadioButtonActionPerformed(evt);
}
});
cancelRadioButtonsGroup.add(cancelAllModulesRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(cancelAllModulesRadioButton, org.openide.util.NbBundle.getMessage(DataSourceIngestCancellationPanel.class, "DataSourceIngestCancellationPanel.cancelAllModulesRadioButton.text")); // NOI18N
cancelAllModulesRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cancelAllModulesRadioButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(cancelAllModulesRadioButton)
.addComponent(cancelCurrentModuleRadioButton))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(cancelCurrentModuleRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(cancelAllModulesRadioButton)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
private void cancelCurrentModuleRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelCurrentModuleRadioButtonActionPerformed
this.cancelAllIngestModules = this.cancelAllModulesRadioButton.isEnabled();
}//GEN-LAST:event_cancelCurrentModuleRadioButtonActionPerformed
private void cancelAllModulesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelAllModulesRadioButtonActionPerformed
this.cancelAllIngestModules = this.cancelAllModulesRadioButton.isEnabled();
}//GEN-LAST:event_cancelAllModulesRadioButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton cancelAllModulesRadioButton;
private javax.swing.JRadioButton cancelCurrentModuleRadioButton;
private javax.swing.ButtonGroup cancelRadioButtonsGroup;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,48 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.ingest;
import org.sleuthkit.autopsy.coreutils.ExecUtil;
/**
* An ExecUtil process terminator for data source ingest modules that checks for
* ingest job cancellation.
*/
public final class DataSourceIngestModuleProcessTerminator implements ExecUtil.ProcessTerminator {
private final IngestJobContext context;
/**
* Constructs a process terminator for a data source ingest module.
*
* @param context The ingest job context for the ingest module.
*/
public DataSourceIngestModuleProcessTerminator(IngestJobContext context) {
this.context = context;
}
/**
* @inheritDoc
*/
@Override
public boolean shouldTerminateProcess() {
return this.context.dataSourceIngestIsCancelled();
}
}

View File

@ -25,10 +25,10 @@ import org.netbeans.api.progress.ProgressHandle;
*/
public class DataSourceIngestModuleProgress {
private final ProgressHandle progress;
private final IngestJob job;
DataSourceIngestModuleProgress(ProgressHandle progress) {
this.progress = progress;
DataSourceIngestModuleProgress(IngestJob job) {
this.job = job;
}
/**
@ -40,7 +40,7 @@ public class DataSourceIngestModuleProgress {
* data source.
*/
public void switchToDeterminate(int workUnits) {
progress.switchToDeterminate(workUnits);
this.job.switchDataSourceIngestProgressBarToDeterminate(workUnits);
}
/**
@ -48,7 +48,7 @@ public class DataSourceIngestModuleProgress {
* the total work units to process the data source is unknown.
*/
public void switchToIndeterminate() {
progress.switchToIndeterminate();
this.job.switchDataSourceIngestProgressBarToIndeterminate();
}
/**
@ -58,17 +58,18 @@ public class DataSourceIngestModuleProgress {
* @param workUnits Number of work units performed so far by the module.
*/
public void progress(int workUnits) {
progress.progress("", workUnits);
this.job.advanceDataSourceIngestProgressBar("", workUnits);
}
/**
* Updates the sub-title on the progress bar
*
* @param message Message to display
*/
public void progress(String message) {
progress.progress(message);
this.job.advanceDataSourceIngestProgressBar(message);
}
/**
* Updates the progress bar with the number of work units performed, if in
* the determinate mode.
@ -77,6 +78,7 @@ public class DataSourceIngestModuleProgress {
* @param workUnits Number of work units performed so far by the module.
*/
public void progress(String message, int workUnits) {
progress.progress(message, workUnits);
this.job.advanceDataSourceIngestProgressBar(message, workUnits);
}
}
}

View File

@ -19,52 +19,31 @@
package org.sleuthkit.autopsy.ingest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.Content;
/**
* This class manages a sequence of data source ingest modules. It starts them,
* shuts them down, and runs them in sequential order.
* This class manages a sequence of data source ingest modules. It starts them,
* shuts them down, and runs them in sequential order.
*/
final class DataSourceIngestPipeline {
private static final IngestManager ingestManager = IngestManager.getInstance();
private final IngestJobContext context;
private final IngestJob job;
private final List<DataSourceIngestModuleDecorator> modules = new ArrayList<>();
DataSourceIngestPipeline(IngestJobContext context, List<IngestModuleTemplate> moduleTemplates) {
this.context = context;
DataSourceIngestPipeline(IngestJob job, List<IngestModuleTemplate> moduleTemplates) {
this.job = job;
// Create an ingest module instance from each data source ingest module
// template. Put the modules in a map of module class names to module
// instances to facilitate loading the modules into the pipeline in the
// sequence indicated by the ordered list of module class names that
// will be obtained from the data source ingest pipeline configuration.
Map<String, DataSourceIngestModuleDecorator> modulesByClass = new HashMap<>();
// template.
for (IngestModuleTemplate template : moduleTemplates) {
if (template.isDataSourceIngestModuleTemplate()) {
DataSourceIngestModuleDecorator module = new DataSourceIngestModuleDecorator(template.createDataSourceIngestModule(), template.getModuleName());
modulesByClass.put(module.getClassName(), module);
modules.add(module);
}
}
// Add the ingest modules to the pipeline in the order indicated by the
// data source ingest pipeline configuration, adding any additional
// modules found in the global lookup, but not mentioned in the
// configuration, to the end of the pipeline in arbitrary order.
List<String> pipelineConfig = IngestPipelinesConfiguration.getInstance().getDataSourceIngestPipelineConfig();
for (String moduleClassName : pipelineConfig) {
if (modulesByClass.containsKey(moduleClassName)) {
modules.add(modulesByClass.remove(moduleClassName));
}
}
for (DataSourceIngestModuleDecorator module : modulesByClass.values()) {
modules.add(module);
}
}
boolean isEmpty() {
@ -75,7 +54,7 @@ final class DataSourceIngestPipeline {
List<IngestModuleError> errors = new ArrayList<>();
for (DataSourceIngestModuleDecorator module : modules) {
try {
module.startUp(context);
module.startUp(new IngestJobContext(this.job));
} catch (Exception ex) { // Catch-all exception firewall
errors.add(new IngestModuleError(module.getDisplayName(), ex));
}
@ -83,21 +62,24 @@ final class DataSourceIngestPipeline {
return errors;
}
List<IngestModuleError> process(DataSourceIngestTask task, ProgressHandle progress) {
List<IngestModuleError> process(DataSourceIngestTask task) {
List<IngestModuleError> errors = new ArrayList<>();
Content dataSource = task.getDataSource();
for (DataSourceIngestModuleDecorator module : modules) {
try {
progress.setDisplayName(NbBundle.getMessage(this.getClass(),
String displayName = NbBundle.getMessage(this.getClass(),
"IngestJob.progress.dataSourceIngest.displayName",
module.getDisplayName(), dataSource.getName()));
module.getDisplayName(), dataSource.getName());
this.job.updateDataSourceIngestProgressBarDisplayName(displayName);
ingestManager.setIngestTaskProgress(task, module.getDisplayName());
module.process(dataSource, new DataSourceIngestModuleProgress(progress));
module.process(dataSource, new DataSourceIngestModuleProgress(this.job));
} catch (Exception ex) { // Catch-all exception firewall
errors.add(new IngestModuleError(module.getDisplayName(), ex));
}
if (context.isJobCancelled()) {
if (this.job.isCancelled()) {
break;
} else if (this.job.currentDataSourceIngestModuleIsCancelled()) {
this.job.currentDataSourceIngestModuleCancellationCompleted();
}
}
ingestManager.setIngestTaskProgressCompleted(task);

View File

@ -0,0 +1,48 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.ingest;
import org.sleuthkit.autopsy.coreutils.ExecUtil;
/**
* An ExecUtil process terminator for data source ingest modules that checks for
* ingest job cancellation.
*/
public final class FileIngestModuleProcessTerminator implements ExecUtil.ProcessTerminator {
private final IngestJobContext context;
/**
* Constructs a process terminator for a file ingest module.
*
* @param context The ingest job context for the ingest module.
*/
public FileIngestModuleProcessTerminator(IngestJobContext context) {
this.context = context;
}
/**
* @inheritDoc
*/
@Override
public boolean shouldTerminateProcess() {
return this.context.fileIngestIsCancelled();
}
}

View File

@ -29,46 +29,24 @@ import org.sleuthkit.datamodel.AbstractFile;
final class FileIngestPipeline {
private static final IngestManager ingestManager = IngestManager.getInstance();
private final IngestJobContext context;
private final IngestJob job;
private final List<FileIngestModuleDecorator> modules = new ArrayList<>();
FileIngestPipeline(IngestJobContext context, List<IngestModuleTemplate> moduleTemplates) {
this.context = context;
FileIngestPipeline(IngestJob job, List<IngestModuleTemplate> moduleTemplates) {
this.job = job;
// Create an ingest module instance from each file ingest module
// template.
// current code uses the order pased in.
// Commented out code relied on the XML configuration file for ordering.
//Map<String, FileIngestModuleDecorator> modulesByClass = new HashMap<>();
for (IngestModuleTemplate template : moduleTemplates) {
if (template.isFileIngestModuleTemplate()) {
FileIngestModuleDecorator module = new FileIngestModuleDecorator(template.createFileIngestModule(), template.getModuleName());
modules.add(module);
//modulesByClass.put(module.getClassName(), module);
}
}
// Add the ingest modules to the pipeline in the order indicated by the
// data source ingest pipeline configuration, adding any additional
// modules found in the global lookup, but not mentioned in the
// configuration, to the end of the pipeline in arbitrary order.
/*List<String> pipelineConfig = IngestPipelinesConfiguration.getInstance().getFileIngestPipelineConfig();
for (String moduleClassName : pipelineConfig) {
if (modulesByClass.containsKey(moduleClassName)) {
modules.add(modulesByClass.remove(moduleClassName));
}
else {
// @@@ add error message to flag renamed / removed modules
}
}
for (FileIngestModuleDecorator module : modulesByClass.values()) {
modules.add(module);
}
*/
}
boolean isEmpty() {
return modules.isEmpty();
return this.modules.isEmpty();
}
/**
@ -77,9 +55,9 @@ final class FileIngestPipeline {
*/
List<IngestModuleError> startUp() {
List<IngestModuleError> errors = new ArrayList<>();
for (FileIngestModuleDecorator module : modules) {
for (FileIngestModuleDecorator module : this.modules) {
try {
module.startUp(context);
module.startUp(new IngestJobContext(this.job));
} catch (Exception ex) { // Catch-all exception firewall
errors.add(new IngestModuleError(module.getDisplayName(), ex));
}
@ -97,28 +75,28 @@ final class FileIngestPipeline {
List<IngestModuleError> process(FileIngestTask task) {
List<IngestModuleError> errors = new ArrayList<>();
AbstractFile file = task.getFile();
for (FileIngestModuleDecorator module : modules) {
for (FileIngestModuleDecorator module : this.modules) {
try {
ingestManager.setIngestTaskProgress(task, module.getDisplayName());
FileIngestPipeline.ingestManager.setIngestTaskProgress(task, module.getDisplayName());
module.process(file);
} catch (Exception ex) { // Catch-all exception firewall
errors.add(new IngestModuleError(module.getDisplayName(), ex));
}
if (context.isJobCancelled()) {
if (this.job.isCancelled()) {
break;
}
}
file.close();
if (!context.isJobCancelled()) {
if (!this.job.isCancelled()) {
IngestManager.getInstance().fireFileIngestDone(file);
}
ingestManager.setIngestTaskProgressCompleted(task);
FileIngestPipeline.ingestManager.setIngestTaskProgressCompleted(task);
return errors;
}
List<IngestModuleError> shutDown() {
List<IngestModuleError> errors = new ArrayList<>();
for (FileIngestModuleDecorator module : modules) {
for (FileIngestModuleDecorator module : this.modules) {
try {
module.shutDown();
} catch (Exception ex) {

View File

@ -23,6 +23,7 @@ import java.util.Date;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import javax.swing.JOptionPane;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.util.Cancellable;
@ -32,50 +33,90 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
/**
* IngestJob encapsulates the settings, ingest module pipelines, and progress
* bars that are used to process a data source when a user chooses to run a set
* of ingest modules on the data source.
* Encapsulates a data source to be processed and the settings, ingest module
* pipelines, and progress bars that are used to process it.
*/
final class IngestJob {
private static final Logger logger = Logger.getLogger(IngestManager.class.getName());
private static final IngestScheduler ingestTaskScheduler = IngestScheduler.getInstance();
private static final Logger logger = Logger.getLogger(IngestJob.class.getName());
private static final IngestScheduler ingestScheduler = IngestScheduler.getInstance();
// These fields define the ingest job and the work it entails.
private final long id;
private final Content dataSource;
private final boolean processUnallocatedSpace;
private final LinkedBlockingQueue<FileIngestPipeline> fileIngestPipelines = new LinkedBlockingQueue<>();
private long estimatedFilesToProcess = 0L; // Guarded by this
private long processedFiles = 0L; // Guarded by this
// Contains names of files that are being run. Used to keep progress bar up to date.
private final List<String> filesInProgress = new ArrayList<>(); // Guarded by this.
private DataSourceIngestPipeline dataSourceIngestPipeline;
private final LinkedBlockingQueue<FileIngestPipeline> fileIngestPipelines;
// These fields are used to update the ingest progress UI components. The
// filesInProgress collection contains the names of the files that are in
// the file ingest pipelines and the two file counter fields are used to
// update the file ingest progress bar.
private ProgressHandle dataSourceIngestProgress;
private final Object dataSourceIngestProgressLock;
private final List<String> filesInProgress;
private long estimatedFilesToProcess;
private long processedFiles;
private ProgressHandle fileIngestProgress;
private volatile boolean cancelled = false;
private long startTime;
private final Object fileIngestProgressLock;
// These fields support cancellation of either the currently running data
// source ingest module or the entire ingest job.
private volatile boolean currentDataSourceIngestModuleCancelled;
private volatile boolean cancelled;
// This field is used for generating ingest job diagnostic data.
private final long startTime;
/**
* Constructs an ingest job.
*
* @param id The identifier assigned to the job.
* @param dataSource The data source to be ingested.
* @param processUnallocatedSpace Whether or not unallocated space should be
* processed during the ingest job.
*/
IngestJob(long id, Content dataSource, boolean processUnallocatedSpace) {
this.id = id;
this.dataSource = dataSource;
this.processUnallocatedSpace = processUnallocatedSpace;
startTime = new Date().getTime();
}
long getId() {
return id;
}
Content getDataSource() {
return dataSource;
}
boolean shouldProcessUnallocatedSpace() {
return processUnallocatedSpace;
this.fileIngestPipelines = new LinkedBlockingQueue<>();
this.filesInProgress = new ArrayList<>();
this.dataSourceIngestProgressLock = new Object();
this.fileIngestProgressLock = new Object();
this.startTime = new Date().getTime();
}
/**
* Startup the ingest pipelines and progress bars.
* Gets the identifier assigned to the ingest job.
*
* @return The ingest job identifier.
*/
long getId() {
return this.id;
}
/**
* Gets the data source to be ingested by this job.
*
* @return A reference to a Content object representing the data source.
*/
Content getDataSource() {
return this.dataSource;
}
/**
* Queries whether or not unallocated space should be processed as part of
* this job.
*
* @return True or false.
*/
boolean shouldProcessUnallocatedSpace() {
return this.processUnallocatedSpace;
}
/**
* Starts up the ingest pipelines and ingest progress bars.
*
* @return A collection of ingest module startup errors, empty on success.
* @throws InterruptedException
@ -84,10 +125,10 @@ final class IngestJob {
createIngestPipelines(ingestModuleTemplates);
List<IngestModuleError> errors = startUpIngestPipelines();
if (errors.isEmpty()) {
if (!dataSourceIngestPipeline.isEmpty()) {
if (!this.dataSourceIngestPipeline.isEmpty()) {
startDataSourceIngestProgressBar();
}
if (!fileIngestPipelines.peek().isEmpty()) {
if (!this.fileIngestPipelines.peek().isEmpty()) {
startFileIngestProgressBar();
}
}
@ -95,23 +136,22 @@ final class IngestJob {
}
/**
* Create the file and data source ingest pipelines.
* Creates the file and data source ingest pipelines.
*
* @param ingestModuleTemplates Ingest module templates to use to populate
* the pipelines.
* @throws InterruptedException
*/
private void createIngestPipelines(List<IngestModuleTemplate> ingestModuleTemplates) throws InterruptedException {
IngestJobContext context = new IngestJobContext(this);
dataSourceIngestPipeline = new DataSourceIngestPipeline(context, ingestModuleTemplates);
int numberOfPipelines = IngestManager.getInstance().getNumberOfFileIngestThreads();
for (int i = 0; i < numberOfPipelines; ++i) {
fileIngestPipelines.put(new FileIngestPipeline(context, ingestModuleTemplates));
this.dataSourceIngestPipeline = new DataSourceIngestPipeline(this, ingestModuleTemplates);
int numberOfFileIngestThreads = IngestManager.getInstance().getNumberOfFileIngestThreads();
for (int i = 0; i < numberOfFileIngestThreads; ++i) {
this.fileIngestPipelines.put(new FileIngestPipeline(this, ingestModuleTemplates));
}
}
/**
* Startup each of the file and data source ingest modules to collect
* Starts up each of the file and data source ingest modules to collect
* possible errors.
*
* @return A collection of ingest module startup errors, empty on success.
@ -119,13 +159,24 @@ final class IngestJob {
*/
private List<IngestModuleError> startUpIngestPipelines() throws InterruptedException {
List<IngestModuleError> errors = new ArrayList<>();
errors.addAll(dataSourceIngestPipeline.startUp());
for (FileIngestPipeline pipeline : fileIngestPipelines) {
// Start up the data source ingest pipeline.
errors.addAll(this.dataSourceIngestPipeline.startUp());
// Start up the file ingest pipelines (one per file ingest thread).
for (FileIngestPipeline pipeline : this.fileIngestPipelines) {
errors.addAll(pipeline.startUp());
if (!errors.isEmpty()) {
// No need to accumulate presumably redundant errors.
while (!fileIngestPipelines.isEmpty()) {
pipeline = fileIngestPipelines.poll();
// If there are start up errors, the ingest job will not proceed
// and the errors will ultimately be reported to the user for
// possible remedy so shut down the pipelines now that an
// attempt has been made to start up the data source ingest
// pipeline and at least one copy of the file ingest pipeline.
// pipeline. There is no need to complete starting up all of the
// file ingest pipeline copies since any additional start up
// errors are likely redundant.
while (!this.fileIngestPipelines.isEmpty()) {
pipeline = this.fileIngestPipelines.poll();
List<IngestModuleError> shutDownErrors = pipeline.shutDown();
if (!shutDownErrors.isEmpty()) {
logIngestModuleErrors(shutDownErrors);
@ -134,259 +185,472 @@ final class IngestJob {
break;
}
}
logIngestModuleErrors(errors);
return errors;
}
private void startDataSourceIngestProgressBar() {
final String displayName = NbBundle.getMessage(this.getClass(),
"IngestJob.progress.dataSourceIngest.initialDisplayName",
dataSource.getName());
dataSourceIngestProgress = ProgressHandleFactory.createHandle(displayName, new Cancellable() {
@Override
public boolean cancel() {
handleCancelFromProgressBar();
return true;
}
});
dataSourceIngestProgress.start();
dataSourceIngestProgress.switchToIndeterminate();
}
private void startFileIngestProgressBar() {
final String displayName = NbBundle.getMessage(this.getClass(),
"IngestJob.progress.fileIngest.displayName",
dataSource.getName());
fileIngestProgress = ProgressHandleFactory.createHandle(displayName, new Cancellable() {
@Override
public boolean cancel() {
handleCancelFromProgressBar();
return true;
}
});
estimatedFilesToProcess = dataSource.accept(new GetFilesCountVisitor());
fileIngestProgress.start();
fileIngestProgress.switchToDeterminate((int) estimatedFilesToProcess);
}
/**
* Common steps to cancel a job, regardless of what progress bar was
* used to cancel the job.
* Starts the data source ingest progress bar.
*/
private void handleCancelFromProgressBar() {
cancel();
if (fileIngestProgress != null) {
final String displayName = NbBundle.getMessage(this.getClass(),
"IngestJob.progress.fileIngest.displayName",
dataSource.getName());
fileIngestProgress.setDisplayName(
NbBundle.getMessage(this.getClass(), "IngestJob.progress.cancelling",
displayName));
private void startDataSourceIngestProgressBar() {
synchronized (this.dataSourceIngestProgressLock) {
String displayName = NbBundle.getMessage(this.getClass(),
"IngestJob.progress.dataSourceIngest.initialDisplayName",
this.dataSource.getName());
this.dataSourceIngestProgress = ProgressHandleFactory.createHandle(displayName, new Cancellable() {
@Override
public boolean cancel() {
// If this method is called, the user has already pressed
// the cancel button on the progress bar and the OK button
// of a cancelation confirmation dialog supplied by
// NetBeans. What remains to be done is to find out whether
// the user wants to cancel only the currently executing
// data source ingest module or the entire ingest job.
DataSourceIngestCancellationPanel panel = new DataSourceIngestCancellationPanel();
String dialogTitle = NbBundle.getMessage(IngestJob.this.getClass(), "IngestJob.cancellationDialog.title");
JOptionPane.showConfirmDialog(null, panel, dialogTitle, JOptionPane.OK_OPTION, JOptionPane.PLAIN_MESSAGE);
if (panel.cancelAllDataSourceIngestModules()) {
IngestJob.this.cancel();
} else {
IngestJob.this.cancelCurrentDataSourceIngestModule();
}
return true;
}
});
this.dataSourceIngestProgress.start();
this.dataSourceIngestProgress.switchToIndeterminate();
}
if (dataSourceIngestProgress != null) {
final String displayName = NbBundle.getMessage(this.getClass(),
"IngestJob.progress.dataSourceIngest.initialDisplayName",
dataSource.getName());
dataSourceIngestProgress.setDisplayName(
NbBundle.getMessage(this.getClass(),
"IngestJob.progress.cancelling",
displayName));
}
IngestScheduler.getInstance().cancelIngestJob(IngestJob.this);
}
/**
* Check to see if the job has a data source ingest pipeline.
* Starts the file ingest progress bar.
*/
private void startFileIngestProgressBar() {
synchronized (this.fileIngestProgressLock) {
String displayName = NbBundle.getMessage(this.getClass(),
"IngestJob.progress.fileIngest.displayName",
this.dataSource.getName());
this.fileIngestProgress = ProgressHandleFactory.createHandle(displayName, new Cancellable() {
@Override
public boolean cancel() {
// If this method is called, the user has already pressed
// the cancel button on the progress bar and the OK button
// of a cancelation confirmation dialog supplied by
// NetBeans.
IngestJob.this.cancel();
return true;
}
});
this.estimatedFilesToProcess = this.dataSource.accept(new GetFilesCountVisitor());
this.fileIngestProgress.start();
this.fileIngestProgress.switchToDeterminate((int) this.estimatedFilesToProcess);
}
}
/**
* Checks to see if this job has a data source ingest pipeline.
*
* @return True or false.
*/
boolean hasDataSourceIngestPipeline() {
return (dataSourceIngestPipeline.isEmpty() == false);
return (this.dataSourceIngestPipeline.isEmpty() == false);
}
/**
* Check to see if the job has a file ingest pipeline.
* Checks to see if the job has a file ingest pipeline.
*
* @return True or false.
*/
boolean hasFileIngestPipeline() {
return (fileIngestPipelines.peek().isEmpty() == false);
return (this.fileIngestPipelines.peek().isEmpty() == false);
}
/**
* Executes the data source ingest module pipeline for the data source in the task
* @param task
* @throws InterruptedException
* Passes the data source for this job through the data source ingest
* pipeline.
*
* @param task A data source ingest task wrapping the data source.
* @throws InterruptedException
*/
void process(DataSourceIngestTask task) throws InterruptedException {
try {
if (!isCancelled() && !dataSourceIngestPipeline.isEmpty()) {
if (!this.isCancelled() && !this.dataSourceIngestPipeline.isEmpty()) {
List<IngestModuleError> errors = new ArrayList<>();
errors.addAll(dataSourceIngestPipeline.process(task, dataSourceIngestProgress));
errors.addAll(this.dataSourceIngestPipeline.process(task));
if (!errors.isEmpty()) {
logIngestModuleErrors(errors);
}
}
// all data source ingest jobs are done for this task, so shut down progress bar
if (null != dataSourceIngestProgress) {
dataSourceIngestProgress.finish();
// This is safe because this method will be called at most once per
// ingest job and finish() will not be called while that single
// data source ingest task has not been reported complete by this
// code to the ingest scheduler.
dataSourceIngestProgress = null;
}
}
finally {
ingestTaskScheduler.notifyTaskCompleted(task);
// The single data source ingest task for this job is done, so shut
// down the data source ingest progress bar right away.
synchronized (this.dataSourceIngestProgressLock) {
if (null != this.dataSourceIngestProgress) {
this.dataSourceIngestProgress.finish();
this.dataSourceIngestProgress = null;
}
}
} finally {
// No matter what happens, let the ingest scheduler know that this
// task is completed.
IngestJob.ingestScheduler.notifyTaskCompleted(task);
}
}
/**
* Executes the file ingest module pipeline for the file in the task
* @param task
* @throws InterruptedException
* Updates the display name of the data source ingest progress bar.
*
* @param displayName The new display name.
*/
void updateDataSourceIngestProgressBarDisplayName(String displayName) {
if (!this.cancelled) {
synchronized (this.dataSourceIngestProgressLock) {
this.dataSourceIngestProgress.setDisplayName(displayName);
}
}
}
/**
* Updates the data source progress bar and switches it to determinate mode.
*
* @param workUnits Total number of work units for the processing of the
* data source.
*/
void switchDataSourceIngestProgressBarToDeterminate(int workUnits) {
if (!this.cancelled) {
synchronized (this.dataSourceIngestProgressLock) {
this.dataSourceIngestProgress.switchToDeterminate(workUnits);
}
}
}
/**
* Switches the data source ingest progress bar to indeterminate mode. This
* should be called if the total work units to process the data source is
* unknown.
*/
void switchDataSourceIngestProgressBarToIndeterminate() {
if (!this.cancelled) {
synchronized (this.dataSourceIngestProgressLock) {
this.dataSourceIngestProgress.switchToIndeterminate();
}
}
}
/**
* Updates the data source ingest progress bar with the number of work units
* performed, if in the determinate mode.
*
* @param workUnits Number of work units performed.
*/
void advanceDataSourceIngestProgressBar(int workUnits) {
if (!this.cancelled) {
synchronized (this.dataSourceIngestProgressLock) {
this.dataSourceIngestProgress.progress("", workUnits);
}
}
}
/**
* Updates the data source ingest progress bar display name.
*
* @param displayName The new display name.
*/
void advanceDataSourceIngestProgressBar(String displayName) {
if (!this.cancelled) {
synchronized (this.dataSourceIngestProgressLock) {
this.dataSourceIngestProgress.progress(displayName);
}
}
}
/**
* Updates the progress bar with the number of work units performed, if in
* the determinate mode.
*
* @param message Message to display in sub-title
* @param workUnits Number of work units performed.
*/
void advanceDataSourceIngestProgressBar(String message, int workUnits) {
if (!this.cancelled) {
synchronized (this.fileIngestProgressLock) {
this.dataSourceIngestProgress.progress(message, workUnits);
}
}
}
/**
* Passes the a file from the data source for this job through the file
* ingest pipeline.
*
* @param task A file ingest task.
* @throws InterruptedException
*/
void process(FileIngestTask task) throws InterruptedException {
try {
if (!isCancelled()) {
FileIngestPipeline pipeline = fileIngestPipelines.take();
if (!this.isCancelled()) {
// Get a file ingest pipeline not currently in use by another
// file ingest thread.
FileIngestPipeline pipeline = this.fileIngestPipelines.take();
if (!pipeline.isEmpty()) {
// Get the file to process.
AbstractFile file = task.getFile();
synchronized (this) {
++processedFiles;
if (processedFiles <= estimatedFilesToProcess) {
fileIngestProgress.progress(file.getName(), (int) processedFiles);
// Update the file ingest progress bar.
synchronized (this.fileIngestProgressLock) {
++this.processedFiles;
if (this.processedFiles <= this.estimatedFilesToProcess) {
this.fileIngestProgress.progress(file.getName(), (int) this.processedFiles);
} else {
fileIngestProgress.progress(file.getName(), (int) estimatedFilesToProcess);
this.fileIngestProgress.progress(file.getName(), (int) this.estimatedFilesToProcess);
}
filesInProgress.add(file.getName());
this.filesInProgress.add(file.getName());
}
// Run the file through the pipeline.
List<IngestModuleError> errors = new ArrayList<>();
errors.addAll(pipeline.process(task));
if (!errors.isEmpty()) {
logIngestModuleErrors(errors);
}
// update the progress bar in case this file was being displayed
synchronized (this) {
filesInProgress.remove(file.getName());
if (filesInProgress.size() > 0) {
fileIngestProgress.progress(filesInProgress.get(0));
}
else {
fileIngestProgress.progress("");
// Update the file ingest progress bar again in case the
// file was being displayed.
if (!this.cancelled) {
synchronized (this.fileIngestProgressLock) {
this.filesInProgress.remove(file.getName());
if (this.filesInProgress.size() > 0) {
this.fileIngestProgress.progress(this.filesInProgress.get(0));
} else {
this.fileIngestProgress.progress("");
}
}
}
}
fileIngestPipelines.put(pipeline);
// Relinquish the pipeline so it can be reused by another file
// ingest thread.
this.fileIngestPipelines.put(pipeline);
}
}
finally {
ingestTaskScheduler.notifyTaskCompleted(task);
} finally {
// No matter what happens, let the ingest scheduler know that this
// task is completed.
IngestJob.ingestScheduler.notifyTaskCompleted(task);
}
}
/**
* Shutdown pipelines and progress bars
*
* Shuts down the ingest pipelines and progress bars for this job.
*/
void finish() {
// Shut down the file ingest pipelines. Note that no shut down is
// required for the data source ingest pipeline because data source
// ingest modules do not have a shutdown() method.
List<IngestModuleError> errors = new ArrayList<>();
while (!fileIngestPipelines.isEmpty()) {
while (!this.fileIngestPipelines.isEmpty()) {
FileIngestPipeline pipeline = fileIngestPipelines.poll();
errors.addAll(pipeline.shutDown());
}
if (!errors.isEmpty()) {
logIngestModuleErrors(errors);
}
// NOTE: Nothing to do for datasource ingest modules -- no shutdown method
if (dataSourceIngestProgress != null) {
dataSourceIngestProgress.finish();
}
if (fileIngestProgress != null) {
fileIngestProgress.finish();
}
}
private void logIngestModuleErrors(List<IngestModuleError> errors) {
for (IngestModuleError error : errors) {
logger.log(Level.SEVERE, error.getModuleDisplayName() + " experienced an error", error.getModuleError()); //NON-NLS
}
}
boolean isCancelled() {
return cancelled;
}
void cancel() {
cancelled = true;
}
/**
* Stores basic info for a given ingest job.
*/
class IngestJobStats {
private final long startTime;
private final long processedFiles;
private final long estimatedFilesToProcess;
private final long snapShotTime;
IngestJobStats () {
synchronized (IngestJob.this) {
this.startTime = IngestJob.this.startTime;
this.processedFiles = IngestJob.this.processedFiles;
this.estimatedFilesToProcess = IngestJob.this.estimatedFilesToProcess;
snapShotTime = new Date().getTime();
// Finish the data source ingest progress bar, if it hasn't already
// been finished.
synchronized (this.dataSourceIngestProgressLock) {
if (this.dataSourceIngestProgress != null) {
this.dataSourceIngestProgress.finish();
this.dataSourceIngestProgress = null;
}
}
/**
* Get files per second throughput since job started
* @return
*/
double getSpeed() {
return (double)processedFiles / ((snapShotTime - startTime)/1000);
}
long getStartTime() {
return startTime;
}
/**
* Get time these stats were collected
* @return
*/
long getSnapshotTime() {
return snapShotTime;
}
/**
* Number of files processed by job so far
* @return
*/
long getFilesProcessed() {
return processedFiles;
}
long getFilesEstimated() {
return estimatedFilesToProcess;
// Finish the file ingest progress bar, if it hasn't already
// been finished.
synchronized (this.fileIngestProgressLock) {
if (this.fileIngestProgress != null) {
this.fileIngestProgress.finish();
this.fileIngestProgress = null;
}
}
}
/**
* Get some basic performance stats on this job
* @return
* Write ingest module errors to the log.
*
* @param errors The errors.
*/
private void logIngestModuleErrors(List<IngestModuleError> errors) {
for (IngestModuleError error : errors) {
IngestJob.logger.log(Level.SEVERE, error.getModuleDisplayName() + " experienced an error", error.getModuleError()); //NON-NLS
}
}
/**
* Requests a temporary cancellation of data source ingest in order to stop
* the currently executing data source ingest module.
*/
private void cancelCurrentDataSourceIngestModule() {
this.currentDataSourceIngestModuleCancelled = true;
}
/**
* Determines whether or not a temporary cancellation of data source ingest
* in order to stop the currently executing data source ingest module is in
* effect.
*
* @return True or false.
*/
boolean currentDataSourceIngestModuleIsCancelled() {
return this.currentDataSourceIngestModuleCancelled;
}
/**
* Rescind a temporary cancellation of data source ingest in order to stop
* the currently executing data source ingest module.
*/
void currentDataSourceIngestModuleCancellationCompleted() {
this.currentDataSourceIngestModuleCancelled = false;
// A new progress bar must be created because the cancel button of the
// previously constructed component is disabled by NetBeans when the
// user selects the "OK" button of the cancellation confirmation dialog
// popped up by NetBeans when the progress bar cancel button was
// pressed.
synchronized (this.dataSourceIngestProgressLock) {
this.dataSourceIngestProgress.finish();
this.dataSourceIngestProgress = null;
this.startDataSourceIngestProgressBar();
}
}
/**
* Requests cancellation of ingest, i.e., a shutdown of the data source and
* file ingest pipelines.
*/
void cancel() {
// Put a cancellation message on data source ingest progress bar,
// if it is still running.
synchronized (this.dataSourceIngestProgressLock) {
if (dataSourceIngestProgress != null) {
final String displayName = NbBundle.getMessage(this.getClass(),
"IngestJob.progress.dataSourceIngest.initialDisplayName",
dataSource.getName());
dataSourceIngestProgress.setDisplayName(
NbBundle.getMessage(this.getClass(),
"IngestJob.progress.cancelling",
displayName));
}
}
// Put a cancellation message on the file ingest progress bar,
// if it is still running.
synchronized (this.fileIngestProgressLock) {
if (this.fileIngestProgress != null) {
final String displayName = NbBundle.getMessage(this.getClass(),
"IngestJob.progress.fileIngest.displayName",
this.dataSource.getName());
this.fileIngestProgress.setDisplayName(
NbBundle.getMessage(this.getClass(), "IngestJob.progress.cancelling",
displayName));
}
}
this.cancelled = true;
// Tell the ingest scheduler to cancel all pending tasks.
IngestJob.ingestScheduler.cancelPendingTasksForIngestJob(this);
}
/**
* Queries whether or not cancellation of ingest i.e., a shutdown of the
* data source and file ingest pipelines, has been requested
*
* @return True or false.
*/
boolean isCancelled() {
return this.cancelled;
}
/**
* Get some basic performance statistics on this job.
*
* @return An ingest job statistics object.
*/
IngestJobStats getStats() {
return new IngestJobStats();
}
/**
* Stores basic diagnostic statistics for an ingest job.
*/
class IngestJobStats {
private final long startTime;
private final long processedFiles;
private final long estimatedFilesToProcess;
private final long snapShotTime;
/**
* Constructs an object to stores basic diagnostic statistics for an
* ingest job.
*/
IngestJobStats() {
this.startTime = IngestJob.this.startTime;
synchronized (IngestJob.this.fileIngestProgressLock) {
this.processedFiles = IngestJob.this.processedFiles;
this.estimatedFilesToProcess = IngestJob.this.estimatedFilesToProcess;
this.snapShotTime = new Date().getTime();
}
}
/**
* Gets files per second throughput since job started.
*
* @return Files processed per second (approximate).
*/
double getSpeed() {
return (double) processedFiles / ((snapShotTime - startTime) / 1000);
}
/**
* Gets the the ingest job was started.
*
* @return The start time as number of milliseconds since January 1,
* 1970, 00:00:00 GMT.
*/
long getStartTime() {
return startTime;
}
/**
* Gets time these statistics were collected.
*
* @return The statistics collection time as number of milliseconds
* since January 1, 1970, 00:00:00 GMT.
*/
long getSnapshotTime() {
return snapShotTime;
}
/**
* Gets the number of files processed for the job so far.
*
* @return The number of processed files.
*/
long getFilesProcessed() {
return processedFiles;
}
/**
* Gets an estimate of the files that still need to be processed for
* this job.
*
* @return The estimate.
*/
long getFilesEstimated() {
return estimatedFilesToProcess;
}
}
}

View File

@ -125,9 +125,9 @@ public final class IngestJobConfigurator {
ModuleSettings.setConfigSetting(launcherContext, DISABLED_INGEST_MODULES_KEY, makeCommaSeparatedList(disabledModuleNames));
// Get the process unallocated space flag setting. If the setting does
// not exist yet, default it to false.
// not exist yet, default it to true.
if (ModuleSettings.settingExists(launcherContext, PARSE_UNALLOC_SPACE_KEY) == false) {
ModuleSettings.setConfigSetting(launcherContext, PARSE_UNALLOC_SPACE_KEY, "false"); //NON-NLS
ModuleSettings.setConfigSetting(launcherContext, PARSE_UNALLOC_SPACE_KEY, "true"); //NON-NLS
}
boolean processUnallocatedSpace = Boolean.parseBoolean(ModuleSettings.getConfigSetting(launcherContext, PARSE_UNALLOC_SPACE_KEY));

View File

@ -25,23 +25,26 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
/**
* Provides an instance of an ingest module with services specific to the ingest
* job and the ingest pipeline of which the module is a part.
* Provides an ingest module with services specific to the ingest job of which
* the module is a part.
*/
public final class IngestJobContext {
private static final Logger logger = Logger.getLogger(IngestJobContext.class.getName());
private static final IngestScheduler scheduler = IngestScheduler.getInstance();
private final IngestJob ingestJob;
IngestJobContext(IngestJob ingestJob) {
this.ingestJob = ingestJob;
}
/**
*
* @return The data source that this ingest job is associated with.
* Gets the data source associated with this context.
*
* @return The data source.
*/
public Content getDataSource() {
return ingestJob.getDataSource();
return this.ingestJob.getDataSource();
}
/**
@ -54,32 +57,70 @@ public final class IngestJobContext {
}
/**
* Determines whether the ingest job associated with the current context has
* been canceled.
* Queries whether or not cancellation of the data source ingest part of the
* ingest job associated with this context has been requested.
*
* @return True if the job has been canceled, false otherwise.
* @return True or false.
*
* @deprecated Use dataSourceIngestIsCancelled() or fileIngestIsCancelled()
* instead.
*/
@Deprecated
public boolean isJobCancelled() {
return this.dataSourceIngestIsCancelled();
}
/**
* Allows a data source ingest module to determine whether or not
* cancellation of the data source ingest part of the ingest job associated
* with this context has been requested.
*
* @return True or false.
*/
public boolean dataSourceIngestIsCancelled() {
return this.ingestJob.currentDataSourceIngestModuleIsCancelled() || this.ingestJob.isCancelled();
}
/**
* Allows a file ingest module to determine whether or not cancellation of
* the file ingest part of the ingest job associated with this context has
* been requested.
*
* @return True or false.
*/
public boolean fileIngestIsCancelled() {
return this.ingestJob.isCancelled();
}
/**
* Queries whether or not unallocated space should be processed for the
* ingest job associated with this context.
*
* @return True or false.
*/
public boolean processingUnallocatedSpace() {
return this.ingestJob.shouldProcessUnallocatedSpace();
}
/**
* Adds one or more files to the files to be passed through the file ingest
* pipeline of the ingest job associated with the current context.
* pipeline of the ingest job associated with this context.
*
* @param files The files to be processed by the file ingest pipeline.
*/
public void scheduleFiles(List<AbstractFile> files) {
for (AbstractFile file : files) {
try {
IngestScheduler.getInstance().scheduleAdditionalFileIngestTask(ingestJob, file);
IngestJobContext.scheduler.scheduleAdditionalFileIngestTask(this.ingestJob, file);
} catch (InterruptedException ex) {
// Ultimately, this method is called by ingest task execution
// threads running ingest module code. Handle the unexpected
// interrupt here rather
// Handle the unexpected interrupt here rather than make ingest
// module writers responsible for writing this exception handler.
// The interrupt flag of the thread is reset for detection by
// the thread task code.
Thread.currentThread().interrupt();
Logger.getLogger(IngestJobContext.class.getName()).log(Level.SEVERE, "File task scheduling unexpectedly interrupted", ex); //NON-NLS
IngestJobContext.logger.log(Level.SEVERE, "File task scheduling unexpectedly interrupted", ex); //NON-NLS
}
}
}
}

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.TreeMap;
import java.util.logging.Level;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
@ -41,14 +42,17 @@ import org.sleuthkit.autopsy.modules.sevenzip.ArchiveFileExtractorModuleFactory;
import org.sleuthkit.autopsy.python.JythonModuleLoader;
/**
* Discovers ingest module factories implemented in Java or Jython.
* Discovers and instantiates ingest module factories.
*/
final class IngestModuleFactoryLoader {
private static final Logger logger = Logger.getLogger(IngestModuleFactoryLoader.class.getName());
private static final String SAMPLE_MODULE_FACTORY_CLASS_NAME = SampleIngestModuleFactory.class.getCanonicalName();
private static final String SAMPLE_EXECUTABLE_MODULE_FACTORY_CLASS_NAME = SampleExecutableIngestModuleFactory.class.getCanonicalName();
private static final ArrayList<String> coreModuleOrdering = new ArrayList<String>() {
{
// The ordering of Core module factories is hard-coded.
// The ordering of the core ingest module factories implemented
// using Java is hard-coded.
add("org.sleuthkit.autopsy.recentactivity.RecentActivityExtracterModuleFactory"); //NON-NLS
add(HashLookupModuleFactory.class.getCanonicalName());
add(FileTypeIdModuleFactory.class.getCanonicalName());
@ -64,37 +68,36 @@ final class IngestModuleFactoryLoader {
};
/**
* Get the currently available set of ingest module factories. The factories
* are not cached between calls since NetBeans modules with classes labeled
* as IngestModuleFactory service providers and/or Python scripts defining
* classes derived from IngestModuleFactory may be added or removed between
* invocations.
* Gets the currently available set of ingest module factories. The
* factories are not cached between calls since NetBeans modules with
* classes annotated as IngestModuleFactory service providers and/or Python
* scripts defining classes derived from IngestModuleFactory may be added or
* removed between invocations.
*
* @return A list of objects that implement the IngestModuleFactory
* interface.
*/
static List<IngestModuleFactory> getIngestModuleFactories() {
// Discover the ingest module factories implemented using Java, making a
// hash map of display names to discovered factories and a hash set of
// display names.
// A hash set of display names and a hash map of class names to
// discovered factories are used to de-duplicate and order the
// factories.
HashSet<String> moduleDisplayNames = new HashSet<>();
HashMap<String, IngestModuleFactory> javaFactoriesByClass = new HashMap<>();
// Discover the ingest module factories implemented using Java with a
// service provider annotation for the IngestModuleFactory interface.
for (IngestModuleFactory factory : Lookup.getDefault().lookupAll(IngestModuleFactory.class)) {
if (!moduleDisplayNames.contains(factory.getModuleDisplayName())) {
moduleDisplayNames.add(factory.getModuleDisplayName());
javaFactoriesByClass.put(factory.getClass().getCanonicalName(), factory);
logger.log(Level.INFO, "Found ingest module factory: name = {0}, version = {1}", new Object[]{factory.getModuleDisplayName(), factory.getModuleVersionNumber()}); //NON-NLS
} else {
logger.log(Level.SEVERE, "Found duplicate ingest module display name (name = {0})", factory.getModuleDisplayName()); //NON-NLS
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
NbBundle.getMessage(IngestModuleFactoryLoader.class, "IngestModuleFactoryLoader.errorMessages.duplicateDisplayName", factory.getModuleDisplayName()),
NotifyDescriptor.ERROR_MESSAGE));
}
IngestModuleFactoryLoader.addFactory(factory, moduleDisplayNames, javaFactoriesByClass);
}
// Kick out the sample ingest module factories implemented in Java.
javaFactoriesByClass.remove(SampleIngestModuleFactory.class.getCanonicalName());
javaFactoriesByClass.remove(SampleExecutableIngestModuleFactory.class.getCanonicalName());
// Discover the ingest module factories implemented using Java with a
// service provider annotation for the IngestModuleFactoryAdapter
// abstract base class.
for (IngestModuleFactory factory : Lookup.getDefault().lookupAll(IngestModuleFactoryAdapter.class)) {
if (!javaFactoriesByClass.containsValue(factory)) {
IngestModuleFactoryLoader.addFactory(factory, moduleDisplayNames, javaFactoriesByClass);
}
}
// Add the core ingest module factories in the desired order, removing
// the core factories from the map so that the map will only contain
@ -109,9 +112,13 @@ final class IngestModuleFactoryLoader {
}
}
// Add any remaining non-core factories discovered. Order is not
// guaranteed!
factories.addAll(javaFactoriesByClass.values());
// Add any remaining non-core factories discovered. Order with an
// alphabetical sort by module display name.
TreeMap<String, IngestModuleFactory> javaFactoriesSortedByName = new TreeMap<>();
for (IngestModuleFactory factory : javaFactoriesByClass.values()) {
javaFactoriesSortedByName.put(factory.getModuleDisplayName(), factory);
}
factories.addAll(javaFactoriesSortedByName.values());
// Add any ingest module factories implemented using Jython. Order is
// not guaranteed!
@ -130,4 +137,25 @@ final class IngestModuleFactoryLoader {
return factories;
}
private static void addFactory(IngestModuleFactory factory, HashSet<String> moduleDisplayNames, HashMap<String, IngestModuleFactory> javaFactoriesByClass) {
// Ignore the sample ingest module factories implemented in Java.
String className = factory.getClass().getCanonicalName();
if (className.equals(IngestModuleFactoryLoader.SAMPLE_MODULE_FACTORY_CLASS_NAME)
|| className.equals(IngestModuleFactoryLoader.SAMPLE_EXECUTABLE_MODULE_FACTORY_CLASS_NAME)) {
return;
}
if (!moduleDisplayNames.contains(factory.getModuleDisplayName())) {
moduleDisplayNames.add(factory.getModuleDisplayName());
javaFactoriesByClass.put(factory.getClass().getCanonicalName(), factory);
logger.log(Level.INFO, "Found ingest module factory: name = {0}, version = {1}", new Object[]{factory.getModuleDisplayName(), factory.getModuleVersionNumber()}); //NON-NLS
} else {
logger.log(Level.SEVERE, "Found duplicate ingest module display name (name = {0})", factory.getModuleDisplayName()); //NON-NLS
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
NbBundle.getMessage(IngestModuleFactoryLoader.class, "IngestModuleFactoryLoader.errorMessages.duplicateDisplayName", factory.getModuleDisplayName()),
NotifyDescriptor.ERROR_MESSAGE));
}
}
}

View File

@ -43,8 +43,8 @@ import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* Creates ingest jobs and their constituent ingest tasks, enabled the tasks for
* execution by the ingest manager's ingest threads.
* Creates ingest jobs and their constituent ingest tasks, queuing the tasks in
* priority order for execution by the ingest manager's ingest threads.
*/
final class IngestScheduler {
@ -60,7 +60,6 @@ final class IngestScheduler {
private volatile boolean enabled = false;
// private volatile boolean cancellingAllTasks = false; TODO: Uncomment this with related code, if desired
private final DataSourceIngestTaskQueue dataSourceTaskDispenser = new DataSourceIngestTaskQueue();
private final FileIngestTaskQueue fileTaskDispenser = new FileIngestTaskQueue();
@ -108,12 +107,11 @@ final class IngestScheduler {
/**
* Creates an ingest job for a data source.
*
* @param dataSource The data source to ingest.
* @param ingestModuleTemplates The ingest module templates to use to
* create
* the ingest pipelines for the job.
* @param dataSource The data source to ingest.
* @param ingestModuleTemplates The ingest module templates to use to create
* the ingest pipelines for the job.
* @param processUnallocatedSpace Whether or not the job should include
* processing of unallocated space.
* processing of unallocated space.
*
* @return A collection of ingest module start up errors, empty on success.
*
@ -136,10 +134,10 @@ final class IngestScheduler {
}
synchronized private void scheduleIngestTasks(IngestJob job) throws InterruptedException {
// This is synchronized to guard the task queues and make enabled for
// a job an an atomic operation. Otherwise, the data source task might
// be completed before the file tasks were scheduled, resulting in a
// false positive for a job completion check.
// This is synchronized to guard the task queues and make ingest
// scheduling for a job an an atomic operation. Otherwise, the data
// source task might be completed before the file tasks were scheduled,
// resulting in a false positive for a job completion check.
if (job.hasDataSourceIngestPipeline()) {
scheduleDataSourceIngestTask(job);
}
@ -364,35 +362,32 @@ final class IngestScheduler {
if (jobIsCompleted) {
// The lock does not need to be held for the job shut down.
finishIngestJob(job);
// TODO: Uncomment this code to make sure that there is no more work
// being done by the ingest threads. The wait() call is in
// cancelAllTasks().
// if (cancellingAllTasks) {
// synchronized (ingestJobsById) {
// ingestJobsById.notify();
// }
// }
}
}
/**
* Queries whether or not ingest jobs are running.
*
* @return True or false.
*/
boolean ingestJobsAreRunning() {
return !ingestJobsById.isEmpty();
}
/**
* Clears the queues for the job. If job is complete, finish it up.
* Otherwise,
* this will exit and the last worker thread will finish / clean up.
* Clears the pending ingest task queues for an ingest job. If job is
* complete (no pending or in progress tasks) the job is finished up.
* Otherwise, the last worker thread with an in progress task will finish /
* clean up the job.
*
* @param job
* @param job The job to cancel.
*/
synchronized void cancelIngestJob(IngestJob job) {
synchronized void cancelPendingTasksForIngestJob(IngestJob job) {
long jobId = job.getId();
removeAllPendingTasksForJob(pendingRootDirectoryTasks, jobId);
removeAllPendingTasksForJob(pendingDirectoryTasks, jobId);
removeAllPendingTasksForJob(pendingFileTasks, jobId);
removeAllPendingTasksForJob(pendingDataSourceTasks, jobId);
job.cancel();
if (ingestJobIsComplete(job)) {
finishIngestJob(job);
}
@ -443,20 +438,6 @@ final class IngestScheduler {
}
}
}
// TODO: Uncomment this code to make sure that there is no more work
// being done by the ingest threads. The notify() call is in
// notifyTaskCompleted()
// cancellingAllTasks = true;
// synchronized (ingestJobsById) {
// while (ingestJobsById.isEmpty() == false) {
// try {
// ingestJobsById.wait();
// } catch (InterruptedException ex) {
// Logger.getLogger(IngestScheduler.class.getName()).log(Level.FINE, "Unexpected interruption of wait on ingest jobs collection", ex); //NON-NLS
// }
// }
// }
// cancellingAllTasks = false;
}
synchronized private <T> void removeAllPendingTasks(Collection<T> taskQueue) {

View File

@ -53,7 +53,7 @@ class AndroidIngestModule implements DataSourceIngestModule {
try {
ContactAnalyzer.findContacts();
progressBar.progress(1);
if (context.isJobCancelled()) {
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {
@ -63,7 +63,7 @@ class AndroidIngestModule implements DataSourceIngestModule {
try {
CallLogAnalyzer.findCallLogs();
progressBar.progress(2);
if (context.isJobCancelled()) {
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {
@ -73,7 +73,7 @@ class AndroidIngestModule implements DataSourceIngestModule {
try {
TextMessageAnalyzer.findTexts();
progressBar.progress(3);
if (context.isJobCancelled()) {
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {
@ -83,7 +83,7 @@ class AndroidIngestModule implements DataSourceIngestModule {
try {
TangoMessageAnalyzer.findTangoMessages();
progressBar.progress(4);
if (context.isJobCancelled()) {
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {
@ -93,7 +93,7 @@ class AndroidIngestModule implements DataSourceIngestModule {
try {
WWFMessageAnalyzer.findWWFMessages();
progressBar.progress(5);
if (context.isJobCancelled()) {
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {
@ -103,7 +103,7 @@ class AndroidIngestModule implements DataSourceIngestModule {
try {
GoogleMapLocationAnalyzer.findGeoLocations();
progressBar.progress(6);
if (context.isJobCancelled()) {
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {
@ -113,7 +113,7 @@ class AndroidIngestModule implements DataSourceIngestModule {
try {
BrowserLocationAnalyzer.findGeoLocations();
progressBar.progress(7);
if (context.isJobCancelled()) {
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {

View File

@ -135,7 +135,7 @@ public class E01VerifyIngestModule implements DataSourceIngestModule {
// Read in byte size chunks and update the hash value with the data.
for (int i = 0; i < totalChunks; i++) {
if (context.isJobCancelled()) {
if (context.dataSourceIngestIsCancelled()) {
return ProcessResult.OK;
}
data = new byte[(int) chunkSize];

View File

@ -102,17 +102,8 @@ public class FileTypeIdIngestModule implements FileIngestModule {
try {
long startTime = System.currentTimeMillis();
String mimeType = tikaDetector.attemptMatch(abstractFile);
tikaDetector.detectAndSave(abstractFile);
addToTotals(jobId, (System.currentTimeMillis() - startTime)); //add match time
if (mimeType != null) {
// add artifact
BlackboardArtifact getInfoArt = abstractFile.getGenInfoArtifact();
BlackboardAttribute batt = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG.getTypeID(), FileTypeIdModuleFactory.getModuleName(), mimeType);
getInfoArt.addAttribute(batt);
// we don't fire the event because we just updated TSK_GEN_INFO, which isn't displayed in the tree and is vague.
}
return ProcessResult.OK;
} catch (TskException ex) {
logger.log(Level.WARNING, "Error matching file signature", ex); //NON-NLS

View File

@ -24,20 +24,41 @@ import org.apache.tika.mime.MediaType;
import org.apache.tika.mime.MimeTypes;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
class TikaFileTypeDetector {
public class TikaFileTypeDetector {
private static final Tika tikaInst = new Tika(); //calling detect() with this should be thread-safe
private final int BUFFER_SIZE = 64 * 1024; //how many bytes to pass in
private final byte buffer[] = new byte[BUFFER_SIZE];
/**
*
* Detect the mime type of the passed in file and save it to the blackboard
* @param abstractFile
* @return mime type or null
* @throws TskCoreException
*/
public synchronized String detectAndSave(AbstractFile abstractFile) throws TskCoreException {
String mimeType = detect(abstractFile);
if (mimeType != null) {
// add artifact
BlackboardArtifact getInfoArt = abstractFile.getGenInfoArtifact();
BlackboardAttribute batt = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG.getTypeID(), FileTypeIdModuleFactory.getModuleName(), mimeType);
getInfoArt.addAttribute(batt);
// we don't fire the event because we just updated TSK_GEN_INFO, which isn't displayed in the tree and is vague.
}
return mimeType;
}
/**
* Detect the mime type of the passed in file
* @param abstractFile
* @return mime type of detected format or null
*/
public synchronized String attemptMatch(AbstractFile abstractFile) {
public synchronized String detect(AbstractFile abstractFile) {
try {
byte buf[];
int len = abstractFile.read(buffer, 0, BUFFER_SIZE);

View File

@ -24,39 +24,137 @@ import java.util.List;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
/**
* Settings for a hash lookup file ingest module instance.
* Ingest job settings for the hash lookup module.
*/
final class HashLookupModuleSettings implements IngestModuleIngestJobSettings {
private static final long serialVersionUID = 1L;
private final HashSet<String> namesOfEnabledKnownHashSets = new HashSet<>();
private final HashSet<String> namesOfEnabledKnownBadHashSets = new HashSet<>();
private HashSet<String> namesOfEnabledKnownHashSets;
private HashSet<String> namesOfDisabledKnownHashSets; // Added in version 1.1
private HashSet<String> namesOfEnabledKnownBadHashSets;
private HashSet<String> namesOfDisabledKnownBadHashSets; // Added in version 1.1
private boolean shouldCalculateHashes = true;
HashLookupModuleSettings(boolean shouldCalculateHashes, List<String> namesOfEnabledKnownHashSets, List<String> namesOfEnabledKnownBadHashSets) {
this.shouldCalculateHashes = shouldCalculateHashes;
this.namesOfEnabledKnownHashSets.addAll(namesOfEnabledKnownHashSets);
this.namesOfEnabledKnownBadHashSets.addAll(namesOfEnabledKnownBadHashSets);
/**
* Constructs ingest job settings for the hash lookup module.
*
* @param shouldCalculateHashes Whether or not hashes should be calculated.
* @param namesOfEnabledKnownHashSets A list of enabled known hash sets.
* @param namesOfEnabledKnownBadHashSets A list of enabled known bad hash
* sets.
*/
HashLookupModuleSettings(boolean shouldCalculateHashes,
List<String> namesOfEnabledKnownHashSets,
List<String> namesOfEnabledKnownBadHashSets) {
this(shouldCalculateHashes, namesOfEnabledKnownHashSets, namesOfEnabledKnownBadHashSets, new ArrayList<>(), new ArrayList<>());
}
/**
* Constructs ingest job settings for the hash lookup module.
*
* @param shouldCalculateHashes Whether or not hashes should be calculated.
* @param namesOfEnabledKnownHashSets A list of enabled known hash sets.
* @param namesOfEnabledKnownBadHashSets A list of enabled known bad hash
* sets.
* @param namesOfDisabledKnownHashSets A list of disabled known hash sets.
* @param namesOfDisabledKnownBadHashSets A list of disabled known bad hash
* sets.
*/
HashLookupModuleSettings(boolean shouldCalculateHashes,
List<String> namesOfEnabledKnownHashSets,
List<String> namesOfEnabledKnownBadHashSets,
List<String> namesOfDisabledKnownHashSets,
List<String> namesOfDisabledKnownBadHashSets) {
this.shouldCalculateHashes = shouldCalculateHashes;
this.namesOfEnabledKnownHashSets = new HashSet<>(namesOfEnabledKnownHashSets);
this.namesOfEnabledKnownBadHashSets = new HashSet<>(namesOfEnabledKnownBadHashSets);
this.namesOfDisabledKnownHashSets = new HashSet<>(namesOfDisabledKnownHashSets);
this.namesOfDisabledKnownBadHashSets = new HashSet<>(namesOfDisabledKnownBadHashSets);
}
/**
* @inheritDoc
*/
@Override
public long getVersionNumber() {
return serialVersionUID;
}
this.upgradeFromOlderVersions();
return HashLookupModuleSettings.serialVersionUID;
}
/**
* Checks the setting that specifies whether or not hashes are to be
* calculated.
*
* @return True if hashes are to be calculated, false otherwise.
*/
boolean shouldCalculateHashes() {
return shouldCalculateHashes;
this.upgradeFromOlderVersions();
return this.shouldCalculateHashes;
}
/**
* Checks whether or not a hash set is enabled. If there is no setting for
* the requested hash set, it is deemed to be enabled.
*
* @param hashSetName The name of the hash set to check.
* @return True if the hash set is enabled, false otherwise.
*/
boolean isHashSetEnabled(String hashSetName) {
return (namesOfEnabledKnownHashSets.contains(hashSetName) || namesOfEnabledKnownBadHashSets.contains(hashSetName));
this.upgradeFromOlderVersions();
return !(this.namesOfDisabledKnownHashSets.contains(hashSetName) || this.namesOfDisabledKnownBadHashSets.contains(hashSetName));
}
/**
* Get the names of all explicitly enabled known files hash sets.
*
* @return The list of names.
*/
List<String> getNamesOfEnabledKnownHashSets() {
return new ArrayList<>(namesOfEnabledKnownHashSets);
this.upgradeFromOlderVersions();
return new ArrayList<>(this.namesOfEnabledKnownHashSets);
}
List<String> getNamesOfEnabledKnownBadHashSets() {
return new ArrayList<>(namesOfEnabledKnownBadHashSets);
/**
* Get the names of all explicitly disabled known files hash sets.
*
* @return The list of names.
*/
List<String> getNamesOfDisabledKnownHashSets() {
this.upgradeFromOlderVersions();
return new ArrayList<>(namesOfDisabledKnownHashSets);
}
/**
* Get the names of all explicitly enabled known bad files hash sets.
*
* @return The list of names.
*/
List<String> getNamesOfEnabledKnownBadHashSets() {
this.upgradeFromOlderVersions();
return new ArrayList<>(this.namesOfEnabledKnownBadHashSets);
}
/**
* Get the names of all explicitly disabled known bad files hash sets.
*
* @return The list of names.
*/
List<String> getNamesOfDisabledKnownBadHashSets() {
this.upgradeFromOlderVersions();
return new ArrayList<>(this.namesOfDisabledKnownBadHashSets);
}
/**
* Initialize fields set to null when an instance of a previous, but still
* compatible, version of this class is de-serialized.
*/
private void upgradeFromOlderVersions() {
if (null == this.namesOfDisabledKnownHashSets) {
this.namesOfDisabledKnownHashSets = new HashSet<>();
}
if (null == this.namesOfDisabledKnownBadHashSets) {
this.namesOfDisabledKnownBadHashSets = new HashSet<>();
}
}
}

View File

@ -101,19 +101,25 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
@Override
public IngestModuleIngestJobSettings getSettings() {
List<String> enabledKnownHashSetNames = new ArrayList<>();
List<String> disabledKnownHashSetNames = new ArrayList<>();
List<String> enabledKnownBadHashSetNames = new ArrayList<>();
List<String> disabledKnownBadHashSetNames = new ArrayList<>();
getHashSetNames(knownHashSetModels, enabledKnownHashSetNames, disabledKnownHashSetNames);
getHashSetNames(knownBadHashSetModels, enabledKnownBadHashSetNames, disabledKnownBadHashSetNames);
return new HashLookupModuleSettings(alwaysCalcHashesCheckbox.isSelected(),
getNamesOfEnabledHashSets(knownHashSetModels),
getNamesOfEnabledHashSets(knownBadHashSetModels));
enabledKnownHashSetNames, enabledKnownBadHashSetNames,
disabledKnownHashSetNames, disabledKnownBadHashSetNames);
}
private List<String> getNamesOfEnabledHashSets(List<HashSetModel> hashSetModels) {
List<String> namesOfEnabledHashSets = new ArrayList<>();
private void getHashSetNames(List<HashSetModel> hashSetModels, List<String> enabledHashSetNames, List<String> disabledHashSetNames) {
for (HashSetModel model : hashSetModels) {
if (model.isEnabled() && model.isIndexed()) {
namesOfEnabledHashSets.add(model.getName());
enabledHashSetNames.add(model.getName());
} else {
disabledHashSetNames.add(model.getName());
}
}
return namesOfEnabledHashSets;
}
void update() {
@ -289,11 +295,6 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
alwaysCalcHashesCheckbox.setPreferredSize(new java.awt.Dimension(271, 35));
alwaysCalcHashesCheckbox.setVerticalAlignment(javax.swing.SwingConstants.TOP);
alwaysCalcHashesCheckbox.setVerticalTextPosition(javax.swing.SwingConstants.TOP);
alwaysCalcHashesCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
alwaysCalcHashesCheckboxActionPerformed(evt);
}
});
jScrollPane2.setBorder(javax.swing.BorderFactory.createEtchedBorder());
@ -346,10 +347,6 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
);
}// </editor-fold>//GEN-END:initComponents
private void alwaysCalcHashesCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_alwaysCalcHashesCheckboxActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_alwaysCalcHashesCheckboxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox alwaysCalcHashesCheckbox;
private javax.swing.JScrollPane jScrollPane1;

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.modules.interestingitems;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -29,17 +30,32 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
final class FilesIdentifierIngestJobSettings implements IngestModuleIngestJobSettings {
private static final long serialVersionUID = 1L;
private final Set<String> enabledFilesSetNames = new HashSet<>();
private Set<String> enabledFilesSetNames = new HashSet<>();
private Set<String> disabledFilesSetNames = new HashSet<>();
/**
* Construct the ingest job settings for an interesting files identifier
* ingest module.
*
* @param enabledFilesSetNames The names of the interesting files sets
* that are enabled for the ingest job.
* @param enabledFilesSetNames The names of the interesting files sets that
* are enabled for the ingest job.
*/
FilesIdentifierIngestJobSettings(List<String> enabledFilesSetNames) {
this.enabledFilesSetNames.addAll(enabledFilesSetNames);
this(enabledFilesSetNames, new ArrayList<>());
}
/**
* Construct the ingest job settings for an interesting files identifier
* ingest module.
*
* @param enabledFilesSetNames The names of the interesting files sets that
* are enabled for the ingest job.
* @param disabledFilesSetNames The names of the interesting files sets that
* are disabled for the ingest job.
*/
FilesIdentifierIngestJobSettings(List<String> enabledFilesSetNames, List<String> disabledFilesSetNames) {
this.enabledFilesSetNames = new HashSet<>(enabledFilesSetNames);
this.disabledFilesSetNames = new HashSet<>(disabledFilesSetNames);
}
/**
@ -52,12 +68,32 @@ final class FilesIdentifierIngestJobSettings implements IngestModuleIngestJobSet
/**
* Determines whether or not an interesting files set definition is enabled
* for an ingest job.
* for an ingest job. If there is no setting for the requested files set, it
* is deemed to be enabled.
*
* @param filesSetName The name of the files set definition to check.
* @return True if the file set is enabled, false otherwise.
*/
boolean isInterestingFilesSetEnabled(String filesSetName) {
return (this.enabledFilesSetNames.contains(filesSetName));
boolean interestingFilesSetIsEnabled(String filesSetName) {
return !(this.disabledFilesSetNames.contains(filesSetName));
}
/**
* Get the names of all explicitly enabled interesting files set definitions.
*
* @return The list of names.
*/
List<String> getNamesOfEnabledInterestingFilesSets() {
return new ArrayList<>(this.enabledFilesSetNames);
}
/**
* Get the names of all explicitly disabled interesting files set definitions.
*
* @return The list of names.
*/
List<String> getNamesOfDisabledInterestingFilesSets() {
return new ArrayList<>(disabledFilesSetNames);
}
}

View File

@ -76,7 +76,7 @@ final class FilesIdentifierIngestJobSettingsPanel extends IngestModuleIngestJobS
List<FilesSetRow> filesSetRows = new ArrayList<>();
this.filesSetSnapshot = new TreeMap<>(InterestingItemDefsManager.getInstance().getInterestingFilesSets());
for (FilesSet set : this.filesSetSnapshot.values()) {
filesSetRows.add(new FilesSetRow(set, settings.isInterestingFilesSetEnabled(set.getName())));
filesSetRows.add(new FilesSetRow(set, settings.interestingFilesSetIsEnabled(set.getName())));
}
// Make a table model to manage the row objects.
@ -107,10 +107,15 @@ final class FilesIdentifierIngestJobSettingsPanel extends IngestModuleIngestJobS
@Override
public IngestModuleIngestJobSettings getSettings() {
List<String> enabledInterestingFilesSets = new ArrayList<>();
this.tableModel.filesSetRows.stream().filter((rowModel) -> (rowModel.isEnabled())).forEach((rowModel) -> {
enabledInterestingFilesSets.add(rowModel.getFilesSet().getName());
});
return new FilesIdentifierIngestJobSettings(enabledInterestingFilesSets);
List<String> disabledInterestingFilesSets = new ArrayList<>();
for (FilesSetRow rowModel : this.tableModel.filesSetRows) {
if (rowModel.isEnabled()) {
enabledInterestingFilesSets.add(rowModel.getFilesSet().getName());
} else {
disabledInterestingFilesSets.add(rowModel.getFilesSet().getName());
}
}
return new FilesIdentifierIngestJobSettings(enabledInterestingFilesSets, disabledInterestingFilesSets);
}
/**
@ -128,7 +133,7 @@ final class FilesIdentifierIngestJobSettingsPanel extends IngestModuleIngestJobS
for (FilesSet set : newFilesSetSnapshot.values()) {
if (this.filesSetSnapshot.keySet().contains(set.getName())) {
// Preserve the current enabled/diabled state of the set.
rowModels.add(new FilesSetRow(set, settings.isInterestingFilesSetEnabled(set.getName())));
rowModels.add(new FilesSetRow(set, settings.interestingFilesSetIsEnabled(set.getName())));
} else {
// New sets are enabled by default.
rowModels.add(new FilesSetRow(set, true));

View File

@ -70,7 +70,7 @@ final class FilesIdentifierIngestModule implements FileIngestModule {
// to disable the interesting files set definition UI during ingest.
List<FilesSet> filesSets = new ArrayList<>();
for (FilesSet set : InterestingItemDefsManager.getInstance().getInterestingFilesSets().values()) {
if (settings.isInterestingFilesSetEnabled(set.getName())) {
if (settings.interestingFilesSetIsEnabled(set.getName())) {
filesSets.add(set);
}
}

View File

@ -0,0 +1,12 @@
OpenIDE-Module-Name=PhotoRec Carver Ingest Module
OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Long-Description=PhotoRec Carver ingest module. \n\n Carves unallocated space and feeds the resulting carved files back into the system for processing.
OpenIDE-Module-Short-Description=Carves unallocated space and feeds carved files back into the system for processing.
unallocatedSpaceProcessingSettingsError.message="Process Unallocated Space" is not checked. This module is designed to carve unallocated space. Either allow processing of unallocated space, or do not use this module.
moduleDisplayName.text=PhotoRec Carver
moduleDescription.text=Runs PhotoRec carver against unallocated space on the system.
unrecognizedSettings.message=Settings not instanceof org.sleuthkit.autopsy.modules.photoreccarver
unsupportedOS.message=Module is not supported for other than Windows platforms
missingExecutable.message=Unable to locate unallocated carver executable.
cannotRunExecutable.message=Unable to execute unallocated carver
cannotCreateOutputDir.message=Unable to create output directory: {0}

View File

@ -0,0 +1,378 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.photoreccarver;
import java.io.File;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.nio.file.DirectoryStream;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import org.openide.modules.InstalledFileLocator;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.ExecUtil;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.FileIngestModule;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.ingest.IngestModule;
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.Volume;
import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.ingest.FileIngestModuleProcessTerminator;
import org.sleuthkit.autopsy.ingest.IngestServices;
/**
* A file ingest module that runs the Unallocated Carver executable with unallocated space files as input.
*/
final class PhotoRecCarverFileIngestModule implements FileIngestModule {
private static final String PHOTOREC_DIRECTORY = "photorec_exec"; //NON-NLS
private static final String PHOTOREC_EXECUTABLE = "photorec_win.exe"; //NON-NLS
private static final String PHOTOREC_RESULTS_BASE = "results"; //NON-NLS
private static final String PHOTOREC_RESULTS_EXTENDED = "results.1"; //NON-NLS
private static final String PHOTOREC_REPORT = "report.xml"; //NON-NLS
private static final String LOG_FILE = "run_log.txt"; //NON-NLS
private static final String TEMP_DIR_NAME = "temp"; // NON-NLS
private static final Logger logger = Logger.getLogger(PhotoRecCarverFileIngestModule.class.getName());
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
private static final Map<Long, WorkingPaths> pathsByJob = new ConcurrentHashMap<>();
private IngestJobContext context;
private Path rootOutputDirPath;
private File executableFile;
/**
* Constructs a file ingest module that runs the Unallocated Carver executable with unallocated space files as
* input.
*
* @param None
*/
PhotoRecCarverFileIngestModule() {
}
/**
* @inheritDoc
*/
@Override
public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException {
this.context = context;
// If the global unallocated space processing setting and the module
// process unallocated space only setting are not in sych, throw an
// exception. Although the result would not be incorrect, it would be
// unfortunate for the user to get an accidental no-op for this module.
if (!this.context.processingUnallocatedSpace()) {
throw new IngestModule.IngestModuleException(NbBundle.getMessage(this.getClass(), "unallocatedSpaceProcessingSettingsError.message"));
}
this.context = context;
this.rootOutputDirPath = PhotoRecCarverFileIngestModule.createModuleOutputDirectoryForCase();
Path execName = Paths.get(PHOTOREC_DIRECTORY, PHOTOREC_EXECUTABLE);
executableFile = locateExecutable(execName.toString());
if (PhotoRecCarverFileIngestModule.refCounter.incrementAndGet(this.context.getJobId()) == 1) {
try {
// The first instance of the module for an ingest job creates
// a time-stamped output subdirectory of the unallocated space
// scans subdirectory of the Unallocated Carver module output
// directory for the current case.
// Make output subdirectories for the current time and image within
// the module output directory for the current case.
DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss-SSSS"); // NON-NLS
Date date = new Date();
String folder = this.context.getDataSource().getId() + "_" + dateFormat.format(date);
Path outputDirPath = Paths.get(this.rootOutputDirPath.toAbsolutePath().toString(), folder);
Files.createDirectories(outputDirPath);
// A temp subdirectory is also created as a location for writing unallocated space files to disk.
Path tempDirPath = Paths.get(outputDirPath.toString(), PhotoRecCarverFileIngestModule.TEMP_DIR_NAME);
Files.createDirectory(tempDirPath);
// Save the directories for the current job.
PhotoRecCarverFileIngestModule.pathsByJob.put(this.context.getJobId(), new WorkingPaths(outputDirPath, tempDirPath));
}
catch (SecurityException | IOException | UnsupportedOperationException ex) {
throw new IngestModule.IngestModuleException(NbBundle.getMessage(this.getClass(), "Utilities.cannotCreateOutputDir.message", ex.getLocalizedMessage()));
}
}
}
/**
* @inheritDoc
*/
@Override
public IngestModule.ProcessResult process(AbstractFile file) {
// Skip everything except unallocated space files.
if (file.getType() != TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) {
return IngestModule.ProcessResult.OK;
}
Path tempFilePath = null;
try {
long id = getRootId(file);
// make sure we have a valid systemID
if (id == -1) {
return ProcessResult.ERROR;
}
// Verify initialization succeeded.
if (null == this.executableFile) {
logger.log(Level.SEVERE, "Unallocated Carver unallocated space ingest module called after failed start up"); // NON-NLS
return IngestModule.ProcessResult.ERROR;
}
// Check that we have roughly enough disk space left to complete the operation
long freeDiskSpace = IngestServices.getInstance().getFreeDiskSpace();
if ((file.getSize() * 2) > freeDiskSpace) {
logger.log(Level.SEVERE, "Error processing " + file.getName() + " with "
+ PhotoRecCarverIngestModuleFactory.getModuleName()
+ " Not enough space on primary disk to carve unallocated space."); // NON-NLS
return IngestModule.ProcessResult.ERROR;
}
// Write the file to disk.
WorkingPaths paths = PhotoRecCarverFileIngestModule.pathsByJob.get(this.context.getJobId());
tempFilePath = Paths.get(paths.getTempDirPath().toString(), file.getName());
ContentUtils.writeToFile(file, tempFilePath.toFile());
// Create a subdirectory for this file.
Path outputDirPath = Paths.get(paths.getOutputDirPath().toString(), file.getName());
Files.createDirectory(outputDirPath);
File log = new File(Paths.get(outputDirPath.toString(), LOG_FILE).toString()); //NON-NLS
// Scan the file with Unallocated Carver.
ProcessBuilder processAndSettings = new ProcessBuilder(
"\"" + executableFile + "\"",
"/d",
"\"" + outputDirPath.toAbsolutePath() + File.separator + PHOTOREC_RESULTS_BASE + "\"",
"/cmd",
"\"" + tempFilePath.toFile() + "\"",
"search"); // NON_NLS
// Add environment variable to force PhotoRec to run with the same permissions Autopsy uses
processAndSettings.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS
processAndSettings.redirectErrorStream(true);
processAndSettings.redirectOutput(Redirect.appendTo(log));
int exitValue = ExecUtil.execute(processAndSettings, new FileIngestModuleProcessTerminator(this.context));
if (this.context.fileIngestIsCancelled() == true) {
// if it was cancelled by the user, result is OK
// cleanup the output path
FileUtil.deleteDir(new File(outputDirPath.toString()));
if (null != tempFilePath && Files.exists(tempFilePath)) {
tempFilePath.toFile().delete();
}
logger.log(Level.INFO, "Cancelled by user"); // NON-NLS
return IngestModule.ProcessResult.OK;
}
else if (0 != exitValue) {
// if it failed or was cancelled by timeout, result is ERROR
// cleanup the output path
FileUtil.deleteDir(new File(outputDirPath.toString()));
if (null != tempFilePath && Files.exists(tempFilePath)) {
tempFilePath.toFile().delete();
}
logger.log(Level.SEVERE, "Unallocated Carver returned error exit value = {0} when scanning {1}",
new Object[]{exitValue, file.getName()}); // NON-NLS
return IngestModule.ProcessResult.ERROR;
}
// Move carver log file to avoid placement into Autopsy results. PhotoRec appends ".1" to the folder name.
java.io.File oldAuditFile = new java.io.File(Paths.get(outputDirPath.toString(), PHOTOREC_RESULTS_EXTENDED, PHOTOREC_REPORT).toString()); //NON-NLS
java.io.File newAuditFile = new java.io.File(Paths.get(outputDirPath.toString(), PHOTOREC_REPORT).toString()); //NON-NLS
oldAuditFile.renameTo(newAuditFile);
Path pathToRemove = Paths.get(outputDirPath.toAbsolutePath().toString());
DirectoryStream<Path> stream = Files.newDirectoryStream(pathToRemove);
for (Path entry : stream) {
if (Files.isDirectory(entry)) {
FileUtil.deleteDir(new File(entry.toString()));
}
}
// Now that we've cleaned up the folders and data files, parse the xml output file to add carved items into the database
PhotoRecCarverOutputParser parser = new PhotoRecCarverOutputParser(outputDirPath);
List<LayoutFile> theList = parser.parse(newAuditFile, id, file);
if (theList != null) { // if there were any results from carving, add the unallocated carving event to the reports list.
context.scheduleFiles(new ArrayList<AbstractFile>(theList));
}
}
catch (IOException ex) {
logger.log(Level.SEVERE, "Error processing " + file.getName() + " with Unallocated Carver", ex); // NON-NLS
return IngestModule.ProcessResult.ERROR;
}
finally {
if (null != tempFilePath && Files.exists(tempFilePath)) {
// Get rid of the unallocated space file.
tempFilePath.toFile().delete();
}
}
return IngestModule.ProcessResult.OK;
}
/**
* @inheritDoc
*/
@Override
public void shutDown() {
if (refCounter.decrementAndGet(this.context.getJobId()) == 0) {
try {
// The last instance of this module for an ingest job cleans out
// the working paths map entry for the job and deletes the temp dir.
WorkingPaths paths = PhotoRecCarverFileIngestModule.pathsByJob.remove(this.context.getJobId());
FileUtil.deleteDir(new File(paths.getTempDirPath().toString()));
}
catch (SecurityException ex) {
logger.log(Level.SEVERE, "Error shutting down Unallocated Carver unallocated space module", ex); // NON-NLS
}
}
}
private static final class WorkingPaths {
private final Path outputDirPath;
private final Path tempDirPath;
WorkingPaths(Path outputDirPath, Path tempDirPath) {
this.outputDirPath = outputDirPath;
this.tempDirPath = tempDirPath;
}
Path getOutputDirPath() {
return this.outputDirPath;
}
Path getTempDirPath() {
return this.tempDirPath;
}
}
/**
* Creates the output directory for this module for the current case, if it does not already exist.
*
* @return The absolute path of the output directory.
* @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException
*/
synchronized static Path createModuleOutputDirectoryForCase() throws IngestModule.IngestModuleException {
Path path = Paths.get(Case.getCurrentCase().getModulesOutputDirAbsPath(), PhotoRecCarverIngestModuleFactory.getModuleName());
try {
Files.createDirectory(path);
}
catch (FileAlreadyExistsException ex) {
// No worries.
}
catch (IOException | SecurityException | UnsupportedOperationException ex) {
throw new IngestModule.IngestModuleException(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "cannotCreateOutputDir.message", ex.getLocalizedMessage()));
}
return path;
}
/**
* Finds the root Volume or Image of the AbstractFile passed in.
*
* @param file The file we want to find the root parent for
* @return The ID of the root parent Volume or Image
*/
private static long getRootId(AbstractFile file) {
long id = -1;
Content parent = null;
try {
parent = file.getParent();
while (parent != null) {
if (parent instanceof Volume || parent instanceof Image) {
id = parent.getId();
break;
}
parent = parent.getParent();
}
}
catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Exception while trying to get parent of AbstractFile.", ex); //NON-NLS
}
return id;
}
/**
* Determines whether or not a directory is empty.
*
* @param directoryPath The path to the directory to inspect.
* @return True if the directory is empty, false otherwise.
* @throws IllegalArgumentException
* @throws IOException
*/
private static boolean isDirectoryEmpty(final Path directoryPath) throws IllegalArgumentException, IOException {
if (!Files.isDirectory(directoryPath)) {
throw new IllegalArgumentException("The directoryPath argument must be a directory path"); // NON-NLS
}
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(directoryPath)) {
return !dirStream.iterator().hasNext();
}
}
/**
* Finds and returns the path to the executable, if able.
*
* @param executableToFindName The name of the executable to find
* @return A File reference or throws an exception
* @throws IngestModuleException
*/
public static File locateExecutable(String executableToFindName) throws IngestModule.IngestModuleException {
// Must be running under a Windows operating system.
if (!PlatformUtil.isWindowsOS()) {
throw new IngestModule.IngestModuleException(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "unsupportedOS.message"));
}
File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, PhotoRecCarverFileIngestModule.class.getPackage().getName(), false);
if (null == exeFile) {
throw new IngestModule.IngestModuleException(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "missingExecutable.message"));
}
if (!exeFile.canExecute()) {
throw new IngestModule.IngestModuleException(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "cannotRunExecutable.message"));
}
return exeFile;
}
}

View File

@ -0,0 +1,96 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.photoreccarver;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.ingest.FileIngestModule;
import org.sleuthkit.autopsy.ingest.IngestModuleFactory;
import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
/**
* A factory for creating instances of file ingest modules that carve unallocated space
*/
@ServiceProvider(service = IngestModuleFactory.class)
public class PhotoRecCarverIngestModuleFactory extends IngestModuleFactoryAdapter
{
/**
* Gets the ingest module name for use within this package.
*
* @return A name string.
*/
static String getModuleName()
{
return NbBundle.getMessage(PhotoRecCarverIngestModuleFactory.class, "moduleDisplayName.text");
}
/**
* @inheritDoc
*/
@Override
public String getModuleDisplayName()
{
return PhotoRecCarverIngestModuleFactory.getModuleName();
}
/**
* @inheritDoc
*/
@Override
public String getModuleDescription()
{
return NbBundle.getMessage(PhotoRecCarverIngestModuleFactory.class, "moduleDescription.text");
}
/**
* @inheritDoc
*/
@Override
public String getModuleVersionNumber()
{
return Version.getVersion();
}
/**
* @inheritDoc
*/
@Override
public boolean isFileIngestModuleFactory()
{
return true;
}
/**
* @inheritDoc
*/
@Override
public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings)
{
if (!(settings instanceof IngestModuleIngestJobSettings))
{
throw new IllegalArgumentException(NbBundle.getMessage(PhotoRecCarverIngestModuleFactory.class, "unrecognizedSettings.message"));
}
return new PhotoRecCarverFileIngestModule();
}
}

View File

@ -0,0 +1,142 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.photoreccarver;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.CarvedFileContainer;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskFileRange;
/**
* This class parses the xml output from PhotoRec, and creates a list of entries to add back in to be processed.
*/
public class PhotoRecCarverOutputParser {
Path basePath;
private static final Logger logger = Logger.getLogger(PhotoRecCarverFileIngestModule.class.getName());
public PhotoRecCarverOutputParser(Path base) {
basePath = base;
}
/**
* Gets the value inside the XML element and returns it. Ignores leading whitespace.
*
* @param name The XML element we are looking for.
* @param line The line in which we are looking for the element.
* @return The String value found
*/
public String getValue(String name, String line) {
return line.replaceAll("[\t ]*</?" + name + ">", ""); //NON-NLS
}
/**
* Gets the value inside the XML element and returns it. Ignores leading whitespace.
*
* @param xmlInputFile The XML file we are trying to read and parse
* @param id The parent id of the unallocated space we are parsing.
* @param af The AbstractFile representing the unallocated space we are parsing.
* @return A List<LayoutFile> containing all the files added into the database
* @throws FileNotFoundException
* @throws IOException
*/
public List<LayoutFile> parse(File xmlInputFile, long id, AbstractFile af) throws FileNotFoundException, IOException {
try {
String fileName;
long fileSize;
String result;
String[] fields;
FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
// create and initialize the list to put into the database
List<CarvedFileContainer> carvedFileContainer = new ArrayList<CarvedFileContainer>();
// create a BufferedReader
BufferedReader in = new BufferedReader(new FileReader(xmlInputFile));
// create and initialize a line
String line = in.readLine();
// loop until an empty line is read
reachedEndOfFile:
while (!line.isEmpty()) {
while (!line.contains("<fileobject>")) //NON-NLS
{
if (line.equals("</dfxml>")) //NON-NLS
{ // We have found the end. Break out of both loops and move on to processing.
break reachedEndOfFile;
}
line = in.readLine();
}
List<TskFileRange> ranges = new ArrayList<TskFileRange>();
// read filename line
line = in.readLine();
fileName = getValue("filename", line); //NON-NLS
Path p = Paths.get(fileName);
if (p.startsWith(basePath)) {
fileName = p.getFileName().toString();
}
line = in.readLine(); /// read filesize line
fileSize = Long.parseLong(getValue("filesize", line)); //NON-NLS
in.readLine(); /// eat a line and move on to the next
line = in.readLine(); /// now get next valid line
while (line.contains("<byte_run")) //NON-NLS
{
result = line.replaceAll("[\t ]*<byte_run offset='", ""); //NON-NLS
result = result.replaceAll("'[\t ]*img_offset='", " "); //NON-NLS
result = result.replaceAll("'[\t ]*len='", " "); //NON-NLS
result = result.replaceAll("'/>[\t ]*", ""); //NON-NLS
fields = result.split(" "); /// offset, image offset, length //NON-NLS
ranges.add((new TskFileRange(af.convertToImgOffset(Long.parseLong(fields[1])), Long.parseLong(fields[2]), ranges.size())));
// read the next line
line = in.readLine();
}
carvedFileContainer.add(new CarvedFileContainer(fileName, fileSize, id, ranges));
}
in.close(); // close the BufferedReader
return fileManager.addCarvedFiles(carvedFileContainer);
}
catch (IOException | NumberFormatException | TskCoreException ex) {
logger.log(Level.SEVERE, "Error parsing PhotoRec output and inserting it into the database" + ex); //NON_NLS
}
return null;
}
}

View File

@ -60,6 +60,8 @@ import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
import net.sf.sevenzipjbinding.ArchiveFormat;
import static net.sf.sevenzipjbinding.ArchiveFormat.RAR;
/**
* 7Zip ingest module extracts supported archives, adds extracted DerivedFiles,
@ -260,6 +262,53 @@ public final class SevenZipIngestModule implements FileIngestModule {
return false;
}
}
/**
* Check file extension and return appropriate input options for SevenZip.openInArchive()
*
* @param archiveFile file to check file extension
* @return input parameter for SevenZip.openInArchive()
*/
private ArchiveFormat get7ZipOptions(AbstractFile archiveFile)
{
// try to get the file type from the BB
String detectedFormat = null;
try {
ArrayList<BlackboardAttribute> attributes = archiveFile.getGenInfoAttributes(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG);
for (BlackboardAttribute attribute : attributes) {
detectedFormat = attribute.getValueString();
break;
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Couldn't obtain file attributes for file: " + archiveFile.toString(), ex);
}
if (detectedFormat == null) {
logger.log(Level.WARNING, "Could not detect format for file: " + archiveFile); //NON-NLS
// if we don't have attribute info then use file extension
String extension = archiveFile.getNameExtension();
if ("rar".equals(extension))
{
// for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
// it will be opened incorrectly when using 7zip's built-in auto-detect functionality
return RAR;
}
// Otherwise open the archive using 7zip's built-in auto-detect functionality
return null;
}
else if (detectedFormat.contains("application/x-rar-compressed"))
{
// for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
// it will be opened incorrectly when using 7zip's built-in auto-detect functionality
return RAR;
}
// Otherwise open the archive using 7zip's built-in auto-detect functionality
return null;
}
/**
* Unpack the file to local folder and return a list of derived files
@ -301,8 +350,12 @@ public final class SevenZipIngestModule implements FileIngestModule {
boolean progressStarted = false;
try {
stream = new SevenZipContentReadStream(new ReadContentInputStream(archiveFile));
inArchive = SevenZip.openInArchive(null, // autodetect archive type
stream);
// for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
// it will be opened incorrectly when using 7zip's built-in auto-detect functionality.
// All other archive formats are still opened using 7zip built-in auto-detect functionality.
ArchiveFormat options = get7ZipOptions(archiveFile);
inArchive = SevenZip.openInArchive(options, stream);
int numItems = inArchive.getNumberOfItems();
logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{archiveFile.getName(), numItems}); //NON-NLS

View File

@ -67,7 +67,7 @@ public class ArtifactSelectionDialog extends javax.swing.JDialog {
try {
ArrayList<BlackboardArtifact.ARTIFACT_TYPE> doNotReport = new ArrayList<>();
doNotReport.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_GEN_INFO);
doNotReport.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT); // output is too unstructured for table review.
artifacts = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifactTypesInUse();
artifacts.removeAll(doNotReport);

View File

@ -82,6 +82,15 @@ ReportGenerator.tagTable.header.tag=Tag
ReportGenerator.tagTable.header.comment=Comment
ReportGenerator.tagTable.header.srcFile=Source File
ReportGenerator.progress.createdThumb.text=Creating thumbnails...
ReportGenerator.htmlOutput.header.file=File
ReportGenerator.htmlOutput.header.tag=Tag
ReportGenerator.htmlOutput.header.comment=Comment
ReportGenerator.htmlOutput.header.timeModified=Modified Time
ReportGenerator.htmlOutput.header.timeChanged=Changed Time
ReportGenerator.htmlOutput.header.timeAccessed=Accessed Time
ReportGenerator.htmlOutput.header.timeCreated=Created Time
ReportGenerator.htmlOutput.header.size=Size (Bytes)
ReportGenerator.htmlOutput.header.hash=Hash
ReportGenerator.thumbnailTable.name=Thumbnails
ReportGenerator.thumbnailTable.desc=Contains thumbnails of images that are associated with tagged files and results.
ReportGenerator.writeKwHits.userSrchs=User Searches
@ -195,3 +204,6 @@ ReportHTML.writeIndex.srcModuleName.text=HTML Report
ReportKML.genReport.srcModuleName.text=KML Report
ReportGenerator.artTableColHdr.extension.text=Extension
ReportGenerator.artTableColHdr.mimeType.text=MIME Type
ReportGenerator.artTableColHdr.processorArchitecture.text=Processor Architecture
ReportGenerator.artTableColHdr.osName.text=Operating System Name
ReportGenerator.artTableColHdr.osInstallDate.text=Install Date

View File

@ -19,15 +19,10 @@
package org.sleuthkit.autopsy.report;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
@ -41,7 +36,6 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
public final class ReportBranding implements ReportBrandingProviderI {
//property names
private static final String GENERATOR_LOGO_PATH_PROP = "GeneratorLogoPath"; //NON-NLS
private static final String AGENCY_LOGO_PATH_PROP = "AgencyLogoPath"; //NON-NLS
private static final String REPORT_TITLE_PROP = "ReportTitle"; //NON-NLS
private static final String REPORT_FOOTER_PROP = "ReportFooter"; //NON-NLS
@ -54,6 +48,7 @@ public final class ReportBranding implements ReportBrandingProviderI {
private String reportsBrandingDir; //dir with extracted reports branding resources
private static final String MODULE_NAME = ReportBranding.class.getSimpleName();
private static final Logger logger = Logger.getLogger(ReportBranding.class.getName());
private static String generatorLogoPath;
public ReportBranding() {
@ -69,7 +64,7 @@ public final class ReportBranding implements ReportBrandingProviderI {
//TODO use defaults
}
}
getGeneratorLogoPath();
extractGeneratorLogo();
getAgencyLogoPath();
getReportTitle();
}
@ -78,31 +73,24 @@ public final class ReportBranding implements ReportBrandingProviderI {
public String getReportsBrandingDir() {
return reportsBrandingDir;
}
private void extractGeneratorLogo() {
try {
PlatformUtil.extractResourceToUserConfigDir(getClass(), DEFAULT_GENERATOR_LOGO, true);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error extracting report branding resource for generator logo ", ex); //NON-NLS
}
generatorLogoPath = PlatformUtil.getUserConfigDirectory() + File.separator + DEFAULT_GENERATOR_LOGO;
}
@Override
public String getGeneratorLogoPath() {
String curPath = null;
try {
curPath = ModuleSettings.getConfigSetting(MODULE_NAME, GENERATOR_LOGO_PATH_PROP);
if (curPath == null || (!curPath.isEmpty() && !new File(curPath).canRead() ) ) {
//use default
logger.log(Level.INFO, "Using default report branding for generator logo"); //NON-NLS
curPath = reportsBrandingDir + File.separator + "logo.png"; //NON-NLS
InputStream in = getClass().getResourceAsStream(DEFAULT_GENERATOR_LOGO);
OutputStream output = new FileOutputStream(new File(curPath));
FileUtil.copy(in, output);
ModuleSettings.setConfigSetting(MODULE_NAME, GENERATOR_LOGO_PATH_PROP, curPath);
}
} catch (IOException e) {
logger.log(Level.SEVERE, "Error extracting report branding resources for generator logo", e); //NON-NLS
}
return curPath;
return generatorLogoPath;
}
@Override
public void setGeneratorLogoPath(String path) {
ModuleSettings.setConfigSetting(MODULE_NAME, GENERATOR_LOGO_PATH_PROP, path);
}
@Override

View File

@ -25,7 +25,6 @@ import java.util.List;
import java.util.logging.Level;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -42,7 +41,6 @@ import org.sleuthkit.datamodel.TskCoreException;
private CellStyle elementStyle;
private int rowIndex = 0;
private int sheetColCount = 0;
private int artifactsCount = 0;
private String reportPath;
// Get the default instance of this report
@ -142,31 +140,6 @@ import org.sleuthkit.datamodel.TskCoreException;
sheet = wb.createSheet(name);
sheet.setAutobreaks(true);
rowIndex = 0;
artifactsCount = 0;
// Add a title row to the worksheet.
Row row = sheet.createRow(rowIndex);
row.setRowStyle(setStyle);
row.createCell(0).setCellValue(name);
++rowIndex;
// Add an artifacts count row. The actual count will be filled in later.
row = sheet.createRow(rowIndex);
row.setRowStyle(setStyle);
row.createCell(0).setCellValue(NbBundle.getMessage(this.getClass(), "ReportExcel.numAartifacts.text"));
++rowIndex;
// Add a comment row, if a comment was supplied.
if (!description.isEmpty()) {
row = sheet.createRow(rowIndex);
row.setRowStyle(setStyle);
row.createCell(0).setCellValue(description);
++rowIndex;
}
// Add an empty row as a separator.
sheet.createRow(rowIndex);
++rowIndex;
// There will be at least two columns, one each for the artifacts count and its label.
sheetColCount = 2;
@ -176,12 +149,7 @@ import org.sleuthkit.datamodel.TskCoreException;
* End the current data type and sheet.
*/
@Override
public void endDataType() {
// Fill in the artifact count cell in row 0.
Row row = sheet.getRow(1);
row.setRowStyle(setStyle);
row.createCell(1).setCellValue(artifactsCount);
public void endDataType() {
// Now that the sheet is complete, size the columns to the content.
for (int i = 0; i < sheetColCount; ++i) {
sheet.autoSizeColumn(i);
@ -268,7 +236,6 @@ import org.sleuthkit.datamodel.TskCoreException;
row.createCell(i).setCellValue(rowData.get(i));
}
++rowIndex;
++artifactsCount;
}
/**

View File

@ -564,7 +564,17 @@ import org.sleuthkit.datamodel.TskData;
tableProgress.get(module).updateStatusLabel(
NbBundle.getMessage(this.getClass(), "ReportGenerator.progress.processing",
ARTIFACT_TYPE.TSK_TAG_FILE.getDisplayName()));
ArrayList<String> columnHeaders = new ArrayList<>(Arrays.asList("File", "Tag", "Comment")); //NON-NLS
ArrayList<String> columnHeaders = new ArrayList<>(Arrays.asList(
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.tag"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.file"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.comment"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeModified"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeChanged"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeAccessed"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeCreated"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.size"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.hash")));
StringBuilder comment = new StringBuilder();
if (!tagNamesFilter.isEmpty()) {
comment.append(
@ -596,7 +606,7 @@ import org.sleuthkit.datamodel.TskData;
fileName = tag.getContent().getName();
}
ArrayList<String> rowData = new ArrayList<>(Arrays.asList(fileName, tag.getName().getDisplayName(), tag.getComment()));
ArrayList<String> rowData = new ArrayList<>(Arrays.asList(tag.getName().getDisplayName(), fileName, tag.getComment()));
for (TableReportModule module : tableModules) {
// @@@ This casting is a tricky little workaround to allow the HTML report module to slip in a content hyperlink.
if (module instanceof ReportHTML) {
@ -1327,6 +1337,13 @@ import org.sleuthkit.datamodel.TskData;
NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.mimeType.text"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.path")}));
break;
case TSK_OS_INFO:
columnHeaders = new ArrayList<>(Arrays.asList(new String[] {
NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.processorArchitecture.text"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.osName.text"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.osInstallDate.text"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.srcFile")}));
break;
default:
return null;
}
@ -1667,6 +1684,12 @@ import org.sleuthkit.datamodel.TskData;
}
orderedRowData.add(file.getUniquePath());
break;
case TSK_OS_INFO:
orderedRowData.add(mappedAttributes.get(ATTRIBUTE_TYPE.TSK_PROCESSOR_ARCHITECTURE.getTypeID()));
orderedRowData.add(mappedAttributes.get(ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID()));
orderedRowData.add(mappedAttributes.get(ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID()));
orderedRowData.add(getFileUniquePath(getObjectID()));
break;
}
orderedRowData.add(makeCommaSeparatedList(getTags()));

View File

@ -227,6 +227,9 @@ import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
case TSK_GPS_SEARCH:
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gps-search.png"); //NON-NLS
break;
case TSK_OS_INFO:
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/computer.png"); //NON-NLS
break;
default:
logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = " + dataType); //NON-NLS
@ -526,7 +529,6 @@ import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
addRow(row);
return;
}
AbstractFile file = (AbstractFile) content;
// Don't make a local copy of the file if it is a directory or unallocated space.
if (file.isDir() ||
@ -535,7 +537,15 @@ import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
row.add("");
return;
}
// Add metadata about the file to HTML output
row.add(file.getMtimeAsDate());
row.add(file.getCtimeAsDate());
row.add(file.getAtimeAsDate());
row.add(file.getCrtimeAsDate());
row.add(Long.toString(file.getSize()));
row.add(file.getMd5Hash());
// save it in a folder based on the tag name
String localFilePath = saveContent(file, contentTag.getName().getDisplayName());
@ -543,13 +553,23 @@ import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
StringBuilder localFileLink = new StringBuilder();
localFileLink.append("<a href=\""); //NON-NLS
localFileLink.append(localFilePath);
localFileLink.append("\">").append(NbBundle.getMessage(this.getClass(), "ReportHTML.link.viewFile")).append("</a>"); //NON-NLS
row.add(localFileLink.toString());
localFileLink.append("\">");
StringBuilder builder = new StringBuilder();
builder.append("\t<tr>\n"); //NON-NLS
int positionCounter=0;
for (String cell : row) {
builder.append("\t\t<td>").append(cell).append("</td>\n"); //NON-NLS
// position-dependent code used to format this report. Not great, but understandable for formatting.
if(positionCounter==1) { // Convert the file name to a hyperlink and left-align it
builder.append("\t\t<td class=\"left_align_cell\">").append(localFileLink.toString()).append(cell).append("</a></td>\n"); //NON-NLS
}
else if (positionCounter==7) { // Right-align the bytes column.
builder.append("\t\t<td class=\"right_align_cell\">").append(cell).append("</td>\n"); //NON-NLS
}
else { // Regular case, not a file name nor a byte count
builder.append("\t\t<td>").append(cell).append("</td>\n"); //NON-NLS
}
++positionCounter;
}
builder.append("\t</tr>\n"); //NON-NLS
rowCount++;
@ -559,7 +579,7 @@ import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
}
catch (IOException ex) {
logger.log(Level.SEVERE, "Failed to write row to out.", ex); //NON-NLS
}
}
catch (NullPointerException ex) {
logger.log(Level.SEVERE, "Output writer is null. Page was not initialized before writing.", ex); //NON-NLS
}
@ -767,10 +787,12 @@ import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
"ul li a {font-size: 14px; color: #444; text-decoration: none; padding-left: 25px;}\n" + //NON-NLS
"ul li a:hover {text-decoration: underline;}\n" + //NON-NLS
"p {margin: 0 0 20px 0;}\n" + //NON-NLS
"table {max-width: 100%; min-width: 700px; padding: 0; margin: 0; border-collapse: collapse; border-bottom: 2px solid #e5e5e5;}\n" + //NON-NLS
".keyword_list table {width: 100%; margin: 0 0 25px 25px; border-bottom: 2px solid #dedede;}\n" + //NON-NLS
"table th {display: table-cell; text-align: left; padding: 8px 16px; background: #e5e5e5; color: #777; font-size: 11px; text-shadow: #e9f9fd 0 1px 0; border-top: 1px solid #dedede; border-bottom: 2px solid #e5e5e5;}\n" + //NON-NLS
"table td {display: table-cell; padding: 8px 16px; font: 13px/20px Arial, Helvetica, sans-serif; max-width: 500px; min-width: 125px; word-break: break-all; overflow: auto;}\n" + //NON-NLS
"table {white-space:nowrap; min-width: 700px; padding: 2; margin: 0; border-collapse: collapse; border-bottom: 2px solid #e5e5e5;}\n" + //NON-NLS
".keyword_list table {margin: 0 0 25px 25px; border-bottom: 2px solid #dedede;}\n" + //NON-NLS
"table th {white-space:nowrap; display: table-cell; text-align: center; padding: 2px 4px; background: #e5e5e5; color: #777; font-size: 11px; text-shadow: #e9f9fd 0 1px 0; border-top: 1px solid #dedede; border-bottom: 2px solid #e5e5e5;}\n" + //NON-NLS
"table .left_align_cell{display: table-cell; padding: 2px 4px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align: left; }\n" + //NON-NLS
"table .right_align_cell{display: table-cell; padding: 2px 4px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align: right; }\n" + //NON-NLS
"table td {white-space:nowrap; display: table-cell; padding: 2px 3px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align:left; }\n" + //NON-NLS
"table tr:nth-child(even) td {background: #f3f3f3;}"; //NON-NLS
cssOut.write(css);
} catch (FileNotFoundException ex) {

View File

@ -110,6 +110,7 @@ final class ReportVisualPanel2 extends JPanel {
try {
ArrayList<BlackboardArtifact.ARTIFACT_TYPE> doNotReport = new ArrayList<>();
doNotReport.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_GEN_INFO);
doNotReport.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT); // output is too unstructured for table review
artifacts = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifactTypesInUse();

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Some files were not shown because too many files have changed in this diff Show More