19 package org.sleuthkit.datamodel;
 
   21 import com.google.common.base.Strings;
 
   22 import com.google.common.annotations.Beta;
 
   23 import org.apache.commons.lang3.StringUtils;
 
   24 import java.sql.PreparedStatement;
 
   25 import java.sql.ResultSet;
 
   26 import java.sql.SQLException;
 
   27 import java.sql.Statement;
 
   28 import java.sql.Types;
 
   29 import java.util.Collections;
 
   30 import java.util.ArrayList;
 
   31 import java.util.List;
 
   32 import java.util.Locale;
 
   33 import java.util.NavigableMap;
 
   34 import java.util.Objects;
 
   35 import java.util.Optional;
 
   36 import java.util.UUID;
 
   37 import java.util.concurrent.ConcurrentSkipListMap;
 
   38 import java.util.stream.Collectors;
 
   58         private final Object osAcctInstancesCacheLock;
 
   59         private final NavigableMap<OsAccountInstanceKey, OsAccountInstance> osAccountInstanceCache;
 
   69                 osAcctInstancesCacheLock = 
new Object();
 
   70                 osAccountInstanceCache = 
new ConcurrentSkipListMap<>();
 
   89                 if (Strings.isNullOrEmpty(uniqueAccountId)) {
 
  106                         } 
