diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java index 481d3aa23c..d85755bce5 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java @@ -19,114 +19,126 @@ package org.sleuthkit.autopsy.modules.android; import java.io.File; +import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.List; +import java.util.Arrays; import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; class CallLogAnalyzer { private static final String moduleName = AndroidModuleFactory.getModuleName(); + private static final Logger logger = Logger.getLogger(CallLogAnalyzer.class.getName()); + /** the where clause(without 'where' of sql select statement to choose call + * log dbs, update the list of file names to include more files */ + private static final String fileNameQuery = Stream.of("'logs.db'", "'contacts2.db'", "'contacts.db'") + .collect(Collectors.joining(" OR name = ", "name = ", "")); + + /** the names of tables that potentially hold call logs in the dbs */ + private static final Iterable tableNames = Arrays.asList("calls", "logs"); + public static void findCallLogs() { - List absFiles; try { SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); - absFiles = skCase.findAllFilesWhere("name ='contacts2.db' OR name ='contacts.db'"); //get exact file names - if (absFiles.isEmpty()) { - return; - } - for (AbstractFile abstractFile : absFiles) { - try { - File jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName()); - ContentUtils.writeToFile(abstractFile, jFile); - findCallLogsInDB(jFile.toString(), abstractFile); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing Call logs", e); + for (AbstractFile abstractFile : skCase.findAllFilesWhere(fileNameQuery)) { + try { + File file = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName()); + ContentUtils.writeToFile(abstractFile, file); + findCallLogsInDB(file.toString(), abstractFile); + } catch (IOException e) { + logger.log(Level.SEVERE, "Error writing temporary call log db to disk", e); } } } catch (TskCoreException e) { - logger.log(Level.SEVERE, "Error finding Call logs", e); + logger.log(Level.SEVERE, "Error finding call logs", e); } } private static void findCallLogsInDB(String DatabasePath, AbstractFile f) { - Connection connection = null; - ResultSet resultSet = null; - Statement statement = null; if (DatabasePath == null || DatabasePath.isEmpty()) { return; } - try { - Class.forName("org.sqlite.JDBC"); //load JDBC driver - connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); - statement = connection.createStatement(); - } catch (ClassNotFoundException | SQLException e) { - logger.log(Level.SEVERE, "Error opening database", e); - return; + try (Connection connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); + Statement statement = connection.createStatement();) { + + for (String tableName : tableNames) { + try (ResultSet resultSet = statement.executeQuery( + "SELECT number,date,duration,type, name FROM " + tableName + " ORDER BY date DESC;");) { + logger.log(Level.INFO, "Reading call log from table {0} in db {1}", new Object[]{tableName, DatabasePath}); + while (resultSet.next()) { + Long date = resultSet.getLong("date") / 1000; + final CallDirection direction = CallDirection.fromType(resultSet.getInt("type")); + String directionString = direction != null ? direction.getDisplayName() : ""; + final String number = resultSet.getString("number"); + final long duration = resultSet.getLong("duration");//duration of call is in seconds + final String name = resultSet.getString("name");// name of person dialed or called. null if unregistered + + try { + BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG); //create a call log and then add attributes from result set. + bba.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), moduleName, number)); + bba.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(), moduleName, date)); + bba.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID(), moduleName, duration + date)); + bba.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(), moduleName, directionString)); + bba.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, name)); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error posting call log record to the Blackboard", ex); + } + } + } catch (SQLException e) { + logger.log(Level.WARNING, "Could not read table {0} in db {1}", new Object[]{tableName, DatabasePath}); + } + } + } catch (SQLException e) { + logger.log(Level.SEVERE, "Could not parse call log; error connecting to db " + DatabasePath, e); + } + } + + private static enum CallDirection { + + INCOMING(1, "Incoming"), OUTGOING(2, "Outgoing"), MISSED(3, "Missed"); + + private final int type; + + private final String displayName; + + public String getDisplayName() { + return displayName; } - try { - resultSet = statement.executeQuery( - "SELECT number,date,duration,type, name FROM calls ORDER BY date DESC;"); - - BlackboardArtifact bba; - - while (resultSet.next()) { - // name of person dialed or called. null if unregistered - String name = resultSet.getString("name"); - String number = resultSet.getString("number"); - //duration of call in seconds - Long duration = Long.valueOf(resultSet.getString("duration")); - Long date = Long.valueOf(resultSet.getString("date")) / 1000; - - String direction = ""; - switch (Integer.valueOf(resultSet.getString("type"))) { - case 1: - direction = "Incoming"; - break; - case 2: - direction = "Outgoing"; - break; - case 3: - direction = "Missed"; - break; - } - - bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG); //create a call log and then add attributes from result set. - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), moduleName, number)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(), moduleName, date)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID(), moduleName, duration + date)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(), moduleName, direction)); - bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, name)); - } - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing Call logs to the Blackboard", e); - } finally { - try { - if (resultSet != null) { - resultSet.close(); - } - statement.close(); - connection.close(); - } catch (Exception e) { - logger.log(Level.SEVERE, "Error closing the database", e); - } + private CallDirection(int type, String displayName) { + this.type = type; + this.displayName = displayName; } + static CallDirection fromType(int t) { + switch (t) { + case 1: + return INCOMING; + case 2: + return OUTGOING; + case 3: + return MISSED; + default: + return null; + } + } } }