66import java .io .FileInputStream ;
77import java .io .IOException ;
88import java .io .InputStream ;
9- import java .io .InputStreamReader ;
109import java .io .OutputStreamWriter ;
1110import java .nio .charset .StandardCharsets ;
1211import java .nio .file .Files ;
3938import javax .xml .parsers .SAXParser ;
4039import javax .xml .parsers .SAXParserFactory ;
4140
41+ import org .apache .commons .io .FileUtils ;
42+ import org .apache .commons .io .FilenameUtils ;
4243import org .apache .commons .lang3 .StringUtils ;
4344import org .apache .logging .log4j .Level ;
4445import org .apache .logging .log4j .LogManager ;
@@ -99,6 +100,12 @@ public class UfedXmlReader extends DataSourceReader {
99100 private final Level CONSOLE = Level .getLevel ("MSG" ); //$NON-NLS-1$
100101
101102 private static final String [] HEADER_STRINGS = { "project id" , "extractionType" , "sourceExtractions" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
103+ private static final byte [] UFDR_PROTECTION_NO_PASSWORD_REPORT_XML_INITIAL_BYTES = new byte [] { 0x2D , 0x06 , 0x52 , 0x6D };
104+ private static final int UFDR_REPORT_XML_INITIAL_BYTES_TO_READ = 1024 ;
105+
106+ private static final String UFDR_EXTENSION = "ufdr" ;
107+ private static final String XML_REPORT_EXTENSION = "xml" ;
108+
102109
103110 private static final String AVATAR_PATH_META = ExtraProperties .UFED_META_PREFIX + "contactphoto_extracted_path" ; //$NON-NLS-1$
104111 private static final String ATTACH_PATH_META = ExtraProperties .UFED_META_PREFIX + "attachment_extracted_path" ; //$NON-NLS-1$
@@ -152,41 +159,40 @@ public UfedXmlReader(ICaseData caseData, File output, boolean listOnly) {
152159 @ Override
153160 public boolean isSupported (File datasource ) {
154161
155- if (datasource .getName (). toLowerCase (). endsWith ( ".ufdr" )) {
162+ if (FilenameUtils . isExtension ( datasource .getName (), UFDR_EXTENSION )) {
156163 return true ;
157164 }
158165
159- InputStream xmlReport = lookUpXmlReportInputStream (datasource );
160- IOUtil .closeQuietly (xmlReport );
161-
162- if (xmlReport != null )
163- return true ;
164-
165- return false ;
166+ // supports any folder with valid XML report inside
167+ try (InputStream xmlReport = lookUpXmlReportInputStream (datasource )) {
168+ return xmlReport != null ;
169+ } catch (Exception e ) {
170+ return false ;
171+ }
166172 }
167173
168174 private InputStream getXmlInputStream (File file ) {
169- if (file .getName ().toLowerCase ().endsWith (".xml" )) { //$NON-NLS-1$
170- try (InputStreamReader reader = new InputStreamReader (new FileInputStream (file ), "UTF-8" )) { //$NON-NLS-1$
171- char [] cbuf = new char [1024 ];
172- int off = 0 , i = 0 ;
173- while (off < cbuf .length && (i = reader .read (cbuf , off , cbuf .length - off )) != -1 )
174- off += i ;
175- String header = new String (cbuf , 0 , off );
176- for (String str : HEADER_STRINGS )
177- if (!header .contains (str ))
178- return null ;
175+ if (FilenameUtils .isExtension (file .getName (), XML_REPORT_EXTENSION )) {
176+ try (InputStream fis = new FileInputStream (file )) {
177+ byte [] initialBytes = fis .readNBytes (UFDR_REPORT_XML_INITIAL_BYTES_TO_READ );
178+ if (!containsHeaderStrings (initialBytes )) {
179+ return null ;
180+ }
181+
179182 return new FileInputStream (file );
180183
181184 } catch (IOException e ) {
182185 throw new RuntimeException (e );
183186 }
184- } else if (file .getName (). toLowerCase (). endsWith ( ".ufdr" )) {
187+ } else if (FilenameUtils . isExtension ( file .getName (), UFDR_EXTENSION )) {
185188 try {
186189 ufdrFile = file ;
187190 String xml = "report.xml" ;
188191 if (!getUISF ().entryExists (xml )) {
189192 xml = "Report.xml" ;
193+ if (!getUISF ().entryExists (xml )) {
194+ return null ;
195+ }
190196 }
191197 return getUISF ().getSeekableInputStream (xml );
192198
@@ -197,6 +203,21 @@ private InputStream getXmlInputStream(File file) {
197203 return null ;
198204 }
199205
206+ private boolean isReportXmlProtected (byte [] initialBytes ) {
207+ int len = UFDR_PROTECTION_NO_PASSWORD_REPORT_XML_INITIAL_BYTES .length ;
208+ return Arrays .equals (initialBytes , 0 , len , UFDR_PROTECTION_NO_PASSWORD_REPORT_XML_INITIAL_BYTES , 0 , len );
209+ }
210+
211+ private boolean containsHeaderStrings (byte [] initialBytes ) {
212+ String header = new String (initialBytes , StandardCharsets .UTF_8 );
213+ for (String str : HEADER_STRINGS )
214+ if (header .contains (str ))
215+ return true ;
216+
217+ return false ;
218+
219+ }
220+
200221 private boolean entryExists (String entryPath ) {
201222 if (ufdrFile != null ) {
202223 return getUISF ().entryExists (entryPath );
@@ -233,16 +254,13 @@ private FileInputStreamFactory getFISF() {
233254 private InputStream lookUpXmlReportInputStream (File root ) {
234255 if (root .isFile ())
235256 return getXmlInputStream (root );
236- File [] files = root .listFiles ();
237- if (files != null ) {
238- for (File file : files ) {
239- if (file .getName ().toLowerCase ().endsWith (".xml" )) {
240- InputStream is = getXmlInputStream (file );
241- if (is != null )
242- return is ;
243- }
244- }
257+
258+ for (File file : FileUtils .listFiles (root , new String []{XML_REPORT_EXTENSION }, false )) {
259+ InputStream is = getXmlInputStream (file );
260+ if (is != null )
261+ return is ;
245262 }
263+
246264 return null ;
247265 }
248266
@@ -261,6 +279,8 @@ public void read(File root, Item parent) throws Exception {
261279 try {
262280 xmlStream = lookUpXmlReportInputStream (root );
263281
282+ validateXmlStream (xmlStream );
283+
264284 configureParsers ();
265285
266286 SAXParserFactory spf = SAXParserFactory .newInstance ();
@@ -275,6 +295,33 @@ public void read(File root, Item parent) throws Exception {
275295 }
276296 }
277297
298+ private void validateXmlStream (InputStream xmlStream ) throws IOException {
299+ if (xmlStream == null ) {
300+ if (root .isFile ()) {
301+ throw new RuntimeException ("Invalid UFDR file: XML report not found. File: " + root .getAbsolutePath ());
302+ } else {
303+ throw new RuntimeException ("Invalid UFDR folder: No XML report has been found. Folder: " + root .getAbsolutePath ());
304+ }
305+ }
306+
307+ if (root .isFile ()) {
308+ xmlStream .mark (0 );
309+ byte [] initialBytes = xmlStream .readNBytes (UFDR_REPORT_XML_INITIAL_BYTES_TO_READ );
310+
311+ if (isReportXmlProtected (initialBytes )) {
312+ throw new RuntimeException ("Unsupported UFDR file: protection is enabled. Generate the UFDR file again with protection disabled."
313+ + (root != null ? " File: " + root .getAbsolutePath () : "" ));
314+ }
315+
316+ if (!containsHeaderStrings (initialBytes )) {
317+ throw new RuntimeException ("Invalid UFDR file: XML report is not valid. "
318+ + "Protection may be enabled. Generate the UFDR file again with protection disabled."
319+ + (root != null ? " File: " + root .getAbsolutePath () : "" ));
320+ }
321+ xmlStream .reset ();
322+ }
323+ }
324+
278325 private void configureParsers () {
279326 configureParsers (false );
280327 }
@@ -317,7 +364,7 @@ private void addRootItem(IItem parent) throws InterruptedException {
317364 rootItem .setDataSource (evidenceSource );
318365 rootItem .setIdInDataSource ("" );
319366 rootItem .setHasChildren (true );
320- if (root .getName (). endsWith ( ".ufdr" )) {
367+ if (FilenameUtils . isExtension ( root .getName (), UFDR_EXTENSION )) {
321368 rootItem .setLength (root .length ());
322369 rootItem .setSumVolume (false );
323370 }
0 commit comments