catch (SQLException ex) {
 
  112                                 Optional<OsAccount> osAccount = this.getOsAccountByAddr(uniqueAccountId, realm);
 
  113                                 if (osAccount.isPresent()) {
 
  114                                         return osAccount.get();
 
  118                                 throw new TskCoreException(String.format(
"Error creating OsAccount with uniqueAccountId = %s in realm id = %d", uniqueAccountId, realm.getRealmId()), ex);
 
  156                 if (realmScope == null) {
 
  157                         throw new TskCoreException(
"RealmScope cannot be null. Use UNKNOWN if scope is not known.");
 
  159                 if (referringHost == null) {
 
  160                         throw new TskCoreException(
"A referring host is required to create an account.");
 
  164                 if ((StringUtils.isBlank(sid) || sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) 
 
  165                                 && StringUtils.isBlank(loginName)) {
 
  166                         throw new TskCoreException(
"Cannot create OS account with both uniqueId and loginName as null.");
 
  169                 if ((StringUtils.isBlank(sid) || sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) 
 
  170                                 && StringUtils.isBlank(realmName)) {
 
  171                         throw new TskCoreException(
"Realm name or SID is required to create a Windows account.");
 
  174                 if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && !WindowsAccountUtils.isWindowsUserSid(sid)) {
 
  179                 if (StringUtils.isBlank(sid) 
 
  180                         && !StringUtils.isBlank(loginName) && !StringUtils.isBlank(realmName) 
 
  181                                 && WindowsAccountUtils.isWindowsWellKnownAccountName(loginName, realmName)) {
 
  182                         sid = WindowsAccountUtils.getWindowsWellKnownAccountSid(loginName, realmName);
 
  186                 if (StringUtils.isNotBlank(sid)) {
 
  188                         sid = sid.toUpperCase(Locale.ENGLISH);
 
  190                 if (StringUtils.isNotBlank(loginName)) {
 
  192                         loginName = loginName.toLowerCase(Locale.ENGLISH);
 
  194                 if (StringUtils.isNotBlank(realmName)) {
 
  196                         realmName = realmName.toLowerCase(Locale.ENGLISH);
 
  201                 Optional<OsAccountRealm> anotherRealmWithSameName = Optional.empty();
 
  202                 Optional<OsAccountRealm> anotherRealmWithSameAddr = Optional.empty();
 
  206                 try (CaseDbConnection connection = db.getConnection()) {
 
  207                         realmUpdateResult = db.
getOsAccountRealmManager().getAndUpdateWindowsRealm(sid, realmName, referringHost, connection);
 
  209                         Optional<OsAccountRealm> realmOptional = realmUpdateResult.
getUpdatedRealm();
 
  210                         if (realmOptional.isPresent()) {
 
  211                                 realm = realmOptional.get();
 
  219                                         anotherRealmWithSameName = db.
getOsAccountRealmManager().getAnotherRealmByName(realmOptional.get(), realmName, referringHost, connection);
 
  220                                         if (anotherRealmWithSameName.isPresent() && anotherRealmWithSameName.get().getRealmAddr().isPresent()) {
 
  222                                                 anotherRealmWithSameName = Optional.empty();
 
  226                                         anotherRealmWithSameAddr = db.
getOsAccountRealmManager().getAnotherRealmByAddr(realmOptional.get(), realmName, referringHost, connection);
 
  227                                         if (anotherRealmWithSameAddr.isPresent() && !anotherRealmWithSameAddr.get().getRealmNames().isEmpty()) {
 
  229                                                 anotherRealmWithSameName = Optional.empty();
 
  240                         if (anotherRealmWithSameName.isPresent() || anotherRealmWithSameAddr.isPresent()) {
 
  244                                         if (anotherRealmWithSameName.isPresent()) {
 
  247                                         if (anotherRealmWithSameAddr.isPresent()) {
 
  252                                 } 
catch (TskCoreException ex) {
 
  283                 if ((StringUtils.isBlank(sid) || sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) 
 
  284                                 && StringUtils.isBlank(loginName)) {
 
  285                         throw new TskCoreException(
"Cannot create OS account with both uniqueId and loginName as null.");
 
  288                 if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && !WindowsAccountUtils.isWindowsUserSid(sid)) {
 
  293                 String resolvedLoginName = WindowsAccountUtils.toWellknownEnglishLoginName(loginName);
 
  299                                 String uniqueId = (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) ?  sid : null;
 
  300                                 if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && isWindowsWellKnownSid(sid)) {
 
  302                                         String wellKnownLoginName = WindowsAccountUtils.getWindowsWellKnownSidLoginName(sid);
 
  303                                         if (!StringUtils.isEmpty(wellKnownLoginName)) {
 
  304                                                 resolvedLoginName = wellKnownLoginName;
 
  311                                 if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && isWindowsWellKnownSid(sid)) {
 
  312                                         String fullName = getWindowsWellKnownSidFullName(sid);
 
  313                                         if (StringUtils.isNotBlank(fullName)) {
 
  323                         } 
catch (SQLException ex) {
 
  329                                 Optional<OsAccount> osAccount;
 
  332                                 if (!Strings.isNullOrEmpty(sid)) {
 
  333                                         osAccount = getOsAccountByAddr(sid, realm);
 
  334                                         if (osAccount.isPresent()) {
 
  335                                                 return osAccount.get();
 
  340                                 if (!Strings.isNullOrEmpty(resolvedLoginName)) {
 
  341                                         osAccount = getOsAccountByLoginName(resolvedLoginName, realm);
 
  342                                         if (osAccount.isPresent()) {
 
  343                                                 return osAccount.get();
 
  348                                 throw new TskCoreException(String.format(
"Error creating OsAccount with sid = %s, loginName = %s, realm = %s, referring host = %s",
 
  349                                                 (sid != null) ? sid : 
"Null",
 
  350                                                 (resolvedLoginName != null) ? resolvedLoginName : 
"Null",
 
  380                 if (referringHost == null) {
 
  381                         throw new TskCoreException(
"A referring host is required to create a local OS account.");
 
  385                 if (StringUtils.isBlank(uid) && StringUtils.isBlank(loginName)) {
 
  386                         throw new TskCoreException(
"Cannot create OS account with both uniqueId and loginName as null.");
 
  400                         } 
catch (SQLException ex) {
 
  406                                 Optional<OsAccount> osAccount;
 
  409                                 if (!Strings.isNullOrEmpty(uid)) {
 
  410                                         osAccount = getOsAccountByAddr(uid, localRealm);
 
  411                                         if (osAccount.isPresent()) {
 
  412                                                 return osAccount.get();
 
  417                                 if (!Strings.isNullOrEmpty(loginName)) {
 
  418                                         osAccount = getOsAccountByLoginName(loginName, localRealm);
 
  419                                         if (osAccount.isPresent()) {
 
  420                                                 return osAccount.get();
 
  425                                 throw new TskCoreException(String.format(
"Error creating OsAccount with uid = %s, loginName = %s, realm = %s, referring host = %s",
 
  426                                                 (uid != null) ? uid : 
"Null",
 
  427                                                 (loginName != null) ? loginName : 
"Null",
 
  455                 if (Objects.isNull(realm)) {
 
  456                         throw new TskCoreException(
"Cannot create an OS Account, realm is NULL.");
 
  459                 String signature = getOsAccountSignature(uniqueId, loginName);
 
  462                 CaseDbConnection connection = trans.getConnection();
 
  467                 long parentObjId = 0;
 
  469                 int objTypeId = TskData.ObjectType.OS_ACCOUNT.getObjectType();
 
  470                 long osAccountObjId = db.addObject(parentObjId, objTypeId, connection);
 
  472                 String accountInsertSQL = 
"INSERT INTO tsk_os_accounts(os_account_obj_id, login_name, realm_id, addr, signature, status)" 
  473                                 + 
" VALUES (?, ?, ?, ?, ?, ?)"; 
 
  475                 PreparedStatement preparedStatement = connection.getPreparedStatement(accountInsertSQL, Statement.NO_GENERATED_KEYS);
 
  476                 preparedStatement.clearParameters();
 
  478                 preparedStatement.setLong(1, osAccountObjId);
 
  480                 preparedStatement.setString(2, loginName);
 
  481                 preparedStatement.setLong(3, realm.getRealmId());
 
  483                 preparedStatement.setString(4, uniqueId);
 
  484                 preparedStatement.setString(5, signature);
 
  485                 preparedStatement.setInt(6, accountStatus.getId());
 
  487                 connection.executeUpdate(preparedStatement);
 
  489                 account = 
new OsAccount(db, osAccountObjId, realm.getRealmId(), loginName, uniqueId, signature,
 
  490                                 null, null, null, accountStatus, OsAccount.OsAccountDbStatus.ACTIVE);
 
  492                 trans.registerAddedOsAccount(account);
 
  507         private Optional<OsAccount> getOsAccountByAddr(String addr, Host host) 
throws TskCoreException {
 
  509                 try (CaseDbConnection connection = db.getConnection()) {
 
  510                         return getOsAccountByAddr(addr, host, connection);
 
  526         private Optional<OsAccount> getOsAccountByAddr(String uniqueId, Host host, CaseDbConnection connection) 
throws TskCoreException {
 
  528                 String whereHostClause = (host == null)
 
  530                                 : 
" ( realms.scope_host_id = " + host.getHostId() + 
" OR realms.scope_host_id IS NULL) ";
 
  532                 String queryString = 
"SELECT accounts.os_account_obj_id as os_account_obj_id, accounts.login_name, accounts.full_name, " 
  533                                 + 
" accounts.realm_id, accounts.addr, accounts.signature, " 
  534                                 + 
" accounts.type, accounts.status, accounts.created_date, accounts.db_status, " 
  535                                 + 
" realms.realm_name as realm_name, realms.realm_addr as realm_addr, realms.realm_signature, realms.scope_host_id, realms.scope_confidence, realms.db_status as realm_db_status " 
  536                                 + 
" FROM tsk_os_accounts as accounts" 
  537                                 + 
"  LEFT JOIN tsk_os_account_realms as realms" 
  538                                 + 
" ON accounts.realm_id = realms.id" 
  539                                 + 
" WHERE " + whereHostClause
 
  541                                 + 
"  AND accounts.addr = '" + uniqueId + 
"'";
 
  544                 try (Statement s = connection.createStatement();
 
  545                                 ResultSet rs = connection.executeQuery(s, queryString)) {
 
  548                                 return Optional.empty();        
 
  550                                 return Optional.of(osAccountFromResultSet(rs));
 
  552                 } 
catch (SQLException ex) {
 
  553                         throw new TskCoreException(String.format(
"Error getting OS account for unique id = %s and host = %s", uniqueId, (host != null ? host.getName() : 
"null")), ex);
 
  570         Optional<OsAccount> getOsAccountByAddr(String uniqueId, OsAccountRealm realm) 
throws TskCoreException {
 
  572                 String queryString = 
"SELECT * FROM tsk_os_accounts" 
  573                                 + 
" WHERE addr = '" + uniqueId + 
"'" 
  575                                 + 
" AND realm_id = " + realm.getRealmId();
 
  578                 try (CaseDbConnection connection = this.db.getConnection();
 
  579                                 Statement s = connection.createStatement();
 
  580                                 ResultSet rs = connection.executeQuery(s, queryString)) {
 
  583                                 return Optional.empty();        
 
  585                                 return Optional.of(osAccountFromResultSet(rs));
 
  587                 } 
catch (SQLException ex) {
 
  588                         throw new TskCoreException(String.format(
"Error getting OS account for realm = %s and uniqueId = %s.", (realm != null) ? realm.getSignature() : 
"NULL", uniqueId), ex);
 
  605         Optional<OsAccount> getOsAccountByLoginName(String loginName, OsAccountRealm realm) 
throws TskCoreException {
 
  607                 String queryString = 
"SELECT * FROM tsk_os_accounts" 
  608                                 + 
" WHERE login_name = '" + loginName + 
"'" 
  610                                 + 
" AND realm_id = " + realm.getRealmId();
 
  613                 try (CaseDbConnection connection = this.db.getConnection();
 
  614                                 Statement s = connection.createStatement();
 
  615                                 ResultSet rs = connection.executeQuery(s, queryString)) {
 
  618                                 return Optional.empty();        
 
  620                                 return Optional.of(osAccountFromResultSet(rs));
 
  622                 } 
catch (SQLException ex) {
 
  623                         throw new TskCoreException(String.format(
"Error getting OS account for realm = %s and loginName = %s.", (realm != null) ? realm.getSignature() : 
"NULL", loginName), ex);
 
  640                 try (CaseDbConnection connection = this.db.getConnection()) {
 
  657                 String queryString = 
"SELECT * FROM tsk_os_accounts" 
  658                                 + 
" WHERE os_account_obj_id = " + osAccountObjId;
 
  661                 try (Statement s = connection.createStatement();
 
  662                                 ResultSet rs = connection.executeQuery(s, queryString)) {
 
  665                                 throw new TskCoreException(String.format(
"No account found with obj id = %d ", osAccountObjId));
 
  667                                 return osAccountFromResultSet(rs);
 
  669                 } 
catch (SQLException ex) {
 
  670                         throw new TskCoreException(String.format(
"Error getting account with obj id = %d ", osAccountObjId), ex);
 
  701                 if (osAccount == null) {
 
  702                         throw new TskCoreException(
"Cannot create account instance with null account.");
 
  704                 if (dataSource == null) {
 
  705                         throw new TskCoreException(
"Cannot create account instance with null data source.");
 
  709                 Optional<OsAccountInstance> existingInstance = cachedAccountInstance(osAccount.
getId(), dataSource.
getId(), instanceType);
 
  710                 if (existingInstance.isPresent()) {
 
  711                         return existingInstance.get();
 
  714                 try (CaseDbConnection connection = this.db.getConnection()) {
 
  736                 Optional<OsAccountInstance> existingInstance = cachedAccountInstance(osAccountId, dataSourceObjId, instanceType);
 
  737                 if (existingInstance.isPresent()) {
 
  738                         return existingInstance.get();
 
  746                         String accountInsertSQL = db.getInsertOrIgnoreSQL(
"INTO tsk_os_account_instances(os_account_obj_id, data_source_obj_id, instance_type)" 
  747                                         + 
" VALUES (?, ?, ?)"); 
 
  748                         PreparedStatement preparedStatement = connection.getPreparedStatement(accountInsertSQL, Statement.RETURN_GENERATED_KEYS);
 
  749                         preparedStatement.clearParameters();
 
  750                         preparedStatement.setLong(1, osAccountId);
 
  751                         preparedStatement.setLong(2, dataSourceObjId);
 
  752                         preparedStatement.setInt(3, instanceType.getId());
 
  753                         connection.executeUpdate(preparedStatement);
 
  754                         try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) {
 
  755                                 if (resultSet.next()) {
 
  756                                         OsAccountInstance accountInstance = 
new OsAccountInstance(db, resultSet.getLong(1), osAccountId, dataSourceObjId, instanceType);
 
  757                                         synchronized (osAcctInstancesCacheLock) {
 
  758                                                 OsAccountInstanceKey key = 
new OsAccountInstanceKey(osAccountId, dataSourceObjId);
 
  760                                                 for (OsAccountInstance.OsAccountInstanceType type : OsAccountInstance.OsAccountInstanceType.values()) {
 
  761                                                         if (accountInstance.getInstanceType().compareTo(type) < 0) {
 
  762                                                                 osAccountInstanceCache.remove(key);
 
  766                                                 osAccountInstanceCache.put(key, accountInstance);
 
  783                                         db.fireTSKEvent(
new TskEvent.OsAcctInstancesAddedTskEvent(Collections.singletonList(accountInstance)));
 
  785                                         return accountInstance;
 
  790                                         Optional<OsAccountInstance> existingInstanceRetry = cachedAccountInstance(osAccountId, dataSourceObjId, instanceType);
 
  791                                         if (existingInstanceRetry.isPresent()) {
 
  792                                                 return existingInstanceRetry.get();
 
  796                 } 
catch (SQLException ex) {
 
  797                         throw new TskCoreException(String.format(
"Error adding OS account instance for OS account object id = %d, data source object id = %d", osAccountId, dataSourceObjId), ex);
 
  805                 String whereClause = 
" tsk_os_account_instances.os_account_obj_id = " + osAccountId
 
  806                                                    + 
" AND tsk_os_account_instances.data_source_obj_id = " + dataSourceObjId;
 
  808                 if (instances.isEmpty()) {
 
  809                         throw new TskCoreException(String.format(
"Could not get autogen key after row insert or reload instance for OS account instance. OS account object id = %d, data source object id = %d", osAccountId, dataSourceObjId));
 
  812                 OsAccountInstance accountInstance = instances.get(0);
 
  813                 synchronized (osAcctInstancesCacheLock) {
 
  814                         OsAccountInstanceKey key = 
new OsAccountInstanceKey(osAccountId, dataSourceObjId);
 
  816                         for (OsAccountInstance.OsAccountInstanceType type : OsAccountInstance.OsAccountInstanceType.values()) {
 
  817                                 if (accountInstance.getInstanceType().compareTo(type) < 0) {
 
  818                                         osAccountInstanceCache.remove(key);
 
  822                         osAccountInstanceCache.put(key, accountInstance);
 
  824                 return accountInstance;
 
  843         private Optional<OsAccountInstance> cachedAccountInstance(
long osAccountId, 
long dataSourceObjId, OsAccountInstance.OsAccountInstanceType instanceType) {
 
  852                 synchronized (osAcctInstancesCacheLock) {
 
  853                         OsAccountInstanceKey key = 
new OsAccountInstanceKey(osAccountId, dataSourceObjId);
 
  854                         OsAccountInstance instance = osAccountInstanceCache.get(key);
 
  855                         if (instance != null) {
 
  857                                 if (instanceType.compareTo(instance.getInstanceType()) >= 0) {
 
  858                                         return Optional.of(instance);
 
  861                         return Optional.empty();
 
  875                 String queryString = 
"SELECT * FROM tsk_os_accounts accounts " 
  876                                 + 
"WHERE accounts.os_account_obj_id IN " 
  877                                 + 
"(SELECT instances.os_account_obj_id " 
  878                                 + 
"FROM tsk_os_account_instances instances " 
  879                                 + 
"INNER JOIN data_source_info datasources ON datasources.obj_id = instances.data_source_obj_id " 
  880                                 + 
"WHERE datasources.host_id = " + host.getHostId() + 
") " 
  884                 try (CaseDbConnection connection = this.db.getConnection();
 
  885                                 Statement s = connection.createStatement();
 
  886                                 ResultSet rs = connection.executeQuery(s, queryString)) {
 
  888                         List<OsAccount> accounts = 
new ArrayList<>();
 
  890                                 accounts.add(osAccountFromResultSet(rs));
 
  893                 } 
catch (SQLException ex) {
 
  894                         throw new TskCoreException(String.format(
"Error getting OS accounts for host id = %d", host.getHostId()), ex);
 
  910                 String queryString = 
"SELECT * FROM tsk_os_accounts acc " 
  911                                 + 
"WHERE acc.os_account_obj_id IN " 
  912                                 + 
"(SELECT instance.os_account_obj_id " 
  913                                 + 
"FROM tsk_os_account_instances instance " 
  914                                 + 
"WHERE instance.data_source_obj_id = " + dataSourceId + 
") " 
  918                 try (CaseDbConnection connection = this.db.getConnection();
 
  919                                 Statement s = connection.createStatement();
 
  920                                 ResultSet rs = connection.executeQuery(s, queryString)) {
 
  922                         List<OsAccount> accounts = 
new ArrayList<>();
 
  924                                 accounts.add(osAccountFromResultSet(rs));
 
  927                 } 
catch (SQLException ex) {
 
  928                         throw new TskCoreException(String.format(
"Error getting OS accounts for data source id = %d", dataSourceId), ex);
 
  947                 List<OsAccount> destinationAccounts = 
getOsAccounts(destRealm, trans.getConnection());
 
  948                 List<OsAccount> sourceAccounts = 
getOsAccounts(sourceRealm, trans.getConnection());
 
  950                 for (
OsAccount sourceAccount : sourceAccounts) {
 
  957                         if (sourceAccount.getAddr().isPresent() && sourceAccount.getLoginName().isPresent()) {
 
  958                                 List<OsAccount> duplicateDestAccounts = destinationAccounts.stream()
 
  959                                                 .filter(p -> p.getAddr().equals(sourceAccount.getAddr())
 
  960                                                 || (p.getLoginName().equals(sourceAccount.getLoginName()) && (!p.getAddr().isPresent())))
 
  961                                                 .collect(Collectors.toList());
 
  962                                 if (duplicateDestAccounts.size() > 1) {
 
  963                                         OsAccount combinedDestAccount = duplicateDestAccounts.get(0);
 
  964                                         duplicateDestAccounts.remove(combinedDestAccount);
 
  965                                         for (
OsAccount dupeDestAccount : duplicateDestAccounts) {
 
  966                                                 mergeOsAccounts(dupeDestAccount, combinedDestAccount, trans);
 
  975                         Optional<OsAccount> matchingDestAccount = getMatchingAccountForMerge(sourceAccount, destinationAccounts, 
true);
 
  978                         if (matchingDestAccount.isPresent()) {
 
  979                                 mergeOsAccounts(sourceAccount, matchingDestAccount.get(), trans);
 
  981                                 String query = 
"UPDATE tsk_os_accounts SET realm_id = " + destRealm.getRealmId() + 
" WHERE os_account_obj_id = " + sourceAccount.getId();
 
  982                                 try (Statement s = trans.getConnection().createStatement()) {
 
  983                                         s.executeUpdate(query);
 
  984                                 } 
catch (SQLException ex) {
 
  985                                         throw new TskCoreException(
"Error executing SQL update: " + query, ex);
 
  987                                 trans.registerChangedOsAccount(sourceAccount);
 
  999         private Optional<OsAccount> getMatchingAccountForMerge(OsAccount sourceAccount, List<OsAccount> destinationAccounts, 
boolean ignoreCase) {
 
 1001                 OsAccount matchingDestAccount = null;
 
 1004                 if (sourceAccount.getAddr().isPresent()) {
 
 1005                         List<OsAccount> matchingDestAccounts = destinationAccounts.stream()
 
 1006                                         .filter(p -> p.getAddr().equals(sourceAccount.getAddr()))
 
 1007                                         .collect(Collectors.toList());
 
 1008                         if (!matchingDestAccounts.isEmpty()) {
 
 1009                                 matchingDestAccount = matchingDestAccounts.get(0);
 
 1018                 if (matchingDestAccount == null && sourceAccount.getLoginName().isPresent()) {
 
 1019                         List<OsAccount> matchingDestAccounts = destinationAccounts.stream()
 
 1020                                         .filter(p -> p.getLoginName().isPresent())
 
 1021                                         .filter(p -> ( ( ignoreCase ? p.getLoginName().get().equalsIgnoreCase(sourceAccount.getLoginName().get())  
 
 1022                                                                                                 : p.getLoginName().get().equals(sourceAccount.getLoginName().get()) )
 
 1023                                                                         && ((!sourceAccount.getAddr().isPresent()) || (!p.getAddr().isPresent()))))
 
 1024                                         .collect(Collectors.toList());
 
 1025                         if (!matchingDestAccounts.isEmpty()) {
 
 1026                                 matchingDestAccount = matchingDestAccounts.get(0);
 
 1030                 return Optional.ofNullable(matchingDestAccount);
 
 1041         private void mergeOsAccount(OsAccount account, 
boolean ignoreCase, CaseDbTransaction trans) 
throws TskCoreException {
 
 1047                 List<OsAccount> osAccounts = 
getOsAccounts(realm, trans.getConnection());
 
 1048                 osAccounts.removeIf(acc -> Objects.equals(acc.getId(), account.getId()));
 
 1051                 Optional<OsAccount> matchingAccount = getMatchingAccountForMerge(account, osAccounts, ignoreCase);
 
 1054                 if (matchingAccount.isPresent()) {
 
 1055                         mergeOsAccounts(matchingAccount.get(), account, trans);
 
 1073         private void mergeOsAccounts(OsAccount sourceAccount, OsAccount destAccount, CaseDbTransaction trans) 
throws TskCoreException {
 
 1076                 try (Statement s = trans.getConnection().createStatement()) {
 
 1079                         query = makeOsAccountUpdateQuery(
"tsk_os_account_attributes", sourceAccount, destAccount);
 
 1080                         s.executeUpdate(query);
 
 1084                         query = 
"DELETE FROM tsk_os_account_instances " 
 1087                                         + 
"  sourceAccountInstance.id " 
 1089                                         + 
"  tsk_os_account_instances destAccountInstance " 
 1090                                         + 
"INNER JOIN tsk_os_account_instances sourceAccountInstance ON destAccountInstance.data_source_obj_id = sourceAccountInstance.data_source_obj_id " 
 1091                                         + 
"WHERE destAccountInstance.os_account_obj_id = " + destAccount.getId()
 
 1092                                         + 
" AND sourceAccountInstance.os_account_obj_id = " + sourceAccount.getId()
 
 1093                                         + 
" AND sourceAccountInstance.instance_type = destAccountInstance.instance_type" + 
")";
 
 1095                         s.executeUpdate(query);
 
 1097                         query = makeOsAccountUpdateQuery(
"tsk_os_account_instances", sourceAccount, destAccount);
 
 1098                         s.executeUpdate(query);
 
 1099                         synchronized (osAcctInstancesCacheLock) {
 
 1100                                 osAccountInstanceCache.clear();
 
 1103                         query = makeOsAccountUpdateQuery(
"tsk_files", sourceAccount, destAccount);
 
 1104                         s.executeUpdate(query);
 
 1106                         query = makeOsAccountUpdateQuery(
"tsk_data_artifacts", sourceAccount, destAccount);
 
 1107                         s.executeUpdate(query);
 
 1111                         trans.registerMergedOsAccount(sourceAccount.getId(), destAccount.getId());
 
 1114                         String mergedSignature = makeMergedOsAccountSignature();
 
 1115                         query = 
"UPDATE tsk_os_accounts SET merged_into = " + destAccount.getId()
 
 1116                                         + 
", db_status = " + OsAccount.OsAccountDbStatus.MERGED.getId()
 
 1117                                         + 
", signature = '" + mergedSignature + 
"' " 
 1118                                         + 
" WHERE os_account_obj_id = " + sourceAccount.getId();
 
 1120                         s.executeUpdate(query);
 
 1121                         trans.registerDeletedOsAccount(sourceAccount.getId());
 
 1126                         mergeOsAccountObjectsAndUpdateDestAccount(sourceAccount, destAccount, trans);
 
 1127                 } 
catch (SQLException ex) {
 
 1128                         throw new TskCoreException(
"Error executing SQL update: " + query, ex);
 
 1137         private String makeMergedOsAccountSignature() {
 
 1138                 return "MERGED " + UUID.randomUUID().toString();
 
 1150         private String makeOsAccountUpdateQuery(String tableName, OsAccount sourceAccount, OsAccount destAccount) {
 
 1151                 return "UPDATE " + tableName + 
" SET os_account_obj_id = " + destAccount.getId() + 
" WHERE os_account_obj_id = " + sourceAccount.getId();
 
 1165         private OsAccount mergeOsAccountObjectsAndUpdateDestAccount(OsAccount sourceAccount, OsAccount destAccount, CaseDbTransaction trans) 
throws TskCoreException {
 
 1167                 OsAccount mergedDestAccount = destAccount;
 
 1169                 String destLoginName = null;
 
 1170                 String destAddr = null;
 
 1173                 if (!destAccount.getLoginName().isPresent() && sourceAccount.getLoginName().isPresent()) {
 
 1177                 if (!destAccount.getAddr().isPresent() && sourceAccount.getAddr().isPresent()) {
 
 1178                         destAddr = sourceAccount.getAddr().get();
 
 1182                 OsAccountUpdateResult updateStatus = this.updateOsAccountCore(destAccount, destAddr, destLoginName, trans);
 
 1184                 if (updateStatus.getUpdateStatusCode() == OsAccountUpdateStatus.UPDATED && updateStatus.getUpdatedAccount().isPresent()) {
 
 1185                         mergedDestAccount = updateStatus.getUpdatedAccount().get();
 
 1188                 String destFullName = null;
 
 1189                 Long destCreationTime = null;
 
 1190                 if (!destAccount.getFullName().isPresent() && sourceAccount.getFullName().isPresent()) {
 
 1191                         destFullName = sourceAccount.getFullName().get();
 
 1194                 if (!destAccount.getCreationTime().isPresent() && sourceAccount.getCreationTime().isPresent()) {
 
 1195                         destCreationTime = sourceAccount.getCreationTime().get();
 
 1201                 if (updateStatus.getUpdateStatusCode() == OsAccountUpdateStatus.UPDATED && updateStatus.getUpdatedAccount().isPresent()) {
 
 1202                         mergedDestAccount = updateStatus.getUpdatedAccount().get();
 
 1205                 return mergedDestAccount;
 
 1218         private List<OsAccount> 
getOsAccounts(OsAccountRealm realm, CaseDbConnection connection) 
throws TskCoreException {
 
 1219                 String queryString = 
"SELECT * FROM tsk_os_accounts" 
 1221                                 + 
" AND db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId()
 
 1222                                 + 
" ORDER BY os_account_obj_id";
 
 1224                 try (Statement s = connection.createStatement();
 
 1225                                 ResultSet rs = connection.executeQuery(s, queryString)) {
 
 1227                         List<OsAccount> accounts = 
new ArrayList<>();
 
 1229                                 accounts.add(osAccountFromResultSet(rs));
 
 1232                 } 
catch (SQLException ex) {
 
 1233                         throw new TskCoreException(String.format(
"Error getting OS accounts for realm id = %d", realm.getRealmId()), ex);
 
 1245                 String queryString = 
"SELECT * FROM tsk_os_accounts" 
 1249                 try (CaseDbConnection connection = this.db.getConnection();
 
 1250                                 Statement s = connection.createStatement();
 
 1251                                 ResultSet rs = connection.executeQuery(s, queryString)) {
 
 1253                         List<OsAccount> accounts = 
new ArrayList<>();
 
 1255                                 accounts.add(osAccountFromResultSet(rs));
 
 1258                 } 
catch (SQLException ex) {
 
 1259                         throw new TskCoreException(String.format(
"Error getting OS accounts"), ex);
 
 1285                 if (referringHost == null) {
 
 1286                         throw new TskCoreException(
"A referring host is required to get an account.");
 
 1290                 if ((StringUtils.isBlank(sid) || (sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) ) && StringUtils.isBlank(loginName)) {
 
 1291                         throw new TskCoreException(
"Cannot get an OS account with both SID and loginName as null.");
 
 1295                 if (StringUtils.isBlank(sid) 
 
 1296                         && !StringUtils.isBlank(loginName) && !StringUtils.isBlank(realmName) 
 
 1297                                 && WindowsAccountUtils.isWindowsWellKnownAccountName(loginName, realmName)) {
 
 1298                         sid = WindowsAccountUtils.getWindowsWellKnownAccountSid(loginName, realmName);
 
 1303                 if (StringUtils.isNotBlank(sid)) {
 
 1305                         sid = sid.toUpperCase(Locale.ENGLISH);
 
 1307                 if (StringUtils.isNotBlank(loginName)) {
 
 1309                         loginName = loginName.toLowerCase(Locale.ENGLISH);
 
 1311                 if (StringUtils.isNotBlank(realmName)) {
 
 1313                         realmName = realmName.toLowerCase(Locale.ENGLISH);
 
 1318                 if (!realm.isPresent()) {
 
 1319                         return Optional.empty();
 
 1323                 if (!Strings.isNullOrEmpty(sid) && !(sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))) {
 
 1324                         if (!WindowsAccountUtils.isWindowsUserSid(sid)) {
 
 1328                         Optional<OsAccount> account = this.getOsAccountByAddr(sid, realm.get());
 
 1329                         if (account.isPresent()) {
 
 1335                 if (!Strings.isNullOrEmpty(loginName)) {
 
 1336                         String resolvedLoginName = WindowsAccountUtils.toWellknownEnglishLoginName(loginName);
 
 1337                         return this.getOsAccountByLoginName(resolvedLoginName, realm.get());
 
 1339                         return Optional.empty();
 
 1358                 if (referringHost == null) {
 
 1359                         throw new TskCoreException(
"A referring host is required to get an account.");
 
 1363                 if (StringUtils.isBlank(uid) && StringUtils.isBlank(loginName)) {
 
 1364                         throw new TskCoreException(
"Cannot get an OS account with both UID and loginName as null.");
 
 1369                 if (!realm.isPresent()) {
 
 1370                         return Optional.empty();
 
 1374                 if (!Strings.isNullOrEmpty(uid)) {
 
 1375                         Optional<OsAccount> account = this.getOsAccountByAddr(uid, realm.get());
 
 1376                         if (account.isPresent()) {
 
 1382                 if (!Strings.isNullOrEmpty(loginName)) {
 
 1383                         return this.getOsAccountByLoginName(loginName, realm.get());
 
 1385                         return Optional.empty();
 
 1400                 synchronized (account) {  
 
 1403                         try (CaseDbConnection connection = db.getConnection()) {
 
 1406                                         String attributeInsertSQL = 
"INSERT INTO tsk_os_account_attributes(os_account_obj_id, host_id, source_obj_id, attribute_type_id, value_type, value_byte, value_text, value_int32, value_int64, value_double)" 
 1407                                                         + 
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; 
 
 1409                                         PreparedStatement preparedStatement = connection.getPreparedStatement(attributeInsertSQL, Statement.RETURN_GENERATED_KEYS);
 
 1410                                         preparedStatement.clearParameters();
 
 1412                                         preparedStatement.setLong(1, account.getId());
 
 1413                                         if (accountAttribute.getHostId().isPresent()) {
 
 1414                                                 preparedStatement.setLong(2, accountAttribute.getHostId().get());
 
 1416                                                 preparedStatement.setNull(2, java.sql.Types.NULL);
 
 1418                                         if (accountAttribute.getSourceObjectId().isPresent()) {
 
 1419                                                 preparedStatement.setLong(3, accountAttribute.getSourceObjectId().get());
 
 1421                                                 preparedStatement.setNull(3, java.sql.Types.NULL);
 
 1424                                         preparedStatement.setLong(4, accountAttribute.getAttributeType().getTypeID());
 
 1425                                         preparedStatement.setLong(5, accountAttribute.getAttributeType().getValueType().getType());
 
 1428                                                 preparedStatement.setBytes(6, accountAttribute.getValueBytes());
 
 1430                                                 preparedStatement.setBytes(6, null);
 
 1435                                                 preparedStatement.setString(7, accountAttribute.getValueString());
 
 1437                                                 preparedStatement.setString(7, null);
 
 1440                                                 preparedStatement.setInt(8, accountAttribute.getValueInt());
 
 1442                                                 preparedStatement.setNull(8, java.sql.Types.NULL);
 
 1447                                                 preparedStatement.setLong(9, accountAttribute.getValueLong());
 
 1449                                                 preparedStatement.setNull(9, java.sql.Types.NULL);
 
 1453                                                 preparedStatement.setDouble(10, accountAttribute.getValueDouble());
 
 1455                                                 preparedStatement.setNull(10, java.sql.Types.NULL);
 
 1458                                         connection.executeUpdate(preparedStatement);
 
 1460                         } 
catch (SQLException ex) {
 
 1461                                 throw new TskCoreException(String.format(
"Error adding OS Account attribute for account id = %d", account.getId()), ex);
 
 1466                         List<OsAccountAttribute> currentAttribsList = getOsAccountAttributes(account);
 
 1467                         account.setAttributesInternal(currentAttribsList);
 
 1469                 fireChangeEvent(account);
 
 1481         List<OsAccountAttribute> getOsAccountAttributes(
OsAccount account) 
throws TskCoreException {
 
 1483                 String queryString = 
"SELECT attributes.os_account_obj_id as os_account_obj_id, attributes.host_id as host_id, attributes.source_obj_id as source_obj_id, " 
 1484                                 + 
" attributes.attribute_type_id as attribute_type_id,  attributes.value_type as value_type, attributes.value_byte as value_byte, " 
 1485                                 + 
" attributes.value_text as value_text, attributes.value_int32 as value_int32, attributes.value_int64 as value_int64, attributes.value_double as value_double, " 
 1486                                 + 
" hosts.id, hosts.name as host_name, hosts.db_status as host_status " 
 1487                                 + 
" FROM tsk_os_account_attributes as attributes" 
 1488                                 + 
"             LEFT JOIN tsk_hosts as hosts " 
 1489                                 + 
" ON attributes.host_id = hosts.id " 
 1490                                 + 
" WHERE os_account_obj_id = " + account.getId();
 
 1493                 try (CaseDbConnection connection = this.db.getConnection();
 
 1494                                 Statement s = connection.createStatement();
 
 1495                                 ResultSet rs = connection.executeQuery(s, queryString)) {
 
 1497                         List<OsAccountAttribute> attributes = 
new ArrayList<>();
 
 1501                                 long hostId = rs.getLong(
"host_id");
 
 1502                                 if (!rs.wasNull()) {
 
 1503                                         host = 
new Host(hostId, rs.getString(
"host_name"), 
Host.
HostDbStatus.fromID(rs.getInt(
"host_status")));
 
 1506                                 Content sourceContent = null;
 
 1507                                 long sourceObjId = rs.getLong(
"source_obj_id");
 
 1508                                 if (!rs.wasNull()) {
 
 1512                                 OsAccountAttribute attribute = account.new OsAccountAttribute(attributeType, rs.getInt(
"value_int32"), rs.getLong(
"value_int64"),
 
 1513                                                 rs.getDouble(
"value_double"), rs.getString(
"value_text"), rs.getBytes(
"value_byte"),
 
 1514                                                 db, account, host, sourceContent);
 
 1516                                 attributes.add(attribute);
 
 1519                 } 
catch (SQLException ex) {
 
 1520                         throw new TskCoreException(String.format(
"Error getting OS account attributes for account obj id = %d", account.getId()), ex);
 
 1536                 String whereClause = 
" tsk_os_account_instances.os_account_obj_id = " + account.getId();
 
 1551                 String instanceIds = instanceIDs.stream().map(
id -> 
id.toString()).collect(Collectors.joining(
","));
 
 1553                 List<OsAccountInstance> osAcctInstances = 
new ArrayList<>();
 
 1555                 String querySQL = 
"SELECT * FROM tsk_os_account_instances " 
 1556                                 + 
"     WHERE tsk_os_account_instances.id IN (" + instanceIds + 
")";
 
 1559                 try (CaseDbConnection connection = db.getConnection();
 
 1560                                 PreparedStatement preparedStatement = connection.getPreparedStatement(querySQL, Statement.NO_GENERATED_KEYS);
 
 1561                                 ResultSet results = connection.executeQuery(preparedStatement)) {
 
 1563                         osAcctInstances = getOsAccountInstancesFromResultSet(results);
 
 1565                 } 
catch (SQLException ex) {
 
 1566                         throw new TskCoreException(
"Failed to get OsAccountInstances (SQL = " + querySQL + 
")", ex);
 
 1570                 return osAcctInstances;
 
 1587                 List<OsAccountInstance> osAcctInstances = 
new ArrayList<>();
 
 1590                                 = 
"SELECT tsk_os_account_instances.* " 
 1591                                 + 
" FROM tsk_os_account_instances " 
 1592                                 + 
" INNER JOIN ( SELECT os_account_obj_id,  data_source_obj_id, MIN(instance_type) AS min_instance_type " 
 1593                                 + 
"                                     FROM tsk_os_account_instances" 
 1594                                 + 
"                                     GROUP BY os_account_obj_id, data_source_obj_id ) grouped_instances " 
 1595                                 + 
" ON tsk_os_account_instances.os_account_obj_id = grouped_instances.os_account_obj_id " 
 1596                                 + 
" AND tsk_os_account_instances.instance_type = grouped_instances.min_instance_type " 
 1597                                 + 
" WHERE " + whereClause;
 
 1600                 try (CaseDbConnection connection = db.getConnection();
 
 1601                                 PreparedStatement preparedStatement = connection.getPreparedStatement(querySQL, Statement.NO_GENERATED_KEYS);
 
 1602                                 ResultSet results = connection.executeQuery(preparedStatement)) {
 
 1604                         osAcctInstances = getOsAccountInstancesFromResultSet(results);
 
 1606                 } 
catch (SQLException ex) {
 
 1607                         throw new TskCoreException(
"Failed to get OsAccountInstances (SQL = " + querySQL + 
")", ex);
 
 1611                 return osAcctInstances;
 
 1623         private List<OsAccountInstance> getOsAccountInstancesFromResultSet(ResultSet results) 
throws SQLException {
 
 1625                 List<OsAccountInstance> osAcctInstances = 
new ArrayList<>();
 
 1626                 while (results.next()) {
 
 1627                         long instanceId = results.getLong(
"id");
 
 1628                         long osAccountObjID = results.getLong(
"os_account_obj_id");
 
 1629                         long dataSourceObjId = results.getLong(
"data_source_obj_id");
 
 1630                         int instanceType = results.getInt(
"instance_type");
 
 1631                         osAcctInstances.add(
new OsAccountInstance(db, instanceId, osAccountObjID, dataSourceObjId, OsAccountInstance.OsAccountInstanceType.fromID(instanceType)));
 
 1634                 return osAcctInstances;
 
 1662                         return updateStatus;
 
 1664                         if (trans != null) {
 
 1689                 OsAccountUpdateStatus updateStatusCode = OsAccountUpdateStatus.NO_CHANGE;
 
 1692                         CaseDbConnection connection = trans.getConnection();
 
 1694                         if (!StringUtils.isBlank(fullName)) {
 
 1695                                 updateAccountColumn(osAccount.getId(), 
"full_name", fullName, connection);
 
 1696                                 updateStatusCode = OsAccountUpdateStatus.UPDATED;
 
 1699                         if (Objects.nonNull(accountType)) {
 
 1700                                 updateAccountColumn(osAccount.getId(), 
"type", accountType.getId(), connection);
 
 1701                                 updateStatusCode = OsAccountUpdateStatus.UPDATED;
 
 1704                         if (Objects.nonNull(accountStatus)) {
 
 1705                                 updateAccountColumn(osAccount.getId(), 
"status", accountStatus.getId(), connection);
 
 1706                                 updateStatusCode = OsAccountUpdateStatus.UPDATED;
 
 1709                         if (Objects.nonNull(creationTime)) {
 
 1710                                 updateAccountColumn(osAccount.getId(), 
"created_date", creationTime, connection);
 
 1711                                 updateStatusCode = OsAccountUpdateStatus.UPDATED;
 
 1715                         if (updateStatusCode == OsAccountUpdateStatus.NO_CHANGE) {
 
 1716                                 return new OsAccountUpdateResult(updateStatusCode, null);
 
 1723                         trans.registerChangedOsAccount(updatedAccount);
 
 1725                         return new OsAccountUpdateResult(updateStatusCode, updatedAccount);
 
 1727                 } 
catch (SQLException ex) {
 
 1728                         throw new TskCoreException(String.format(
"Error updating account with addr = %s, account id = %d", osAccount.getAddr().orElse(
"Unknown"), osAccount.getId()), ex);
 
 1745         private <T> 
void updateAccountColumn(
long accountObjId, String colName, T colValue, CaseDbConnection connection) 
throws SQLException, TskCoreException {
 
 1747                 String updateSQL = 
"UPDATE tsk_os_accounts " 
 1748                                 + 
" SET " + colName + 
" = ? " 
 1749                                 + 
" WHERE os_account_obj_id = ?";
 
 1753                         PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
 
 1754                         preparedStatement.clearParameters();
 
 1756                         if (Objects.isNull(colValue)) {
 
 1757                                 preparedStatement.setNull(1, Types.NULL); 
 
 1759                                 if (colValue instanceof String) {
 
 1760                                         preparedStatement.setString(1, (String) colValue);
 
 1761                                 } 
else if (colValue instanceof Long) {
 
 1762                                         preparedStatement.setLong(1, (Long) colValue);
 
 1763                                 } 
else if (colValue instanceof Integer) {
 
 1764                                         preparedStatement.setInt(1, (Integer) colValue);
 
 1766                                         throw new TskCoreException(String.format(
"Unhandled column data type received while updating the account (%d) ", accountObjId));
 
 1770                         preparedStatement.setLong(2, accountObjId);
 
 1772                         connection.executeUpdate(preparedStatement);
 
 1788         private void updateAccountSignature(
long accountObjId, String signature, CaseDbConnection connection) 
throws SQLException {
 
 1790                 String updateSQL = 
"UPDATE tsk_os_accounts SET " 
 1792                                 + 
"       CASE WHEN db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId() + 
" THEN ? ELSE signature END  " 
 1793                                 + 
" WHERE os_account_obj_id = ?";       
 
 1795                 PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS);
 
 1796                 preparedStatement.clearParameters();
 
 1798                 preparedStatement.setString(1, signature);
 
 1799                 preparedStatement.setLong(2, accountObjId);
 
 1801                 connection.executeUpdate(preparedStatement);
 
 1831                         if (StringUtils.isNotBlank(accountSid)) {
 
 1833                                 accountSid = accountSid.toUpperCase(Locale.ENGLISH);
 
 1835                         if (StringUtils.isNotBlank(loginName)) {
 
 1837                                 loginName = loginName.toLowerCase(Locale.ENGLISH);
 
 1839                         if (StringUtils.isNotBlank(realmName)) {
 
 1841                                 realmName = realmName.toLowerCase(Locale.ENGLISH);
 
 1848                         return updateStatus;
 
 1850                         if (trans != null) {
 
 1879                 if ((!StringUtils.isBlank(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) || !StringUtils.isBlank(realmName)) {
 
 1881                         String resolvedRealmName = WindowsAccountUtils.toWellknownEnglishRealmName(realmName);
 
 1887                         Optional<OsAccountRealm> realmOptional = realmUpdateResult.
getUpdatedRealm();
 
 1889                         if (realmOptional.isPresent()) {
 
 1896                                         Optional<OsAccountRealm> anotherRealmWithSameName = db.
getOsAccountRealmManager().getAnotherRealmByName(realmOptional.get(), realmName, referringHost, trans.getConnection());
 
 1897                                         if (anotherRealmWithSameName.isPresent() && anotherRealmWithSameName.get().getRealmAddr().isPresent()) {
 
 1899                                                 anotherRealmWithSameName = Optional.empty();
 
 1903                                         Optional<OsAccountRealm> anotherRealmWithSameAddr = db.
getOsAccountRealmManager().getAnotherRealmByAddr(realmOptional.get(), realmName, referringHost, trans.getConnection());
 
 1904                                         if (anotherRealmWithSameAddr.isPresent() && !anotherRealmWithSameAddr.get().getRealmNames().isEmpty()) {
 
 1906                                                 anotherRealmWithSameName = Optional.empty();
 
 1909                                         if (anotherRealmWithSameName.isPresent()) {
 
 1912                                         if (anotherRealmWithSameAddr.isPresent()) {
 
 1920                 String resolvedLoginName = WindowsAccountUtils.toWellknownEnglishLoginName(loginName);
 
 1921                 OsAccountUpdateResult updateStatus = this.updateOsAccountCore(osAccount, accountSid, resolvedLoginName, trans);
 
 1923                 Optional<OsAccount> updatedAccount = updateStatus.getUpdatedAccount();
 
 1924                 if (updatedAccount.isPresent() && updateStatus.updateStatus != OsAccountUpdateStatus.NO_CHANGE) {
 
 1926                         mergeOsAccount(updatedAccount.get(), 
true, trans);
 
 1929                 return updateStatus;
 
 1956                         return updateStatus;
 
 1958                         if (trans != null) {
 
 1985                 OsAccountUpdateResult updateStatus = this.updateOsAccountCore(osAccount, uid, loginName, trans);
 
 1987                 Optional<OsAccount> updatedAccount = updateStatus.getUpdatedAccount();
 
 1988                 if (updatedAccount.isPresent()) {
 
 1990                         mergeOsAccount(updatedAccount.get(), 
false, trans);
 
 1993                 return updateStatus;
 
 2018         private OsAccountUpdateResult updateOsAccountCore(OsAccount osAccount, String address, String loginName, CaseDbTransaction trans) 
throws TskCoreException {
 
 2020                 OsAccountUpdateStatus updateStatusCode = OsAccountUpdateStatus.NO_CHANGE;
 
 2021                 OsAccount updatedAccount;
 
 2024                         CaseDbConnection connection = trans.getConnection();
 
 2027                         if (!StringUtils.isBlank(address) && !address.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && !StringUtils.isBlank(osAccount.getAddr().orElse(null)) && !address.equalsIgnoreCase(osAccount.getAddr().orElse(
""))) {
 
 2028                                 throw new TskCoreException(String.format(
"Account (%d) already has an address (%s), address cannot be updated.", osAccount.getId(), osAccount.getAddr().orElse(
"NULL")));
 
 2031                         if (StringUtils.isBlank(osAccount.getAddr().orElse(null)) && !StringUtils.isBlank(address) && !address.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) {
 
 2032                                 updateAccountColumn(osAccount.getId(), 
"addr", address, connection);
 
 2033                                 updateStatusCode = OsAccountUpdateStatus.UPDATED;
 
 2036                         if (StringUtils.isBlank(osAccount.getLoginName().orElse(null)) && !StringUtils.isBlank(loginName)) {
 
 2037                                 updateAccountColumn(osAccount.getId(), 
"login_name", loginName, connection);
 
 2038                                 updateStatusCode = OsAccountUpdateStatus.UPDATED;
 
 2042                         if (updateStatusCode == OsAccountUpdateStatus.NO_CHANGE) {
 
 2043                                 return new OsAccountUpdateResult(updateStatusCode, osAccount);
 
 2048                         String newAddress = currAccount.
getAddr().orElse(null);
 
 2049                         String newLoginName = currAccount.getLoginName().orElse(null);
 
 2051                         String newSignature = getOsAccountSignature(newAddress, newLoginName);
 
 2054                                 updateAccountSignature(osAccount.getId(), newSignature, connection);
 
 2055                         } 
catch (SQLException ex) {
 
 2064                                 if (osAccount.getAddr().isEmpty() && !StringUtils.isBlank(address)) {
 
 2066                                         Optional<OsAccount> matchingAddrAcct = getOsAccountByAddr(address, realm.getScopeHost().get(), connection);
 
 2067                                         if (matchingAddrAcct.isEmpty() 
 
 2068                                                         || matchingAddrAcct.get().getId() == osAccount.getId()
 
 2069                                                         || matchingAddrAcct.get().getLoginName().isPresent()) {
 
 2075                                         mergeOsAccounts(matchingAddrAcct.get(), osAccount, trans);
 
 2083                         trans.registerChangedOsAccount(updatedAccount);
 
 2085                         return new OsAccountUpdateResult(updateStatusCode, updatedAccount);
 
 2087                 } 
catch (SQLException ex) {                     
 
 2088                         throw new TskCoreException(String.format(
"Error updating account with unique id = %s, account id = %d", osAccount.getAddr().orElse(
"Unknown"), osAccount.getId()), ex);
 
 2102                 List<Host> hostList = 
new ArrayList<>();
 
 2104                 String query = 
"SELECT tsk_hosts.id AS hostId, name, db_status FROM tsk_hosts " 
 2105                                 + 
" JOIN data_source_info ON tsk_hosts.id = data_source_info.host_id" 
 2106                                 + 
"     JOIN tsk_os_account_instances ON data_source_info.obj_id = tsk_os_account_instances.data_source_obj_id" 
 2107                                 + 
" WHERE os_account_obj_id = " + account.getId();
 
 2110                 try (CaseDbConnection connection = db.getConnection();
 
 2111                                 Statement s = connection.createStatement();
 
 2112                                 ResultSet rs = connection.executeQuery(s, query)) {
 
 2115                                 hostList.add(
new Host(rs.getLong(
"hostId"), rs.getString(
"name"), 
Host.
HostDbStatus.fromID(rs.getInt(
"db_status"))));
 
 2118                 } 
catch (SQLException ex) {
 
 2119                         throw new TskCoreException(String.format(
"Failed to get host list for os account %d", account.getId()), ex);
 
 2137         private OsAccount osAccountFromResultSet(ResultSet rs) 
throws SQLException {
 
 2140                 int typeId = rs.getInt(
"type");
 
 2141                 if (!rs.wasNull()) {
 
 2145                 Long creationTime = rs.getLong(
"created_date"); 
 
 2147                         creationTime = null;
 
 2150                 return new OsAccount(db, rs.getLong(
"os_account_obj_id"), rs.getLong(
"realm_id"), rs.getString(
"login_name"), rs.getString(
"addr"),
 
 2151                                 rs.getString(
"signature"), rs.getString(
"full_name"), creationTime, accountType, OsAccount.OsAccountStatus.
fromID(rs.getInt(
"status")),
 
 2152                                 OsAccount.OsAccountDbStatus.
fromID(rs.getInt(
"db_status")));
 
 2162         private void fireChangeEvent(OsAccount account) {
 
 2163                 db.fireTSKEvent(
new OsAccountsUpdatedTskEvent(Collections.singletonList(account)));
 
 2180         static String getOsAccountSignature(String uniqueId, String loginName) 
throws TskCoreException {
 
 2183                 if (Strings.isNullOrEmpty(uniqueId) == 
false) {
 
 2184                         signature = uniqueId;
 
 2185                 } 
else if (Strings.isNullOrEmpty(loginName) == 
false) {
 
 2186                         signature = loginName;
 
 2188                         throw new TskCoreException(
"OS Account must have either a uniqueID or a login name.");
 
 2199                 private static final long serialVersionUID = 1L;
 
 2205                         super(
"No error message available.");
 
 2248                         this.updateStatus = updateStatus;
 
 2249                         this.updatedAccount = updatedAccount;
 
 2253                         return updateStatus;
 
 2257                         return Optional.ofNullable(updatedAccount);
 
 2265         private class OsAccountInstanceKey 
implements Comparable<OsAccountInstanceKey>{
 
 2267                 private final long osAccountId;
 
 2268                 private final long dataSourceId;
 
 2270                 OsAccountInstanceKey(
long osAccountId, 
long dataSourceId) {
 
 2271                         this.osAccountId = osAccountId;
 
 2272                         this.dataSourceId = dataSourceId;
 
 2276                 public boolean equals(Object other) {
 
 2277                         if (
this == other) {
 
 2280                         if (other == null) {
 
 2283                         if (getClass() != other.getClass()) {
 
 2287                         final OsAccountInstanceKey otherKey = (OsAccountInstanceKey) other;
 
 2289                         if (osAccountId != otherKey.osAccountId) {
 
 2293                         return dataSourceId == otherKey.dataSourceId;
 
 2297                 public int hashCode() {
 
 2299                         hash = 53 * hash + (int) (this.osAccountId ^ (this.osAccountId >>> 32));
 
 2300                         hash = 53 * hash + (int) (this.dataSourceId ^ (this.dataSourceId >>> 32));
 
 2305                 public int compareTo(OsAccountInstanceKey other) {
 
 2306                         if(this.equals(other)) {
 
 2310                         if (dataSourceId != other.dataSourceId) {
 
 2311                                 return Long.compare(dataSourceId, other.dataSourceId);
 
 2314                         return Long.compare(osAccountId, other.osAccountId);
 
NotUserSIDException(String msg, Exception ex)
List< OsAccount > getOsAccounts()
Optional< String > getAddr()
OsAccountRealm newWindowsRealm(String accountSid, String realmName, Host referringHost, OsAccountRealm.RealmScope realmScope)
CaseDbTransaction beginTransaction()
List< Host > getHosts(OsAccount account)
OsAccount getOsAccountByObjectId(long osAccountObjId)
Optional< OsAccountRealm > getUpdatedRealm()
static OsAccountType fromID(int typeId)
OsAccountRealm newLocalLinuxRealm(Host referringHost)
Blackboard getBlackboard()
Optional< OsAccount > getWindowsOsAccount(String sid, String loginName, String realmName, Host referringHost)
OsAccountUpdateStatus getUpdateStatusCode()
OsAccountUpdateResult updateStandardOsAccountAttributes(OsAccount osAccount, String fullName, OsAccountType accountType, OsAccountStatus accountStatus, Long creationTime)
Content getContentById(long id)
OsAccount newWindowsOsAccount(String sid, String loginName, OsAccountRealm realm)
NotUserSIDException(String msg)
List< OsAccount > getOsAccounts(Host host)
Optional< OsAccount > getLocalLinuxOsAccount(String uid, String loginName, Host referringHost)
Optional< OsAccountRealm > getLocalLinuxRealm(Host referringHost)
OsAccountRealmManager getOsAccountRealmManager()
OsAccount newLocalLinuxOsAccount(String uid, String loginName, Host referringHost)
void releaseSingleUserCaseReadLock()
Optional< OsAccountRealm > getWindowsRealm(String accountSid, String realmName, Host referringHost)
List< OsAccount > getOsAccountsByDataSourceObjId(long dataSourceId)
List< OsAccountInstance > getOsAccountInstances(List< Long > instanceIDs)
void acquireSingleUserCaseWriteLock()
OsAccountInstance newOsAccountInstance(OsAccount osAccount, DataSource dataSource, OsAccountInstance.OsAccountInstanceType instanceType)
void releaseSingleUserCaseWriteLock()
List< OsAccountInstance > getOsAccountInstances(OsAccount account)
UPDATED
no change was made to account. 
Optional< String > getLoginName()
Optional< Host > getScopeHost()
void acquireSingleUserCaseReadLock()
UPDATED
no change was made to account. 
void addExtendedOsAccountAttributes(OsAccount account, List< OsAccountAttribute > accountAttributes)
OsAccountRealm getRealmByRealmId(long id)
BlackboardAttribute.Type getAttributeType(String attrTypeName)
OsRealmUpdateStatus getUpdateStatus()
Optional< OsAccount > getUpdatedAccount()
OsAccountUpdateResult updateCoreLocalLinuxOsAccountAttributes(OsAccount osAccount, String uid, String loginName)
OsAccountUpdateResult updateCoreWindowsOsAccountAttributes(OsAccount osAccount, String accountSid, String loginName, String realmName, Host referringHost)
OsAccount newWindowsOsAccount(String sid, String loginName, String realmName, Host referringHost, OsAccountRealm.RealmScope realmScope)
List< String > getRealmNames()