Merge branch 'develop' of github.com:sleuthkit/autopsy into develop
27
BUILDING.txt
@ -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.
|
||||
|
||||
|
||||
---------------
|
||||
|
@ -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>
|
||||
|
BIN
Core/release/photorec_exec/63/cygwin
Executable file
4
Core/release/photorec_exec/AUTHORS.txt
Executable 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.
|
339
Core/release/photorec_exec/COPYING.txt
Executable 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.
|
3
Core/release/photorec_exec/INFO
Executable file
@ -0,0 +1,3 @@
|
||||
TestDisk & PhotoRec , http://www.cgsecurity.org
|
||||
Copyright (C) 1998-2014 Christophe GRENIER <grenier@cgsecurity.org>
|
||||
|
390
Core/release/photorec_exec/NEWS.txt
Executable 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.
|
||||
|
6
Core/release/photorec_exec/THANKS.txt
Executable 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!
|
1
Core/release/photorec_exec/VERSION.txt
Executable file
@ -0,0 +1 @@
|
||||
7.0-WIP Sun Oct 5 21:22:27 CEST 2014
|
BIN
Core/release/photorec_exec/cyggcc_s-1.dll
Executable file
BIN
Core/release/photorec_exec/cygiconv-2.dll
Executable file
BIN
Core/release/photorec_exec/cygjpeg-8.dll
Executable file
BIN
Core/release/photorec_exec/cygncursesw-10.dll
Executable file
BIN
Core/release/photorec_exec/cygwin1.dll
Executable file
BIN
Core/release/photorec_exec/cygz.dll
Executable file
12
Core/release/photorec_exec/documentation.html
Executable file
@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>TestDisk & PhotoRec documentation</title>
|
||||
</head>
|
||||
<body>
|
||||
TestDisk & 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>
|
BIN
Core/release/photorec_exec/fidentify_win.exe
Executable file
BIN
Core/release/photorec_exec/photorec_win.exe
Executable file
143
Core/release/photorec_exec/plugins/BartPE/Get_Files.cmd
Executable 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
|
69
Core/release/photorec_exec/plugins/BartPE/Help.htm
Executable 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> </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">© 2007-2008 Windows Xpire Tech Center <br>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
6
Core/release/photorec_exec/plugins/BartPE/RESET.cmd
Executable 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
|
19
Core/release/photorec_exec/plugins/BartPE/ReadMe.txt
Executable 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.
|
||||
-------------------------------------------------------------------------
|
20
Core/release/photorec_exec/plugins/BartPE/SCRIPTS/Start_INF.dat
Executable 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
|
36
Core/release/photorec_exec/plugins/BartPE/SCRIPTS/StaticINF.dat
Executable 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
|
20
Core/release/photorec_exec/plugins/BartPE/start.inf
Executable 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
|
7
Core/release/photorec_exec/plugins/BartPE/testdisk_nu2menu.xml
Executable 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>
|
66
Core/release/photorec_exec/plugins/WinBuilder/Help.htm
Executable 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> </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">© 2007-2008 Windows Xpire Tech Center <br>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
20
Core/release/photorec_exec/plugins/WinBuilder/ReadMe.txt
Executable 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.
|
||||
-------------------------------------------------------------------------
|
52
Core/release/photorec_exec/plugins/WinBuilder/TestDisk.script
Executable 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
|
||||
|
23
Core/release/photorec_exec/readme.txt
Executable 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
|
25
Core/release/photorec_exec/sort.py
Executable 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)
|
BIN
Core/release/photorec_exec/testdisk_win.exe
Executable 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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").
|
||||
|
@ -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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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("");"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSeparator" name="jSeparator1">
|
||||
<Properties>
|
||||
<Property name="orientation" type="int" value="1"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
|
Before Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 72 KiB |
@ -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>
|
@ -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>
|
@ -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
|
||||
*/
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 33 KiB |
@ -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();
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ import org.openide.explorer.view.BeanTreeView;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jantonius
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
BIN
Core/src/org/sleuthkit/autopsy/images/computer.png
Normal file
After Width: | Height: | Size: 566 B |
@ -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
|
||||
|
@ -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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelAllModulesRadioButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -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
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
12
Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties
Executable 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}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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()));
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
BIN
Core/src/org/sleuthkit/autopsy/report/images/computer.png
Normal file
After Width: | Height: | Size: 566 B |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 54 KiB |