Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • irt/sleuthkit
1 result
Show changes
Showing
with 6170 additions and 0 deletions
/*
* Sleuth Kit Data Model
*
* Copyright 2018 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.datamodel;
import java.util.ResourceBundle;
/**
* Provides easy access to the ResourceBundle in this package.
*/
final class BundleProvider {
private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle");
static ResourceBundle getBundle() {
return BUNDLE;
}
private BundleProvider() {
}
}
#Thu Sep 30 10:23:46 UTC 2021
AbstractFile.readLocal.exception.msg1.text=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30ed\u30fc\u30ab\u30eb\u30d1\u30b9\u304c\u30bb\u30c3\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002
AbstractFile.readLocal.exception.msg2.text=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u4e0b\u8a18\u306e\u30ed\u30fc\u30ab\u30eb\u30d1\u30b9\u306b\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\uff1a{0}
AbstractFile.readLocal.exception.msg3.text=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u4e0b\u8a18\u306e\u30ed\u30fc\u30ab\u30eb\u30d1\u30b9\u3067\u306f\u8aad\u307f\u53d6\u308a\u3067\u304d\u307e\u305b\u3093\uff1a{0}
AbstractFile.readLocal.exception.msg4.text=\u30d5\u30a1\u30a4\u30eb{0}\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
AbstractFile.readLocal.exception.msg5.text=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb{0}\u3092\u8aad\u307f\u53d6\u308c\u307e\u305b\u3093
BaseTypes.communication.name=\u30b3\u30df\u30e5\u30cb\u30b1\u30fc\u30b7\u30e7\u30f3
BaseTypes.fileSystem.name=\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0
BaseTypes.geolocation.name=\u30b8\u30aa\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3
BaseTypes.miscTypes.name=\u305d\u306e\u4ed6
BaseTypes.webActivity.name=Web\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3
BlackboardArtifact.shortDescriptionDate.text=\u3067{0}
BlackboardArtifact.tagFile.text=\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb
BlackboardArtifact.tsk.recentObject.text=\u6700\u8fd1\u958b\u3044\u305f\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8
BlackboardArtifact.tskAccount.text=\u30a2\u30ab\u30a6\u30f3\u30c8
BlackboardArtifact.tskAssociatedObject.text=\u95a2\u9023\u30aa\u30d6\u30b8\u30a7\u30af\u30c8
BlackboardArtifact.tskBackupEvent.text=\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u30a4\u30d9\u30f3\u30c8
BlackboardArtifact.tskBluetoothAdapter.text=Bluetooth\u30a2\u30c0\u30d7\u30bf\u30fc
BlackboardArtifact.tskBluetoothPairing.text=Bluetooth\u30da\u30a2\u30ea\u30f3\u30b0
BlackboardArtifact.tskCalendarEntry.text=\u30ab\u30ec\u30f3\u30c0\u30fc\u30a8\u30f3\u30c8\u30ea\u30fc
BlackboardArtifact.tskCalllog.text=\u30b3\u30fc\u30eb\u30ed\u30b0
BlackboardArtifact.tskClipboardContent.text=\u30af\u30ea\u30c3\u30d7\u30dc\u30fc\u30c9\u306e\u5185\u5bb9
BlackboardArtifact.tskContact.text=\u30b3\u30f3\u30bf\u30af\u30c8
BlackboardArtifact.tskDataSourceUsage.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u4f7f\u7528\u6cd5
BlackboardArtifact.tskDeletedProg.text=\u524a\u9664\u3055\u308c\u305f\u30d7\u30ed\u30b0\u30e9\u30e0
BlackboardArtifact.tskDeviceAttached.text=USB\u30c7\u30d0\u30a4\u30b9\u304c\u63a5\u7d9a\u3055\u308c\u3066\u3044\u307e\u3059
BlackboardArtifact.tskDeviceInfo.text=\u30c7\u30d0\u30a4\u30b9\u60c5\u5831
BlackboardArtifact.tskDhcpInfo.text=DHCP\u60c5\u5831
BlackboardArtifact.tskDownloadSource.text=\u30bd\u30fc\u30b9\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9
BlackboardArtifact.tskEmailMsg.text=E\u30e1\u30fc\u30eb\u30e1\u30c3\u30bb\u30fc\u30b8
BlackboardArtifact.tskEncryptionDetected.text=\u6697\u53f7\u5316\u691c\u51fa\u6e08
BlackboardArtifact.tskEncryptionSuspected.text=\u6697\u53f7\u5316\u306e\u53ef\u80fd\u6027
BlackboardArtifact.tskExtMismatchDetected.text=\u62e1\u5f35\u5b50\u4e0d\u4e00\u81f4\u691c\u51fa\u6e08
BlackboardArtifact.tskExtractedText.text=\u62bd\u51fa\u3055\u308c\u305f\u30c6\u30ad\u30b9\u30c8
BlackboardArtifact.tskFaceDetected.text=\u9854\u8a8d\u8b58
BlackboardArtifact.tskGPSArea.text=GPS\u30a8\u30ea\u30a2
BlackboardArtifact.tskGenInfo.text=\u4e00\u822c\u60c5\u5831
BlackboardArtifact.tskGpsBookmark.text=GPS\u30d6\u30c3\u30af\u30de\u30fc\u30af
BlackboardArtifact.tskGpsLastKnownLocation.text=\u6700\u5f8c\u306b\u53d6\u5f97\u3057\u305fGPS\u4f4d\u7f6e\u60c5\u5831
BlackboardArtifact.tskGpsRoute.text=GPS\u30eb\u30fc\u30c8
BlackboardArtifact.tskGpsSearch.text=GPS\u691c\u7d22
BlackboardArtifact.tskGpsTrackpoint.text=GPS\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8
BlackboardArtifact.tskHashsetHit.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30d2\u30c3\u30c8
BlackboardArtifact.tskInstalledProg.text=\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08\u307f\u30d7\u30ed\u30b0\u30e9\u30e0
BlackboardArtifact.tskInterestingArtifactHit.text=\u7591\u308f\u3057\u3044\u7d50\u679c
BlackboardArtifact.tskInterestingFileHit.text=\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb
BlackboardArtifact.tskKeywordHits.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30d2\u30c3\u30c8
BlackboardArtifact.tskMessage.text=\u30e1\u30c3\u30bb\u30fc\u30b8
BlackboardArtifact.tskMetadata.text=\u30e1\u30bf\u30c7\u30fc\u30bf
BlackboardArtifact.tskMetadataExif.text=EXIF\u30e1\u30bf\u30c7\u30fc\u30bf
BlackboardArtifact.tskObjectDetected.text=\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f
BlackboardArtifact.tskOsAccount.text=\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0\u30e6\u30fc\u30b6\u30a2\u30ab\u30a6\u30f3\u30c8
BlackboardArtifact.tskOsInfo.text=\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0\u60c5\u5831
BlackboardArtifact.tskPreviouslyNotable.text=\u4ee5\u524d\u306b\u6ce8\u76ee\u306b\u5024\u3059\u308b
BlackboardArtifact.tskPreviouslySeen.text=\u4ee5\u524d\u306b\u8a8d\u8b58
BlackboardArtifact.tskPreviouslyUnseen.text=\u4ee5\u524d\u306b\u306f\u672a\u8a8d\u8b58
BlackboardArtifact.tskProgNotifications.text=\u30d7\u30ed\u30b0\u30e9\u30e0\u901a\u77e5
BlackboardArtifact.tskProgRun.text=\u5b9f\u884c\u30d7\u30ed\u30b0\u30e9\u30e0
BlackboardArtifact.tskRemoteDrive.text=\u30ea\u30e2\u30fc\u30c8\u30c9\u30e9\u30a4\u30d6
BlackboardArtifact.tskScreenShots.text=\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8
BlackboardArtifact.tskServiceAccount.text=Web\u30a2\u30ab\u30a6\u30f3\u30c8
BlackboardArtifact.tskSimAttached.text=SIM\u63a5\u7d9a
BlackboardArtifact.tskSpeedDialEntry.text=\u30b9\u30d4\u30fc\u30c9\u30c0\u30a4\u30eb\u30a8\u30f3\u30c8\u30ea\u30fc
BlackboardArtifact.tskTLEvent.text=TL\u30a4\u30d9\u30f3\u30c8
BlackboardArtifact.tskTagArtifact.text=\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u7d50\u679c
BlackboardArtifact.tskToolOutput.text=\u30ed\u30fc\u30c4\u30fc\u30eb\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8
BlackboardArtifact.tskTrack.text=GPS\u30c8\u30e9\u30c3\u30af
BlackboardArtifact.tskUserContentSuspected.text=\u7591\u308f\u3057\u3044\u30e6\u30fc\u30b6\u30fc\u30b3\u30f3\u30c6\u30f3\u30c4
BlackboardArtifact.tskUserDeviceEvent.text=\u30e6\u30fc\u30b6\u30fc\u30c7\u30d0\u30a4\u30b9\u30a4\u30d9\u30f3\u30c8
BlackboardArtifact.tskVerificationFailed.text=\u691c\u8a3c\u306e\u5931\u6557
BlackboardArtifact.tskWIFINetwork.text=\u30ef\u30a4\u30e4\u30ec\u30b9\u30cd\u30c3\u30c8\u30ef\u30fc\u30af
BlackboardArtifact.tskWIFINetworkAdapter.text=\u30ef\u30a4\u30e4\u30ec\u30b9\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u30a2\u30c0\u30d7\u30bf
BlackboardArtifact.tskWebAccountType.text=Web\u30a2\u30ab\u30a6\u30f3\u30c8\u30bf\u30a4\u30d7
BlackboardArtifact.tskWebBookmark.text=\u30a6\u30a7\u30d6\u30b5\u30a4\u30c8\u30d6\u30c3\u30af\u30de\u30fc\u30af
BlackboardArtifact.tskWebCache.text=Web\u30ad\u30e3\u30c3\u30b7\u30e5
BlackboardArtifact.tskWebCategorization.text=Web\u30ab\u30c6\u30b4\u30ea
BlackboardArtifact.tskWebCookie.text=\u30a6\u30a7\u30d6cookie
BlackboardArtifact.tskWebDownload.text=\u30a6\u30a7\u30d6\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9
BlackboardArtifact.tskWebFormAddresses.text=Web\u30d5\u30a9\u30fc\u30e0\u306e\u30a2\u30c9\u30ec\u30b9
BlackboardArtifact.tskWebFormAutofill.text=Web\u30d5\u30a9\u30fc\u30e0\u306e\u81ea\u52d5\u5165\u529b
BlackboardArtifact.tskWebHistory.text=\u30a6\u30a7\u30d6\u5c65\u6b74
BlackboardArtifact.tskWebSearchQuery.text=\u30a6\u30a7\u30d6\u691c\u7d22
BlackboardArtifact.tskYaraHit.text=YARA\u30d2\u30c3\u30c8
BlackboardAttribute.tskAccountType.text=\u30a2\u30ab\u30f3\u30c8\u30bf\u30a4\u30d7
BlackboardAttribute.tskActivityType.text=\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3\u30bf\u30a4\u30d7
BlackboardAttribute.tskAssociatedArtifact.text=\u95a2\u9023\u3059\u308b\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8
BlackboardAttribute.tskBankName.text=\u30d0\u30f3\u30af\u540d
BlackboardAttribute.tskBrandName.text=\u30d6\u30e9\u30f3\u30c9\u540d
BlackboardAttribute.tskBssid.text=BSSID
BlackboardAttribute.tskCalendarEntryType.text=\u30ab\u30ec\u30f3\u30c0\u30fc\u30a8\u30f3\u30c8\u30ea\u30fc\u30bf\u30a4\u30d7
BlackboardAttribute.tskCardDiscretionary.text=\u30ab\u30fc\u30c9\u88c1\u91cf\u30c7\u30fc\u30bf
BlackboardAttribute.tskCardExpiration.text=\u30ab\u30fc\u30c9\u6709\u52b9\u671f\u9650\uff08YYMM\uff09
BlackboardAttribute.tskCardLRC.text=\u30ab\u30fc\u30c9\u7e26\u65b9\u5411\u5197\u9577\u6027\u30c1\u30a7\u30c3\u30af
BlackboardAttribute.tskCardNumber.text=\u30ab\u30fc\u30c9\u756a\u53f7
BlackboardAttribute.tskCardScheme.text=\u30ab\u30fc\u30c9\u30b9\u30ad\u30fc\u30e0
BlackboardAttribute.tskCardServiceCode.text=\u30ab\u30fc\u30c9\u30b5\u30fc\u30d3\u30b9\u30b3\u30fc\u30c9
BlackboardAttribute.tskCardType.text=\u30ab\u30fc\u30c9\u306e\u6709\u52b9\u671f\u9650\uff08YYMM\uff09
BlackboardAttribute.tskCategory.text=\u30ab\u30c6\u30b4\u30ea\u30fc
BlackboardAttribute.tskCity.text=\u5e02
BlackboardAttribute.tskComment.text=\u30b3\u30e1\u30f3\u30c8
BlackboardAttribute.tskCorrelationType.text=\u76f8\u95a2\u30bf\u30a4\u30d7
BlackboardAttribute.tskCorrelationValue.text=\u76f8\u95a2\u5024
BlackboardAttribute.tskCount.text=\u30ab\u30a6\u30f3\u30c8
BlackboardAttribute.tskCountry.text=\u56fd
BlackboardAttribute.tskDateTimeAccessed.text=\u30a2\u30af\u30bb\u30b9\u65e5\u4ed8
BlackboardAttribute.tskDateTimeCreated.text=\u4f5c\u6210\u65e5
BlackboardAttribute.tskDateTimeEnd.text=\u7d42\u4e86\u65e5\u4ed8\uff0f\u6642\u523b
BlackboardAttribute.tskDateTimeModified.text=\u4fee\u6b63\u65e5
BlackboardAttribute.tskDateTimeRcvd.text=\u53d7\u4fe1\u65e5
BlackboardAttribute.tskDateTimeSent.text=\u9001\u4fe1\u65e5
BlackboardAttribute.tskDateTimeStart.text=\u958b\u59cb\u65e5\u4ed8\uff0f\u6642\u523b
BlackboardAttribute.tskDatetime.text=\u65e5\u4ed8\uff0f\u6642\u523b
BlackboardAttribute.tskDescription.text=\u8aac\u660e
BlackboardAttribute.tskDeviceId.text=\u6a5f\u5668ID
BlackboardAttribute.tskDeviceMake.text=\u6a5f\u5668\u30e1\u30fc\u30ab\u30fc
BlackboardAttribute.tskDeviceModel.text=\u6a5f\u5668\u30e2\u30c7\u30eb
BlackboardAttribute.tskDeviceName.text=\u6a5f\u5668\u540d
BlackboardAttribute.tskDirection.text=\u65b9\u5411
BlackboardAttribute.tskDomain.text=\u30c9\u30e1\u30a4\u30f3
BlackboardAttribute.tskEmail.text=E\u30e1\u30fc\u30eb
BlackboardAttribute.tskEmailBcc.text=E-Mail BCC
BlackboardAttribute.tskEmailCc.text=E-Mail CC
BlackboardAttribute.tskEmailContentHtml.text=\u30e1\u30c3\u30bb\u30fc\u30b8\uff08HTML\uff09
BlackboardAttribute.tskEmailContentPlain.text=\u30e1\u30c3\u30bb\u30fc\u30b8\uff08\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8\uff09
BlackboardAttribute.tskEmailContentRtf.text=\u30e1\u30c3\u30bb\u30fc\u30b8\uff08RTF\uff09
BlackboardAttribute.tskEmailFrom.text=\u9001\u4fe1\u5143E\u30e1\u30fc\u30eb
BlackboardAttribute.tskEmailHome.text=E\u30e1\u30fc\u30eb\uff08\u81ea\u5b85\uff09
BlackboardAttribute.tskEmailOffice.text=E\u30e1\u30fc\u30eb\uff08\u30aa\u30d5\u30a3\u30b9\uff09
BlackboardAttribute.tskEmailReplyTo.text=\u8fd4\u4fe1\u30a2\u30c9\u30ec\u30b9
BlackboardAttribute.tskEmailTo.text=E\u30e1\u30fc\u30eb\u5b9b\u5148
BlackboardAttribute.tskEncryptionDetected.text=\u6697\u53f7\u5316\u691c\u51fa\u6e08
BlackboardAttribute.tskEntropy.text=\u30a8\u30f3\u30c8\u30ed\u30d4\u30fc
BlackboardAttribute.tskFileTypeExt.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\uff08\u62e1\u5f35\u5b50\uff09
BlackboardAttribute.tskFileTypeSig.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\uff08\u30b7\u30b0\u30cd\u30c1\u30e3\uff09
BlackboardAttribute.tskFlag.text=\u30d5\u30e9\u30b0
BlackboardAttribute.tskGeoAltitude.text=\u6a19\u9ad8
BlackboardAttribute.tskGeoBearing.text=\u65b9\u5411
BlackboardAttribute.tskGeoHPrecision.text=\u6c34\u5e73\u7cbe\u5ea6
BlackboardAttribute.tskGeoLatitude.text=\u7def\u5ea6
BlackboardAttribute.tskGeoLatitudeEnd.text=\u7d42\u4e86\u7def\u5ea6
BlackboardAttribute.tskGeoLatitudeStart.text=\u30b9\u30bf\u30fc\u30c8\u7def\u5ea6
BlackboardAttribute.tskGeoLongitude.text=\u7d4c\u5ea6
BlackboardAttribute.tskGeoLongitudeEnd.text=\u7d42\u4e86\u7d4c\u5ea6
BlackboardAttribute.tskGeoLongitudeStart.text=\u30b9\u30bf\u30fc\u30c8\u7d4c\u5ea6
BlackboardAttribute.tskGeoMapDatum.text=\u6e2c\u5730\u7cfb
BlackboardAttribute.tskGeoVPrecision.text=\u5782\u76f4\u7cbe\u5ea6
BlackboardAttribute.tskGeoVelocity.text=\u901f\u5ea6
BlackboardAttribute.tskHashMd5.text=MD5\u30cf\u30c3\u30b7\u30e5
BlackboardAttribute.tskHashSha1.text=SHA1\u30cf\u30c3\u30b7\u30e5
BlackboardAttribute.tskHashSha225.text=SHA2-256\u30cf\u30c3\u30b7\u30e5
BlackboardAttribute.tskHashSha2512.text=SHA2-512\u30cf\u30c3\u30b7\u30e5
BlackboardAttribute.tskHashsetName.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u540d
BlackboardAttribute.tskHeaders.text=\u30d8\u30c3\u30c0\u30fc
BlackboardAttribute.tskHomeDir.text=\u30db\u30fc\u30e0\u30c7\u30a3\u30ec\u30af\u30c8\u30ea
BlackboardAttribute.tskHost.text=\u30db\u30b9\u30c8
BlackboardAttribute.tskIccid.text=ICCID
BlackboardAttribute.tskId.text=ID
BlackboardAttribute.tskImei.text=IMEI
BlackboardAttribute.tskImsi.text=IMSI
BlackboardAttribute.tskInterestingFile.text=\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb
BlackboardAttribute.tskIpAddress.text=IP\u30a2\u30c9\u30ec\u30b9
BlackboardAttribute.tskIsAdmin.text=\u7ba1\u7406\u8005\u3067\u3059
BlackboardAttribute.tskIsDeleted.text=\u306f\u524a\u9664\u3055\u308c\u307e\u3057\u305f
BlackboardAttribute.tskKeyword.text=\u30ad\u30fc\u30ef\u30fc\u30c9
BlackboardAttribute.tskKeywordPreview.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30d7\u30ec\u30d3\u30e5\u30fc
BlackboardAttribute.tskKeywordRegexp.text=\u6b63\u898f\u8868\u73fe\u30ad\u30fc\u30ef\u30fc\u30c9
BlackboardAttribute.tskKeywordSearchDocumentID.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8ID
BlackboardAttribute.tskKeywordSearchType.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30bf\u30a4\u30d7
BlackboardAttribute.tskKeywordSet.text=\u30ad\u30fc\u30ef\u30fc\u30c9\u30bb\u30c3\u30c8
BlackboardAttribute.tskLocalPath.text=\u30ed\u30fc\u30ab\u30eb\u30d1\u30b9
BlackboardAttribute.tskLocation.text=\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3
BlackboardAttribute.tskMacAddress.text=Mac\u30a2\u30c9\u30ec\u30b9
BlackboardAttribute.tskMalwareDetected.text=\u30de\u30eb\u30a6\u30a7\u30a2\u691c\u51fa\u6e08
BlackboardAttribute.tskMessageType.text=\u30e1\u30c3\u30bb\u30fc\u30b8\u30bf\u30a4\u30d7
BlackboardAttribute.tskMinCount.text=\u6700\u5c0f\u30ab\u30a6\u30f3\u30c8
BlackboardAttribute.tskMsgId.text=\u30e1\u30c3\u30bb\u30fc\u30b8ID
BlackboardAttribute.tskMsgReplyId.text=\u30e1\u30c3\u30bb\u30fc\u30b8\u30ea\u30d7\u30e9\u30a4ID
BlackboardAttribute.tskName.text=\u540d\u524d
BlackboardAttribute.tskNamePerson.text=\u4eba\u540d
BlackboardAttribute.tskOrganization.text=\u7d44\u7e54
BlackboardAttribute.tskOtherCases.text=\u305d\u306e\u4ed6\u306e\u30b1\u30fc\u30b9
BlackboardAttribute.tskOwner.text=\u4fdd\u6709\u8005
BlackboardAttribute.tskPassword.text=\u30d1\u30b9\u30ef\u30fc\u30c9
BlackboardAttribute.tskPath.text=\u30d1\u30b9
BlackboardAttribute.tskPathId.text=\u30d1\u30b9ID
BlackboardAttribute.tskPathSource.text=\u30d1\u30b9\u30bd\u30fc\u30b9
BlackboardAttribute.tskPermissions.text=\u30d1\u30fc\u30df\u30c3\u30b7\u30e7\u30f3
BlackboardAttribute.tskPhoneNumber.text=\u96fb\u8a71\u756a\u53f7
BlackboardAttribute.tskPhoneNumberFrom.text=\u767a\u4fe1\u8005\u96fb\u8a71\u756a\u53f7
BlackboardAttribute.tskPhoneNumberHome.text=\u96fb\u8a71\u756a\u53f7\uff08\u81ea\u5b85\uff09
BlackboardAttribute.tskPhoneNumberMobile.text=\u96fb\u8a71\u756a\u53f7\uff08\u643a\u5e2f\uff09
BlackboardAttribute.tskPhoneNumberOffice.text=\u96fb\u8a71\u756a\u53f7\uff08\u4f1a\u793e\uff09
BlackboardAttribute.tskPhoneNumberTo.text=\u7740\u4fe1\u8005\u96fb\u8a71\u756a\u53f7
BlackboardAttribute.tskProcessorArchitecture.text=\u30d7\u30ed\u30bb\u30c3\u30b5\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3
BlackboardAttribute.tskProcessorName.text=\u30d7\u30ed\u30bb\u30c3\u30b5\u30fc\u540d
BlackboardAttribute.tskProductId.text=\u88fd\u54c1\u756a\u53f7
BlackboardAttribute.tskProgName.text=\u30d7\u30ed\u30b0\u30e9\u30e0\u540d
BlackboardAttribute.tskReadStatus.text=\u8aad\u3080
BlackboardAttribute.tskRealm.text=\u5206\u91ce
BlackboardAttribute.tskReferrer.text=\u30ea\u30d5\u30a1\u30e9
BlackboardAttribute.tskRemotePath.text=\u30ea\u30e2\u30fc\u30c8\u30d1\u30b9
BlackboardAttribute.tskServerName.text=\u30b5\u30fc\u30d0\u540d
BlackboardAttribute.tskSetName.text=\u30bb\u30c3\u30c8\u540d
BlackboardAttribute.tskShortcut.text=\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8
BlackboardAttribute.tskSsid.text=SSID
BlackboardAttribute.tskStegDetected.text=\u30b9\u30c6\u30ac\u30ce\u30b0\u30e9\u30d5\u30a3\u30fc\u691c\u51fa\u6e08
BlackboardAttribute.tskSubject.text=\u30b5\u30d6\u30b8\u30a7\u30af\u30c8
BlackboardAttribute.tskTLEventType.text=\u30a4\u30d9\u30f3\u30c8\u30bf\u30a4\u30d7
BlackboardAttribute.tskTagName.text=\u30bf\u30b0\u540d
BlackboardAttribute.tskTaggedArtifact.text=\u30bf\u30b0\u4ed8\u3051\u3055\u308c\u305f\u7d50\u679c
BlackboardAttribute.tskTempDir.text=\u4e00\u6642\u30d5\u30a1\u30a4\u30eb\u30d5\u30a9\u30eb\u30c0\u30fc
BlackboardAttribute.tskText.text=\u30c6\u30ad\u30b9\u30c8
BlackboardAttribute.tskTextFile.text=\u30c6\u30ad\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb
BlackboardAttribute.tskTextLanguage.text=\u30c6\u30ad\u30b9\u30c8\u8a00\u8a9e
BlackboardAttribute.tskTitle.text=\u30bf\u30a4\u30c8\u30eb
BlackboardAttribute.tskUrl.text=URL
BlackboardAttribute.tskUrlDecoded.text=\u5fa9\u53f7\u5316\u3055\u308c\u305fURL
BlackboardAttribute.tskUserId.text=\u30e6\u30fc\u30b6ID
BlackboardAttribute.tskUserName.text=\u30e6\u30fc\u30b6\u540d
BlackboardAttribute.tskValue.text=\u30d0\u30ea\u30e5\u30fc
BlackboardAttribute.tskVersion.text=\u30d0\u30fc\u30b8\u30e7\u30f3
BlackboardAttribute.tskaccountsettings.text=\u30a2\u30ab\u30a6\u30f3\u30c8\u8a2d\u5b9a
BlackboardAttribute.tskattachments.text=\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u6dfb\u4ed8\u30d5\u30a1\u30a4\u30eb
BlackboardAttribute.tskbytesreceived.text=\u53d7\u4fe1\u3057\u305f\u30d0\u30a4\u30c8\u6570
BlackboardAttribute.tskbytessent.text=\u9001\u4fe1\u30d0\u30a4\u30c8\u6570
BlackboardAttribute.tskdatetimedeleted.text=\u524a\u9664\u6642\u9593
BlackboardAttribute.tskdatetimepwdfail.text=\u30d1\u30b9\u30ef\u30fc\u30c9\u5931\u6557\u65e5
BlackboardAttribute.tskdatetimepwdreset.text=\u30d1\u30b9\u30ef\u30fc\u30c9\u30ea\u30bb\u30c3\u30c8\u65e5
BlackboardAttribute.tskdisplayname.text=\u8868\u793a\u540d
BlackboardAttribute.tskdistancefromhome.text=\u30db\u30fc\u30e0\u30dd\u30a4\u30f3\u30c8\u304b\u3089\u306e\u8ddd\u96e2
BlackboardAttribute.tskdistancetraveled.text=\u79fb\u52d5\u8ddd\u96e2
BlackboardAttribute.tskgeoareapoints.text=\u30a8\u30ea\u30a2\u306e\u8f2a\u90ed\u3092\u69cb\u6210\u3059\u308b\u30dd\u30a4\u30f3\u30c8\u30ea\u30b9\u30c8
BlackboardAttribute.tskgeopath.text=\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8\u306e\u30ea\u30b9\u30c8
BlackboardAttribute.tskgeowaypoints.text=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u306e\u30ea\u30b9\u30c8
BlackboardAttribute.tskgroups.text=\u30b0\u30eb\u30fc\u30d7
BlackboardAttribute.tskhashphotodna.text=PhotoDNA\u30cf\u30c3\u30b7\u30e5
BlackboardAttribute.tsklastprinteddatetime.text=\u6700\u7d42\u5370\u5237\u65e5
BlackboardAttribute.tskpasswordhint.text=\u30d1\u30b9\u30ef\u30fc\u30c9\u306e\u30d2\u30f3\u30c8
BlackboardAttribute.tskpasswordsettings.text=\u30d1\u30b9\u30ef\u30fc\u30c9\u8a2d\u5b9a
BlackboardAttribute.tskrule.text=\u30eb\u30fc\u30eb
BlackboardAttribute.tskthreadid.text=\u30b9\u30ec\u30c3\u30c9ID
CategoryType.AnalysisResult=\u5206\u6790\u7d50\u679c
CategoryType.DataArtifact=\u30c7\u30fc\u30bf\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8
CustomTypes.customArtifact.name=\u30ab\u30b9\u30bf\u30e0\u30fb\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30fb\u30a4\u30d9\u30f3\u30c8
CustomTypes.other.name=\u6a19\u6e96\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30fb\u30a4\u30d9\u30f3\u30c8
CustomTypes.userCreated.name=\u624b\u52d5\u3067\u4f5c\u6210\u3055\u308c\u305f\u30a4\u30d9\u30f3\u30c8
DataSourcesFilter.displayName.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u306b\u5236\u9650\u3059\u308b
DatabaseConnectionCheck.Access=\u30e6\u30fc\u30b6\u30fc\u540d\u304b\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u7121\u52b9\u3067\u3059\u3002
DatabaseConnectionCheck.Authentication=\u30e6\u30fc\u30b6\u30fc\u540d\u304b\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u7121\u52b9\u3067\u3059\u3002
DatabaseConnectionCheck.Connection=\u30db\u30b9\u30c8\u540d\u3001\u30dd\u30fc\u30c8\u3001\u30e6\u30fc\u30b6\u30fc\u540d\u3001\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u7121\u52b9\u3067\u3059\u3002
DatabaseConnectionCheck.Everything=\u30db\u30b9\u30c8\u540d\u3001\u30dd\u30fc\u30c8\u756a\u53f7\u3001\u30e6\u30fc\u30b6\u30fc\u540d\u3001\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u7121\u52b9\u3067\u3059\u3002
DatabaseConnectionCheck.HostnameOrPort=\u30db\u30b9\u30c8\u540d\u3084\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u7121\u52b9\u3067\u3059\u3002
DatabaseConnectionCheck.Installation=\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306b\u95a2\u3059\u308b\u554f\u984c\u3002 JDBC\u30c9\u30e9\u30a4\u30d0\u30fc\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002
DatabaseConnectionCheck.InternalServerIssue=PostgreSQL\u306e\u5185\u90e8\u554f\u984c\u3002 \u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u7834\u640d\u3057\u3066\u3044\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002
DatabaseConnectionCheck.MissingHostname=\u30db\u30b9\u30c8\u540d\u304c\u3042\u308a\u307e\u305b\u3093\u3002
DatabaseConnectionCheck.MissingPassword=\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u3042\u308a\u307e\u305b\u3093\u3002
DatabaseConnectionCheck.MissingPort=\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u3042\u308a\u307e\u305b\u3093\u3002
DatabaseConnectionCheck.MissingUsername=\u30e6\u30fc\u30b6\u30fc\u540d\u304c\u3042\u308a\u307e\u305b\u3093\u3002
DatabaseConnectionCheck.Port=\u30dd\u30fc\u30c8\u756a\u53f7\u304c\u7121\u52b9\u3067\u3059\u3002
DatabaseConnectionCheck.ServerDiskSpace=PostgreSQL\u30b5\u30fc\u30d0\u30fc\u306e\u554f\u984c\u3002 PostgreSQL\u30b5\u30fc\u30d0\u30fc\u306e\u30c7\u30a3\u30b9\u30af\u3068\u30e1\u30e2\u30ea\u5bb9\u91cf\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002
DatabaseConnectionCheck.ServerRestart=PostgreSQL\u30b5\u30fc\u30d0\u30fc\u306e\u554f\u984c\u3002PostgreSQL\u30b5\u30fc\u30d0\u30fc\u306e\u518d\u8d77\u52d5\u304c\u5fc5\u8981\u306a\u5834\u5408\u304c\u3042\u308a\u307e\u3059\u3002
DerviedFile.derivedMethod.exception.msg1.text=\u30d5\u30a1\u30a4\u30ebID\uff1a{0}\u306e\u6d3e\u751f\u65b9\u6cd5\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
DescriptionFilter.mode.exclude=\u9664\u5916\u3059\u308b
DescriptionFilter.mode.include=\u542b\u3080
DescriptionLOD.full=\u8a73\u7d30
DescriptionLOD.medium=\u6982\u8981
DescriptionLOD.short=\u7c21\u6f54
EventTypeHierarchyLevel.category=\u30ab\u30c6\u30b4\u30ea\u30fc
EventTypeHierarchyLevel.event=\u30a4\u30d9\u30f3\u30c8
EventTypeHierarchyLevel.root=\u30eb\u30fc\u30c8
EventTypeZoomLevel.baseType=\u30d9\u30fc\u30b9\u30bf\u30a4\u30d7
EventTypeZoomLevel.rootType=\u30eb\u30fc\u30c8\u30bf\u30a4\u30d7
EventTypeZoomLevel.subType=\u30b5\u30d6\u30bf\u30a4\u30d7
FileSystemTypes.fileAccessed.name=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb
FileSystemTypes.fileChanged.name=\u5909\u66f4\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb
FileSystemTypes.fileCreated.name=\u4f5c\u6210\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb
FileSystemTypes.fileModified.name=\u4fee\u6b63\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb
FileTypesFilter.displayName.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u5236\u9650
FsContent.readInt.err.msg.text=\u753b\u50cf\u30d5\u30a1\u30a4\u30eb\u304c\u5b58\u5728\u3057\u306a\u3044\u304b\u3001\u30a2\u30af\u30bb\u30b9\u3067\u304d\u307e\u305b\u3093\u3002
Image.verifyImageSize.errStr1.text=\u4e0d\u5b8c\u5168\u306a\u753b\u50cf\u306e\u53ef\u80fd\u6027\uff1a\u30aa\u30d5\u30bb\u30c3\u30c8{0}\u3067\u30dc\u30ea\u30e5\u30fc\u30e0\u306e\u8aad\u53d6\u308a\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
Image.verifyImageSize.errStr2.text=\u4e0d\u5b8c\u5168\u306a\u753b\u50cf\u306e\u53ef\u80fd\u6027\uff1a\u30aa\u30d5\u30bb\u30c3\u30c8{0}\u3067\u30dc\u30ea\u30e5\u30fc\u30e0\u306e\u8aad\u53d6\u308a\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
Image.verifyImageSize.errStr3.text=\u4e0d\u5b8c\u5168\u306a\u753b\u50cf\u306e\u53ef\u80fd\u6027\uff1a\u30aa\u30d5\u30bb\u30c3\u30c8{0}\u3067\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u306e\u8aad\u53d6\u308a\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
Image.verifyImageSize.errStr4.text=\u4e0d\u5b8c\u5168\u306a\u753b\u50cf\u306e\u53ef\u80fd\u6027\uff1a\u30aa\u30d5\u30bb\u30c3\u30c8{0}\u3067\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u306e\u8aad\u53d6\u308a\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
IngestJobInfo.IngestJobStatusType.Cancelled.displayName=\u30ad\u30e3\u30f3\u30bb\u30eb
IngestJobInfo.IngestJobStatusType.Completed.displayName=\u5b8c\u4e86
IngestJobInfo.IngestJobStatusType.Started.displayName=\u958b\u59cb
IngestModuleInfo.IngestModuleType.DataArtifact.displayName=\u30c7\u30fc\u30bf\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8
IngestModuleInfo.IngestModuleType.DataSourceLevel.displayName=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30ec\u30d9\u30eb
IngestModuleInfo.IngestModuleType.FileLevel.displayName=\u30d5\u30a1\u30a4\u30eb\u30ec\u30d9\u30eb
IngestModuleInfo.IngestModuleType.Multiple.displayName=\u591a\u6570
IntersectionFilter.displayName.text=\u4ea4\u5dee\u70b9
MiscTypes.Calls.name=\u901a\u8a71\u958b\u59cb
MiscTypes.CallsEnd.name=\u901a\u8a71\u7d42\u4e86
MiscTypes.Email.name=\u9001\u4fe1\u3055\u308c\u305f\u96fb\u5b50\u30e1\u30fc\u30eb
MiscTypes.EmailRcvd.name=\u30e1\u30fc\u30eb\u306e\u53d7\u4fe1
MiscTypes.GPSBookmark.name=GPS\u30d6\u30c3\u30af\u30de\u30fc\u30af
MiscTypes.GPSLastknown.name=GPS\u8a18\u9332\u306e\u6700\u5f8c\u5834\u6240
MiscTypes.GPSRoutes.name=GPS\u30eb\u30fc\u30c8
MiscTypes.GPSTrack.name=GPS\u30c8\u30e9\u30c3\u30af
MiscTypes.GPSTrackpoint.name=GPS\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8
MiscTypes.GPSearch.name=GPS\u691c\u7d22
MiscTypes.LogEntry.name=\u30ed\u30b0\u767b\u9332
MiscTypes.Registry.name=\u30ec\u30b8\u30b9\u30c8\u30ea
MiscTypes.devicesAttached.name=\u63a5\u7d9a\u3055\u308c\u3066\u3044\u308b\u30c7\u30d0\u30a4\u30b9
MiscTypes.exif.name=Exif
MiscTypes.installedPrograms.name=\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08\u307f\u306e\u30d7\u30ed\u30b0\u30e9\u30e0
MiscTypes.message.name=\u30e1\u30c3\u30bb\u30fc\u30b8
MiscTypes.metadataCreated.name=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u4f5c\u6210
MiscTypes.metadataLastPrinted.name=\u6700\u7d42\u5370\u5237\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8
MiscTypes.metadataLastSaved.name=\u6700\u5f8c\u306b\u4fdd\u5b58\u3055\u308c\u305f\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8
MiscTypes.programexecuted.name=\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u5b9f\u884c
MiscTypes.recentDocuments.name=\u6700\u8fd1\u306e\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8
OsAccountInstanceType.Accessed.descr.text=\u30a2\u30ab\u30a6\u30f3\u30c8\u6240\u6709\u8005\u306f\u3001\u4f55\u3089\u304b\u306e\u30b5\u30fc\u30d3\u30b9\u3092\u4ecb\u3057\u3066\u8aad\u307f/\u66f8\u304d\u306b\u30db\u30b9\u30c8\u4e0a\u306e\u30ea\u30bd\u30fc\u30b9\u306b\u30a2\u30af\u30bb\u30b9\u3057\u307e\u3057\u305f\u3002
OsAccountInstanceType.Accessed.text=\u30a2\u30af\u30bb\u30b9\u6e08\u307f
OsAccountInstanceType.Launched.descr.text=\u30a2\u30ab\u30a6\u30f3\u30c8\u6240\u6709\u8005\u306f\u3001\u30db\u30b9\u30c8\u3067\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u3092\u958b\u59cb\u3057\u307e\u3057\u305f\u3002
OsAccountInstanceType.Launched.text=\u4f5c\u52d5
OsAccountInstanceType.Referenced.descr.text=\u30a2\u30ab\u30a6\u30f3\u30c8\u6240\u6709\u8005\u306f\u3001\u30db\u30b9\u30c8\u4e0a\u306e\u30ed\u30b0\u30d5\u30a1\u30a4\u30eb\u3067\u53c2\u7167\u3055\u308c\u307e\u3057\u305f\u3002
OsAccountInstanceType.Referenced.text=\u53c2\u7167
OsAccountRealm.Domain.text=\u30c9\u30e1\u30a4\u30f3
OsAccountRealm.Inferred.text=\u63a8\u6e2c
OsAccountRealm.Known.text=\u65e2\u77e5
OsAccountRealm.Local.text=\u30ed\u30fc\u30ab\u30eb
OsAccountRealm.Unknown.text=\u4e0d\u660e
OsAccountStatus.Active.text=\u30a2\u30af\u30c6\u30a3\u30d6
OsAccountStatus.Deleted.text=\u524a\u9664\u6e08\u307f
OsAccountStatus.NonExistent.text=\u5b58\u5728\u3057\u306a\u3044
OsAccountStatus.Disabled.text=\u7121\u52b9
OsAccountStatus.Unknown.text=\u4e0d\u660e
OsAccountType.Interactive.text=\u30a4\u30f3\u30bf\u30e9\u30af\u30c6\u30a3\u30d6
OsAccountType.Service.text=\u30b5\u30fc\u30d3\u30b9
OsAccountType.Unknown.text=\u4e0d\u660e
ReviewStatus.Approved=\u627f\u8a8d\u6e08\u307f
ReviewStatus.Rejected=\u62d2\u5426\u3055\u308c\u307e\u3057\u305f
ReviewStatus.Undecided=\u672a\u5b9a
RootEventType.eventTypes.name=\u30a4\u30d9\u30f3\u30c8\u30bf\u30a4\u30d7
Score.Priority.Normal.displayName.text=\u6b63\u5e38
Score.Priority.Override.displayName.text=\u30aa\u30fc\u30d0\u30fc\u30e9\u30a4\u30c9
Significance.LikelyNone.displayName.text=\u304a\u305d\u3089\u304f\u6ce8\u76ee\u306b\u5024\u3057\u306a\u3044
Significance.LikelyNotable.displayName.text=\u304a\u305d\u3089\u304f\u6ce8\u76ee\u306b\u5024\u3059\u308b
Significance.None.displayName.text=\u6ce8\u76ee\u306b\u5024\u3057\u306a\u3044
Significance.Notable.displayName.text=\u6ce8\u76ee\u3059\u3079\u304d
Significance.Unknown.displayName.text=\u4e0d\u660e
SlackFile.readInt.err.msg.text=\u753b\u50cf\u30d5\u30a1\u30a4\u30eb\u304c\u5b58\u5728\u3057\u306a\u3044\u304b\u3001\u30a2\u30af\u30bb\u30b9\u3067\u304d\u307e\u305b\u3093\u3002
SleuthkitCase.SchemaVersionMismatch=\u30b9\u30ad\u30fc\u30de\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u4e00\u81f4\u3057\u307e\u305b\u3093
SleuthkitCase.addDerivedFile.exception.msg1.text=\u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u65b0\u898fID\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002\u30d5\u30a1\u30a4\u30eb\u540d\uff1a{0}
SleuthkitCase.addDerivedFile.exception.msg2.text=\u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30d5\u30a1\u30a4\u30eb\u540d\uff1a{0}
SleuthkitCase.addLocalFile.exception.msg1.text=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb{0}\u306e\u8ffd\u52a0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u8ffd\u52a0\u5148\u306e\u30da\u30a2\u30ec\u30f3\u30c8\u304c\u30cc\u30eb\u3067\u3059\u3002
SleuthkitCase.addLocalFile.exception.msg2.text=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u65b0\u898fID\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30d5\u30a1\u30a4\u30eb\u540d\uff1a{0}
SleuthkitCase.addLocalFile.exception.msg3.text=\u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30d5\u30a1\u30a4\u30eb\u540d\uff1a{0}
SleuthkitCase.findFiles.exception.msg1.text=\u30a8\u30e9\u30fc\uff1a\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306f\u30da\u30a2\u30ec\u30f3\u30c8\u304c\u7121\u3044\uff08\u30a4\u30e1\u30fc\u30b8\u3001\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\uff09\u306f\u305a\u3067\u3059\u304c\u3001\u4e0b\u8a18\u304c\u5b58\u5728\u3057\u307e\u3059\uff1a{0}
SleuthkitCase.findFiles.exception.msg2.text=\u30a8\u30e9\u30fc\uff1a\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306f\u30a4\u30e1\u30fc\u30b8\u307e\u305f\u306fVirtualDirectory\u3067\u3042\u308b\u3079\u304d\u3067\u3059\u304c\u3001\u4e0b\u8a18\u304c\u5b58\u5728\u3057\u307e\u3059\uff1a{0}
SleuthkitCase.findFiles.exception.msg3.text=\u30d5\u30a1\u30a4\u30eb\u540d\u306b\u57fa\u3065\u3044\u305f\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
SleuthkitCase.findFiles3.exception.msg1.text=\u30a8\u30e9\u30fc\uff1a\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306f\u30da\u30a2\u30ec\u30f3\u30c8\u304c\u7121\u3044\uff08\u30a4\u30e1\u30fc\u30b8\u3001\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\uff09\u306f\u305a\u3067\u3059\u304c\u3001\u4e0b\u8a18\u304c\u5b58\u5728\u3057\u307e\u3059\uff1a{0}
SleuthkitCase.findFiles3.exception.msg2.text=\u30a8\u30e9\u30fc\uff1a\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306f\u30a4\u30e1\u30fc\u30b8\u307e\u305f\u306fVirtualDirectory\u3067\u3042\u308b\u3079\u304d\u3067\u3059\u304c\u3001\u4e0b\u8a18\u304c\u5b58\u5728\u3057\u307e\u3059\uff1a{0}
SleuthkitCase.findFiles3.exception.msg3.text=\u30d5\u30a1\u30a4\u30eb\u540d\u306b\u57fa\u3065\u3044\u305f\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306e\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
SleuthkitCase.getLastObjectId.exception.msg.text=\u6700\u5f8c\u306e\u30aa\u30d6\u30b8\u30a7\u30af\u30c8ID\u3092\u53d6\u5f97\u3057\u305f\u5f8c\u3001\u7d50\u679c\u30bb\u30c3\u30c8\u3092\u9589\u3058\u308b\u969b\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
SleuthkitCase.isFileFromSource.exception.msg.text=\u30a8\u30e9\u30fc\uff1a\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306f\u30da\u30a2\u30ec\u30f3\u30c8\u304c\u7121\u3044\uff08\u30a4\u30e1\u30fc\u30b8\u3001\u30d5\u30a1\u30a4\u30eb\u30bb\u30c3\u30c8\uff09\u306f\u305a\u3067\u3059\u304c\u3001\u4e0b\u8a18\u304c\u5b58\u5728\u3057\u307e\u3059\uff1a{0}
SleuthkitCase.isFileFromSource.exception.msg2.text=\u30a8\u30e9\u30fc\uff1a\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306f\u30a4\u30e1\u30fc\u30b8\u307e\u305f\u306fVirtualDirectory\u3067\u3042\u308b\u3079\u304d\u3067\u3059\u304c\u3001\u4e0b\u8a18\u304c\u5b58\u5728\u3057\u307e\u3059\uff1a{0}
TextFilter.displayName.text=\u30c6\u30ad\u30b9\u30c8\u3092\u542b\u3081\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\uff1a
TimelineEventType.BackupEvent.description.end=\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u7d42\u4e86
TimelineEventType.BackupEvent.description.start=\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u958b\u59cb
TimelineEventType.BackupEventEnd.txt=\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u7d42\u4e86
TimelineEventType.BackupEventStart.txt=\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u958b\u59cb
TimelineEventType.BluetoothAdapter.txt=Bluetooth\u30a2\u30c0\u30d7\u30bf\u30fc
TimelineEventType.BluetoothPairing.txt=Bluetooth\u30da\u30a2\u30ea\u30f3\u30b0
TimelineEventType.BluetoothPairingLastConnection.txt=\u6700\u5f8c\u306e\u63a5\u7d9a\u3092Bluetooth\u3067\u30da\u30a2\u30ea\u30f3\u30b0
TimelineEventType.CalendarEntryEnd.txt=\u30ab\u30ec\u30f3\u30c0\u30fc\u5165\u529b\u7d42\u4e86
TimelineEventType.CalendarEntryStart.txt=\u30ab\u30ec\u30f3\u30c0\u30fc\u5165\u529b\u958b\u59cb
TimelineEventType.DeletedProgram.txt=\u30d7\u30ed\u30b0\u30e9\u30e0\u304c\u524a\u9664\u3055\u308c\u307e\u3057\u305f
TimelineEventType.DeletedProgramDeleted.txt=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u524a\u9664\u3055\u308c\u307e\u3057\u305f
TimelineEventType.OSAccountAccessed.txt=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305fOS\u30a2\u30ab\u30a6\u30f3\u30c8
TimelineEventType.OSAccountCreated.txt=\u30c6\u30ad\u30b9\u30c8\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u304b\u3089\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u524a\u9664\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
TimelineEventType.OSAccountPwdFail.txt=OS\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u30d1\u30b9\u30ef\u30fc\u30c9\u30fb\u30a8\u30e9\u30fc
TimelineEventType.OSAccountPwdReset.txt=OS\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u30d1\u30b9\u30ef\u30fc\u30c9\u306e\u30ea\u30bb\u30c3\u30c8
TimelineEventType.OSInfo.txt=\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0\u60c5\u5831
TimelineEventType.ProgramNotification.txt=\u30d7\u30ed\u30b0\u30e9\u30e0\u901a\u77e5
TimelineEventType.ScreenShot.txt=\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8
TimelineEventType.ServiceAccount.txt=\u30b5\u30fc\u30d3\u30b9\u30fb\u30a2\u30ab\u30a6\u30f3\u30c8
TimelineEventType.UserDeviceEventEnd.txt=\u30e6\u30fc\u30b6\u30fc\u6d3b\u52d5\u7d42\u4e86
TimelineEventType.UserDeviceEventStart.txt=\u30e6\u30fc\u30b6\u30fc\u6d3b\u52d5\u958b\u59cb
TimelineEventType.WIFINetwork.txt=Wifi\u30cd\u30c3\u30c8\u30ef\u30fc\u30af
TimelineEventType.WebCache.text=Web\u30ad\u30e3\u30c3\u30b7\u30e5
TimelineLevelOfDetail.high=\u9ad8
TimelineLevelOfDetail.low=\u4f4e
TimelineLevelOfDetail.medium=\u4e2d
TskData.encodingType.exception.msg1.text=\u5024\uff1a{0}\u3000\u306eEncodingType\u304c\u3042\u308a\u307e\u305b\u3093
TskData.fileKnown.exception.msg1.text=\u30d0\u30ea\u30e5\u30fc\uff1a{0}\u306fFileKnown\u306b\u8a72\u5f53\u3057\u307e\u305b\u3093
TskData.fileKnown.known=\u65e2\u77e5
TskData.fileKnown.knownBad=\u6ce8\u76ee\u3059\u3079\u304d
TskData.fileKnown.unknown=\u4e0d\u660e
TskData.objectTypeEnum.exception.msg1.text=\u30d0\u30ea\u30e5\u30fc\uff1a{0}\u306f\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u30bf\u30a4\u30d7\u306b\u8a72\u5f53\u3057\u307e\u305b\u3093
TskData.tskDbFilesTypeEnum.exception.msg1.text=\u30d0\u30ea\u30e5\u30fc\uff1a{0}\u306fTSK_FILE_TYPE_ENUM\u306b\u8a72\u5f53\u3057\u307e\u305b\u3093
TskData.tskFsAttrTypeEnum.exception.msg1.text=\u30d0\u30ea\u30e5\u30fc\uff1a{0}\u306fTSK_FS_TYPE_ENUM\u306b\u8a72\u5f53\u3057\u307e\u305b\u3093
TskData.tskFsMetaFlagEnum.unknown=\u4e0d\u660e
TskData.tskFsMetaFlagEnum.allocated=\u5272\u308a\u5f53\u3066\u6e08\u307f
TskData.tskFsMetaFlagEnum.compressed=\u5727\u7e2e\u6e08\u307f
TskData.tskFsMetaFlagEnum.orphan=\u30aa\u30fc\u30d5\u30a1\u30f3
TskData.tskFsMetaFlagEnum.unallocated=\u672a\u5272\u308a\u5f53\u3066
TskData.tskFsMetaFlagEnum.unused=\u672a\u4f7f\u7528
TskData.tskFsMetaFlagEnum.used=\u4f7f\u7528\u6e08\u307f
TskData.tskFsMetaTypeEnum.exception.msg1.text=\u30d0\u30ea\u30e5\u30fc\uff1a{0}\u306fTSK_FS_META_TYPE_ENUM\u306b\u8a72\u5f53\u3057\u307e\u305b\u3093
TskData.tskFsNameFlagEnum.unknown=\u4e0d\u660e
TskData.tskFsNameFlagEnum.allocated=\u5272\u308a\u5f53\u3066\u6e08\u307f
TskData.tskFsNameFlagEnum.exception.msg1.text=\u30d0\u30ea\u30e5\u30fc\uff1a{0}\u306fTSK_FS_NAME_FLAG_ENUM\u306b\u8a72\u5f53\u3057\u307e\u305b\u3093
TskData.tskFsNameFlagEnum.unallocated=\u672a\u5272\u308a\u5f53\u3066
TskData.tskFsNameTypeEnum.exception.msg1.text=\u30d0\u30ea\u30e5\u30fc\uff1a{0}\u306fTSK_FS_NAME_TYPE_ENUM\u306b\u8a72\u5f53\u3057\u307e\u305b\u3093
TskData.tskFsTypeEnum.APFSautoDetect=APFS\uff08\u81ea\u52d5\u691c\u51fa\uff09
TskData.tskFsTypeEnum.ExtXautoDetect=ExtX\uff08\u81ea\u52d5\u691c\u51fa\uff09
TskData.tskFsTypeEnum.FATautoDetect=FAT\uff08\u81ea\u52d5\u691c\u51fa\uff09
TskData.tskFsTypeEnum.HFSautoDetect=HFS\uff08\u81ea\u52d5\u691c\u51fa\uff09
TskData.tskFsTypeEnum.ISO9660autoDetect=ISO9660\uff08\u81ea\u52d5\u691c\u51fa\uff09
TskData.tskFsTypeEnum.NTFSautoDetect=NTFS\uff08\u81ea\u52d5\u691c\u51fa\uff09
TskData.tskFsTypeEnum.RAWautoDetect=RAW\uff08\u81ea\u52d5\u691c\u51fa\uff09
TskData.tskFsTypeEnum.SWAPautoDetect=SWAP\uff08\u81ea\u52d5\u691c\u51fa\uff09
TskData.tskFsTypeEnum.YAFFS2autoDetect=YAFFS2\uff08\u81ea\u52d5\u691c\u51fa\uff09
TskData.tskFsTypeEnum.autoDetect=\u81ea\u52d5\u691c\u51fa
TskData.tskFsTypeEnum.exception.msg1.text=\u30d0\u30ea\u30e5\u30fc\uff1a{0}\u306fTSK_FS_TYPE_ENUM\u306b\u8a72\u5f53\u3057\u307e\u305b\u3093
TskData.tskFsTypeEnum.unsupported=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0
TskData.tskImgTypeEnum.autoDetect=\u81ea\u52d5\u691c\u51fa
TskData.tskImgTypeEnum.exception.msg1.text=\u30d0\u30ea\u30e5\u30fc\uff1a{0}\u306fTSK_IMG_TYPE_ENUM\u306b\u8a72\u5f53\u3057\u307e\u305b\u3093
TskData.tskImgTypeEnum.rawSingle=\u30ed\u30fc\u30b7\u30f3\u30b0\u30eb
TskData.tskImgTypeEnum.rawSplit=\u30ed\u30fc\u30b9\u30d7\u30ea\u30c3\u30c8
TskData.tskImgTypeEnum.unknown=\u4e0d\u660e
TskData.tskVSTypeEnum.autoDetect=\u81ea\u52d5\u691c\u51fa
TskData.tskVSTypeEnum.exception.msg1.text=\u30d0\u30ea\u30e5\u30fc\uff1a{0}\u306fTSK_VS_TYPE_ENUM\u306b\u8a72\u5f53\u3057\u307e\u305b\u3093
TskData.tskVSTypeEnum.fake=\u507d\u7269
TskData.tskVSTypeEnum.unsupported=\u975e\u30b5\u30dd\u30fc\u30c8
TypeFilter.displayName.text=\u30a4\u30d9\u30f3\u30c8\u30bf\u30a4\u30d7\u3092\u5236\u9650
Volume.desc.text=\u4e0d\u660e
Volume.read.exception.msg1.text=\u3053\u306e\u30dc\u30ea\u30e5\u30fc\u30e0\u306e\u30da\u30a2\u30ec\u30f3\u30c8\u306fVolmueSystem\u3067\u3042\u308b\u3079\u304d\u3067\u3059\u304c\u3001\u9055\u3044\u307e\u3059\u3002
Volume.vsFlagToString.allocated=\u5272\u308a\u5f53\u3066\u6e08\u307f
Volume.vsFlagToString.unallocated=\u672a\u5272\u308a\u5f53\u3066
WebTypes.webBookmarks.name=Web\u30d6\u30c3\u30af\u30de\u30fc\u30af
WebTypes.webCookies.name=WebCookie\u3092\u4f5c\u6210
WebTypes.webCookiesAccessed.name=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305fWebCookie
WebTypes.webCookiesEnd.name=WebCookie\u304c\u7d42\u4e86
WebTypes.webCookiesStart.name=WebCookie\u306e\u958b\u59cb
WebTypes.webDownloads.name=Web\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9
WebTypes.webFormAddress.name=Web\u30d5\u30a9\u30fc\u30e0\u30a2\u30c9\u30ec\u30b9\u304c\u4f5c\u6210\u3055\u308c\u307e\u3057\u305f
WebTypes.webFormAddressModified.name=Web\u30d5\u30a9\u30fc\u30e0\u30a2\u30c9\u30ec\u30b9\u304c\u5909\u66f4\u3055\u308c\u307e\u3057\u305f
WebTypes.webFormAutoFill.name=Web\u30d5\u30a9\u30fc\u30e0\u306e\u81ea\u52d5\u5165\u529b\u304c\u4f5c\u6210\u3055\u308c\u307e\u3057\u305f
WebTypes.webFormAutofillAccessed.name=Web\u30d5\u30a9\u30fc\u30e0\u306e\u81ea\u52d5\u5165\u529b\u306b\u30a2\u30af\u30bb\u30b9
WebTypes.webHistory.name=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305fWeb\u5c65\u6b74
WebTypes.webHistoryCreated.name=Web\u5c65\u6b74\u304c\u4f5c\u6210\u3055\u308c\u307e\u3057\u305f
WebTypes.webSearch.name=Web\u691c\u7d22
ZoomSettingsPane.descrLODLabel.text=\u8a73\u7d30\u8aac\u660e\uff1a
ZoomSettingsPane.historyLabel.text=\u5c65\u6b74\uff1a
ZoomSettingsPane.timeUnitLabel.text=\u6642\u9593\u5358\u4f4d\uff1a
ZoomSettingsPane.typeZoomLabel.text=\u30a4\u30d9\u30f3\u30c8\u30bf\u30a4\u30d7\uff1a
hashHitsFilter.displayName.text=\u30cf\u30c3\u30b7\u30e5\u30d2\u30c3\u30c8\u304c\u5fc5\u8981
hideKnownFilter.displayName.text=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb\u3092\u975e\u8868\u793a
tagsFilter.displayName.text=\u30bf\u30b0\u4ed8\u3051\u304c\u5fc5\u8981\u304c\u3067\u3059
/*
* Sleuth Kit Data Model
*
* Copyright 2011-2016 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.datamodel;
import java.util.List;
/**
* @deprecated Use CarvingResult instead.
*/
@Deprecated
public final class CarvedFileContainer {
private final String mCarvedFileName;
private final long mCarvedFileSize;
private final long mContainerId;
private final List<TskFileRange> mRangeData;
public CarvedFileContainer(String carvedFileName, long carvedFileSize, long containerId, List<TskFileRange> rangeData) {
mCarvedFileName = carvedFileName;
mCarvedFileSize = carvedFileSize;
mContainerId = containerId;
mRangeData = rangeData;
}
public String getName() {
return mCarvedFileName;
}
public long getSize() {
return mCarvedFileSize;
}
public long getId() {
return mContainerId;
}
public List<TskFileRange> getRanges() {
return mRangeData;
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2011-2016 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.datamodel;
import java.util.ArrayList;
import java.util.List;
/**
* A carving result consisting of a set of carved files and the parent from
* which the files were carved.
*/
public final class CarvingResult {
private final Content parent;
private final List<CarvedFile> carvedFiles;
/**
* Constructs a carving result consisting of a set of carved files and the
* parent from which the files were carved.
*
* @param parent The parent of the set of carved files in the carving
* result.
* @param carvedFiles The set of carved files in the carving result.
*/
public CarvingResult(Content parent, List<CarvedFile> carvedFiles) {
this.parent = parent;
this.carvedFiles = new ArrayList<CarvedFile>(carvedFiles);
}
/**
* Gets the parent of the carved files in a carving result.
*
* @return The parent of the set of carved files in the carving result.
*/
final Content getParent() {
return parent;
}
/**
* Gets the carved files in a carving result.
*
* @return The set of carved files in the carving result.
*/
final List<CarvedFile> getCarvedFiles() {
return carvedFiles;
}
/**
* A carved file.
*/
public final static class CarvedFile {
private final String name;
private final long sizeInBytes;
private final List<TskFileRange> layoutInParent;
/**
* Constructs a carved file.
*
* @param name The name of the file.
* @param sizeInBytes The size of the file in bytes.
* @param layoutInParent The layout of the file within its parent.
*/
public CarvedFile(String name, long sizeInBytes, List<TskFileRange> layoutInParent) {
this.name = name;
this.sizeInBytes = sizeInBytes;
this.layoutInParent = layoutInParent;
}
/**
* Gets the name of the carved file.
*
* @return The file name.
*/
final String getName() {
return name;
}
/**
* Gets the size of the carved file.
*
* @return The size of the file in bytes.
*/
final long getSizeInBytes() {
return sizeInBytes;
}
/**
* Gets the layout of the carved file within its parent.
*
* @return A list of TskRange objects representing the layout of the
* carved file within its parent.
*/
final List<TskFileRange> getLayoutInParent() {
return layoutInParent;
}
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2020-2021 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.datamodel;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.logging.Logger;
import java.util.logging.Level;
import org.sleuthkit.datamodel.SQLHelper.PostgreSQLHelper;
import org.sleuthkit.datamodel.SQLHelper.SQLiteHelper;
/**
* Creates a SQLite or PostgreSQL case database.
*/
class CaseDatabaseFactory {
private static final Logger logger = Logger.getLogger(CaseDatabaseFactory.class.getName());
private final SQLHelper dbQueryHelper;
private final DbCreationHelper dbCreationHelper;
// ssl=true: enables SSL encryption.
// NonValidatingFactory avoids hostname verification.
// sslmode=require: This mode makes the encryption mandatory and also requires the connection to fail if it can't be encrypted.
// In this mode, the JDBC driver accepts all server certificates, including self-signed ones.
final static String SSL_NONVERIFY_URL = "?ssl=true&sslfactory=org.postgresql.ssl.NonValidatingFactory&sslmode=require";
// ssl=true: enables SSL encryption.
// DefaultJavaSSLFactory: uses application's default JRE keystore to validate server certificate.
// sslmode=verify-ca: verifies that the server we are connecting to is trusted by CA.
final static String SSL_VERIFY_DEFAULT_URL = "?ssl=true&sslfactory=org.postgresql.ssl.DefaultJavaSSLFactory&sslmode=verify-ca";
/**
* Creates JDBC URL string for implementations that use custom keystore to
* validate PostgreSQL CA-signed SSL certificates. The class that performs
* SSL certificate validation must extend org.postgresql.ssl.WrappedFactory
* and generally must follow the same logic.
*
* ssl=true: enables SSL encryption.
* sslmode=verify-ca: verifies that the server we are connecting to is trusted by CA.
*
* @param customSslValidationClassName full canonical name of a Java class
* that performs custom SSL certificate
* validation.
*
* @return JDBS URL string used to connect to PosgreSQL server via CA-signed
* SSL certificate.
*/
static String getCustomPostrgesSslVerificationUrl(String customSslValidationClassName) {
return "?ssl=true&sslfactory=" + customSslValidationClassName + "&sslmode=verify-ca";
}
/**
* Create a new SQLite case
*
* @param dbPath Full path to the database
*/
CaseDatabaseFactory(String dbPath) {
this.dbQueryHelper = new SQLiteHelper();
this.dbCreationHelper = new SQLiteDbCreationHelper(dbPath);
}
/**
* Create a new PostgreSQL case
*
* @param caseName The name of the case. It will be used to create a case
* database name that can be safely used in SQL commands
* and will not be subject to name collisions on the case
* database server. Use getDatabaseName to get the
* created name.
* @param info The information to connect to the database.
*/
CaseDatabaseFactory(String caseName, CaseDbConnectionInfo info) {
this.dbQueryHelper = new PostgreSQLHelper();
this.dbCreationHelper = new PostgreSQLDbCreationHelper(caseName, info);
}
/**
* Creates and initializes the case database.
* Currently the case must be reopened after creation.
*
* @throws TskCoreException
*/
void createCaseDatabase() throws TskCoreException {
createDatabase();
initializeSchema();
}
/**
* Create the database itself (if necessary)
*
* @throws TskCoreException
*/
private void createDatabase() throws TskCoreException {
dbCreationHelper.createDatabase();
}
/**
* Initialize the database schema
*
* @throws TskCoreException
*/
private void initializeSchema() throws TskCoreException {
try (Connection conn = dbCreationHelper.getConnection()) {
// Perform any needed steps before creating the tables
dbCreationHelper.performPreInitialization(conn);
// Add schema version
addDbInfo(conn);
// Add tables
addTables(conn);
dbCreationHelper.performPostTableInitialization(conn);
// Add indexes
addIndexes(conn);
} catch (SQLException ex) {
throw new TskCoreException("Error initializing case database", ex);
}
}
/**
* Create and populate the db_info tables
*
* @param conn the database connection
*
* @throws TskCoreException
*/
private void addDbInfo(Connection conn) throws TskCoreException {
CaseDbSchemaVersionNumber version = SleuthkitCase.CURRENT_DB_SCHEMA_VERSION;
long tskVersionNum = SleuthkitJNI.getSleuthkitVersion(); // This is the current version of TSK
try (Statement stmt = conn.createStatement()) {
stmt.execute("CREATE TABLE tsk_db_info (schema_ver INTEGER, tsk_ver INTEGER, schema_minor_ver INTEGER)");
stmt.execute("INSERT INTO tsk_db_info (schema_ver, tsk_ver, schema_minor_ver) VALUES (" +
version.getMajor() + ", " + tskVersionNum + ", " + version.getMinor() + ");");
stmt.execute("CREATE TABLE tsk_db_info_extended (name TEXT PRIMARY KEY, value TEXT NOT NULL);");
stmt.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('TSK_VERSION', '" + tskVersionNum + "');");
stmt.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('SCHEMA_MAJOR_VERSION', '" + version.getMajor() + "');");
stmt.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('SCHEMA_MINOR_VERSION', '" + version.getMinor() + "');");
stmt.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('CREATION_SCHEMA_MAJOR_VERSION', '" + version.getMajor() + "');");
stmt.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('CREATION_SCHEMA_MINOR_VERSION', '" + version.getMinor() + "');");
} catch (SQLException ex) {
throw new TskCoreException("Error initializing db_info tables", ex);
}
}
/**
* Add and initialize the database tables
*
* @param conn the database connection
*
* @throws TskCoreException
*/
private void addTables(Connection conn) throws TskCoreException {
try (Statement stmt = conn.createStatement()) {
createTskObjects(stmt);
createHostTables(stmt);
createAccountTables(stmt);
createFileTables(stmt);
createArtifactTables(stmt);
createAnalysisResultsTables(stmt);
createTagTables(stmt);
createIngestTables(stmt);
createEventTables(stmt);
createAttributeTables(stmt);
createAccountInstancesAndArtifacts(stmt);
} catch (SQLException ex) {
throw new TskCoreException("Error initializing tables", ex);
}
}
// tsk_objects is referenced by many other tables and should be created first
private void createTskObjects(Statement stmt) throws SQLException {
// The UNIQUE here on the object ID is to create an index
stmt.execute("CREATE TABLE tsk_objects (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, par_obj_id " + dbQueryHelper.getBigIntType()
+ ", type INTEGER NOT NULL, UNIQUE (obj_id), FOREIGN KEY (par_obj_id) REFERENCES tsk_objects (obj_id) ON DELETE CASCADE)");
}
private void createFileTables(Statement stmt) throws SQLException {
stmt.execute("CREATE TABLE tsk_image_info (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, type INTEGER, ssize INTEGER, "
+ "tzone TEXT, size " + dbQueryHelper.getBigIntType() + ", md5 TEXT, sha1 TEXT, sha256 TEXT, display_name TEXT, "
+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
stmt.execute("CREATE TABLE tsk_image_names (obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, name TEXT NOT NULL, "
+ "sequence INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
stmt.execute("CREATE TABLE tsk_vs_info (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, vs_type INTEGER NOT NULL, "
+ "img_offset " + dbQueryHelper.getBigIntType() + " NOT NULL, block_size " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
stmt.execute("CREATE TABLE tsk_vs_parts (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "addr " + dbQueryHelper.getBigIntType() + " NOT NULL, start " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "length " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ dbQueryHelper.getVSDescColName() + " TEXT, "
+ "flags INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE);");
stmt.execute("CREATE TABLE tsk_pool_info (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "pool_type INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE);");
stmt.execute("CREATE TABLE data_source_info (obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, device_id TEXT NOT NULL, "
+ "time_zone TEXT NOT NULL, acquisition_details TEXT, added_date_time "+ dbQueryHelper.getBigIntType() + ", "
+ "acquisition_tool_settings TEXT, acquisition_tool_name TEXT, acquisition_tool_version TEXT, "
+ "host_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "FOREIGN KEY(host_id) REFERENCES tsk_hosts(id), "
+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
stmt.execute("CREATE TABLE tsk_fs_info (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "img_offset " + dbQueryHelper.getBigIntType() + " NOT NULL, fs_type INTEGER NOT NULL, "
+ "block_size " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "block_count " + dbQueryHelper.getBigIntType() + " NOT NULL, root_inum " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "first_inum " + dbQueryHelper.getBigIntType() + " NOT NULL, last_inum " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "display_name TEXT, "
+ "FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
stmt.execute("CREATE TABLE file_collection_status_types (collection_status_type INTEGER PRIMARY KEY, name TEXT NOT NULL)");
stmt.execute("CREATE TABLE tsk_files (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "fs_obj_id " + dbQueryHelper.getBigIntType() + ", data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "attr_type INTEGER, attr_id INTEGER, "
+ "name TEXT NOT NULL, meta_addr " + dbQueryHelper.getBigIntType() + ", meta_seq " + dbQueryHelper.getBigIntType() + ", "
+ "type INTEGER, has_layout INTEGER, has_path INTEGER, "
+ "dir_type INTEGER, meta_type INTEGER, dir_flags INTEGER, meta_flags INTEGER, size " + dbQueryHelper.getBigIntType() + ", "
+ "ctime " + dbQueryHelper.getBigIntType() + ", "
+ "crtime " + dbQueryHelper.getBigIntType() + ", atime " + dbQueryHelper.getBigIntType() + ", "
+ "mtime " + dbQueryHelper.getBigIntType() + ", mode INTEGER, uid INTEGER, gid INTEGER, md5 TEXT, sha256 TEXT, sha1 TEXT,"
+ "known INTEGER, "
+ "parent_path TEXT, mime_type TEXT, extension TEXT, "
+ "owner_uid TEXT DEFAULT NULL, "
+ "os_account_obj_id " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, "
+ "collected INTEGER NOT NULL, "
+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(fs_obj_id) REFERENCES tsk_fs_info(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE SET NULL, "
+ "FOREIGN KEY(collected) REFERENCES file_collection_status_types (collection_status_type))" );
stmt.execute("CREATE TABLE file_encoding_types (encoding_type INTEGER PRIMARY KEY, name TEXT NOT NULL)");
stmt.execute("CREATE TABLE tsk_files_path (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, path TEXT NOT NULL, "
+ "encoding_type INTEGER NOT NULL, FOREIGN KEY(encoding_type) references file_encoding_types(encoding_type), "
+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
stmt.execute("CREATE TABLE tsk_files_derived (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "derived_id " + dbQueryHelper.getBigIntType() + " NOT NULL, rederive TEXT, "
+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
stmt.execute("CREATE TABLE tsk_files_derived_method (derived_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "tool_name TEXT NOT NULL, tool_version TEXT NOT NULL, other TEXT)");
stmt.execute("CREATE TABLE tsk_file_layout (obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "byte_start " + dbQueryHelper.getBigIntType() + " NOT NULL, byte_len " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "sequence INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE);");
stmt.execute("CREATE TABLE reports (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, path TEXT NOT NULL, "
+ "crtime INTEGER NOT NULL, src_module_name TEXT NOT NULL, report_name TEXT NOT NULL, "
+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE);");
}
private void createArtifactTables(Statement stmt) throws SQLException {
stmt.execute("CREATE TABLE blackboard_artifact_types (artifact_type_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "type_name TEXT NOT NULL, display_name TEXT,"
+ "category_type INTEGER DEFAULT 0)");
stmt.execute("CREATE TABLE blackboard_attribute_types (attribute_type_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "type_name TEXT NOT NULL, display_name TEXT, value_type INTEGER NOT NULL)");
stmt.execute("CREATE TABLE review_statuses (review_status_id INTEGER PRIMARY KEY, "
+ "review_status_name TEXT NOT NULL, "
+ "display_name TEXT NOT NULL)");
stmt.execute("CREATE TABLE blackboard_artifacts (artifact_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "artifact_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "data_source_obj_id " + dbQueryHelper.getBigIntType() + ", "
+ "artifact_type_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "review_status_id INTEGER NOT NULL, "
+ "UNIQUE (artifact_obj_id),"
+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(artifact_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(artifact_type_id) REFERENCES blackboard_artifact_types(artifact_type_id), "
+ "FOREIGN KEY(review_status_id) REFERENCES review_statuses(review_status_id))");
/* Binary representation of BYTEA is a bunch of bytes, which could
* include embedded nulls so we have to pay attention to field length.
* http://www.postgresql.org/docs/9.4/static/libpq-example.html
*/
stmt.execute("CREATE TABLE blackboard_attributes (artifact_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "artifact_type_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "source TEXT, context TEXT, attribute_type_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "value_type INTEGER NOT NULL, value_byte " + dbQueryHelper.getBlobType() + ", "
+ "value_text TEXT, value_int32 INTEGER, value_int64 " + dbQueryHelper.getBigIntType() + ", value_double NUMERIC(20, 10), "
+ "FOREIGN KEY(artifact_id) REFERENCES blackboard_artifacts(artifact_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(artifact_type_id) REFERENCES blackboard_artifact_types(artifact_type_id), "
+ "FOREIGN KEY(attribute_type_id) REFERENCES blackboard_attribute_types(attribute_type_id))");
}
private void createAnalysisResultsTables(Statement stmt) throws SQLException {
stmt.execute("CREATE TABLE tsk_analysis_results (artifact_obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, "
+ "conclusion TEXT, "
+ "significance INTEGER NOT NULL, "
+ "priority INTEGER NOT NULL, "
+ "configuration TEXT, justification TEXT, "
+ "ignore_score INTEGER DEFAULT 0, " // boolean
+ "FOREIGN KEY(artifact_obj_id) REFERENCES blackboard_artifacts(artifact_obj_id) ON DELETE CASCADE"
+ ")");
stmt.execute("CREATE TABLE tsk_aggregate_score( obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, "
+ "data_source_obj_id " + dbQueryHelper.getBigIntType() + ", "
+ "significance INTEGER NOT NULL, "
+ "priority INTEGER NOT NULL, "
+ "UNIQUE (obj_id),"
+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE "
+ ")");
}
private void createTagTables(Statement stmt) throws SQLException {
stmt.execute("CREATE TABLE tsk_tag_sets (tag_set_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, name TEXT UNIQUE)");
stmt.execute("CREATE TABLE tag_names (tag_name_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, display_name TEXT UNIQUE, "
+ "description TEXT NOT NULL, color TEXT NOT NULL, knownStatus INTEGER NOT NULL,"
+ " tag_set_id " + dbQueryHelper.getBigIntType() + ", rank INTEGER, FOREIGN KEY(tag_set_id) REFERENCES tsk_tag_sets(tag_set_id) ON DELETE SET NULL)");
stmt.execute("CREATE TABLE tsk_examiners (examiner_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "login_name TEXT NOT NULL, display_name TEXT, UNIQUE(login_name))");
stmt.execute("CREATE TABLE content_tags (tag_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, tag_name_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "comment TEXT NOT NULL, begin_byte_offset " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "end_byte_offset " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "examiner_id " + dbQueryHelper.getBigIntType() + ", "
+ "FOREIGN KEY(examiner_id) REFERENCES tsk_examiners(examiner_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(tag_name_id) REFERENCES tag_names(tag_name_id) ON DELETE CASCADE)");
stmt.execute("CREATE TABLE blackboard_artifact_tags (tag_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "artifact_id " + dbQueryHelper.getBigIntType() + " NOT NULL, tag_name_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "comment TEXT NOT NULL, examiner_id " + dbQueryHelper.getBigIntType() + ", "
+ "FOREIGN KEY(examiner_id) REFERENCES tsk_examiners(examiner_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(artifact_id) REFERENCES blackboard_artifacts(artifact_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(tag_name_id) REFERENCES tag_names(tag_name_id) ON DELETE CASCADE)");
}
/**
* Add indexes
*
* @param conn the database connection
* @throws TskCoreException
*/
private void addIndexes(Connection conn) throws TskCoreException {
try (Statement stmt = conn.createStatement()) {
// tsk_objects index
stmt.execute("CREATE INDEX parObjId ON tsk_objects(par_obj_id)");
// file layout index
stmt.execute("CREATE INDEX layout_objID ON tsk_file_layout(obj_id)");
// blackboard indexes
stmt.execute("CREATE INDEX artifact_objID ON blackboard_artifacts(obj_id)");
stmt.execute("CREATE INDEX artifact_artifact_objID ON blackboard_artifacts(artifact_obj_id)");
stmt.execute("CREATE INDEX artifact_typeID ON blackboard_artifacts(artifact_type_id)");
stmt.execute("CREATE INDEX attrsArtifactID ON blackboard_attributes(artifact_id)");
//file type indexes
stmt.execute("CREATE INDEX mime_type ON tsk_files(dir_type,mime_type,type)");
stmt.execute("CREATE INDEX file_extension ON tsk_files(extension)");
// account indexes
stmt.execute("CREATE INDEX relationships_account1 ON account_relationships(account1_id)");
stmt.execute("CREATE INDEX relationships_account2 ON account_relationships(account2_id)");
stmt.execute("CREATE INDEX relationships_relationship_source_obj_id ON account_relationships(relationship_source_obj_id)");
stmt.execute("CREATE INDEX relationships_date_time ON account_relationships(date_time)");
stmt.execute("CREATE INDEX relationships_relationship_type ON account_relationships(relationship_type)");
stmt.execute("CREATE INDEX relationships_data_source_obj_id ON account_relationships(data_source_obj_id)");
//tsk_events indices
stmt.execute("CREATE INDEX events_data_source_obj_id ON tsk_event_descriptions(data_source_obj_id)");
stmt.execute("CREATE INDEX events_content_obj_id ON tsk_event_descriptions(content_obj_id)");
stmt.execute("CREATE INDEX events_artifact_id ON tsk_event_descriptions(artifact_id)");
stmt.execute("CREATE INDEX events_sub_type_time ON tsk_events(event_type_id, time)");
stmt.execute("CREATE INDEX events_time ON tsk_events(time)");
// analysis results and scores indices
stmt.execute("CREATE INDEX score_significance_priority ON tsk_aggregate_score(significance, priority)");
stmt.execute("CREATE INDEX score_datasource_obj_id ON tsk_aggregate_score(data_source_obj_id)");
stmt.execute("CREATE INDEX tsk_file_attributes_obj_id ON tsk_file_attributes(obj_id)");
} catch (SQLException ex) {
throw new TskCoreException("Error initializing db_info tables", ex);
}
}
private void createIngestTables(Statement stmt) throws SQLException {
stmt.execute("CREATE TABLE ingest_module_types (type_id INTEGER PRIMARY KEY, type_name TEXT NOT NULL)");
stmt.execute("CREATE TABLE ingest_job_status_types (type_id INTEGER PRIMARY KEY, type_name TEXT NOT NULL)");
stmt.execute("CREATE TABLE ingest_modules (ingest_module_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "display_name TEXT NOT NULL, unique_name TEXT UNIQUE NOT NULL, type_id INTEGER NOT NULL, "
+ "version TEXT NOT NULL, FOREIGN KEY(type_id) REFERENCES ingest_module_types(type_id) ON DELETE CASCADE);");
stmt.execute("CREATE TABLE ingest_jobs (ingest_job_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, host_name TEXT NOT NULL, "
+ "start_date_time " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "end_date_time " + dbQueryHelper.getBigIntType() + " NOT NULL, status_id INTEGER NOT NULL, "
+ "settings_dir TEXT, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(status_id) REFERENCES ingest_job_status_types(type_id) ON DELETE CASCADE);");
stmt.execute("CREATE TABLE ingest_job_modules (ingest_job_id INTEGER, ingest_module_id INTEGER, "
+ "pipeline_position INTEGER, PRIMARY KEY(ingest_job_id, ingest_module_id), "
+ "FOREIGN KEY(ingest_job_id) REFERENCES ingest_jobs(ingest_job_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(ingest_module_id) REFERENCES ingest_modules(ingest_module_id) ON DELETE CASCADE);");
}
private void createHostTables(Statement stmt) throws SQLException {
stmt.execute("CREATE TABLE tsk_persons (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "name TEXT NOT NULL, " // person name
+ "UNIQUE(name)) ");
// References tsk_persons
stmt.execute("CREATE TABLE tsk_hosts (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "name TEXT NOT NULL, " // host name
+ "db_status INTEGER DEFAULT 0, " // active/merged/deleted
+ "person_id INTEGER, "
+ "merged_into " + dbQueryHelper.getBigIntType() + ", "
+ "FOREIGN KEY(person_id) REFERENCES tsk_persons(id) ON DELETE SET NULL, "
+ "FOREIGN KEY(merged_into) REFERENCES tsk_hosts(id) ON DELETE CASCADE, "
+ "UNIQUE(name)) ");
stmt.execute("CREATE TABLE tsk_host_addresses (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "address_type INTEGER NOT NULL, "
+ "address TEXT NOT NULL, "
+ "UNIQUE(address_type, address)) ");
stmt.execute("CREATE TABLE tsk_host_address_map (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "host_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "addr_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "source_obj_id " + dbQueryHelper.getBigIntType() + ", " // object id of the source where this mapping was found.
+ "time " + dbQueryHelper.getBigIntType() + ", " // time at which the mapping existed
+ "UNIQUE(host_id, addr_obj_id, time), "
+ "FOREIGN KEY(host_id) REFERENCES tsk_hosts(id) ON DELETE CASCADE, "
+ "FOREIGN KEY(addr_obj_id) REFERENCES tsk_host_addresses(id), "
+ "FOREIGN KEY(source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE SET NULL )");
// stores associations between DNS name and IP address
stmt.execute("CREATE TABLE tsk_host_address_dns_ip_map (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "dns_address_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "ip_address_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "source_obj_id " + dbQueryHelper.getBigIntType() + ", "
+ "time " + dbQueryHelper.getBigIntType() + ", " // time at which the mapping existed
+ "UNIQUE(dns_address_id, ip_address_id, time), "
+ "FOREIGN KEY(dns_address_id) REFERENCES tsk_host_addresses(id) ON DELETE CASCADE, "
+ "FOREIGN KEY(ip_address_id) REFERENCES tsk_host_addresses(id) ON DELETE CASCADE,"
+ "FOREIGN KEY(source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE SET NULL )");
// maps an address to an content/item using it
stmt.execute("CREATE TABLE tsk_host_address_usage (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "addr_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " // obj id of the content/item using the address
+ "data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " // data source where the usage was found
+ "UNIQUE(addr_obj_id, obj_id), "
+ "FOREIGN KEY(addr_obj_id) REFERENCES tsk_host_addresses(id) ON DELETE CASCADE, "
+ "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE )");
}
// Must be called after tsk_persons, tsk_hosts and tsk_objects have been created.
private void createAccountTables(Statement stmt) throws SQLException {
stmt.execute("CREATE TABLE account_types (account_type_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "type_name TEXT UNIQUE NOT NULL, display_name TEXT NOT NULL)");
// References account_types
stmt.execute("CREATE TABLE accounts (account_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "account_type_id INTEGER NOT NULL, account_unique_identifier TEXT NOT NULL, "
+ "UNIQUE(account_type_id, account_unique_identifier), "
+ "FOREIGN KEY(account_type_id) REFERENCES account_types(account_type_id))");
// References accounts, tsk_objects
stmt.execute("CREATE TABLE account_relationships (relationship_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "account1_id INTEGER NOT NULL, account2_id INTEGER NOT NULL, "
+ "relationship_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "date_time " + dbQueryHelper.getBigIntType() + ", relationship_type INTEGER NOT NULL, "
+ "data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "UNIQUE(account1_id, account2_id, relationship_source_obj_id), "
+ "FOREIGN KEY(account1_id) REFERENCES accounts(account_id), "
+ "FOREIGN KEY(account2_id) REFERENCES accounts(account_id), "
+ "FOREIGN KEY(relationship_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
// References tsk_hosts
stmt.execute("CREATE TABLE tsk_os_account_realms (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "realm_name TEXT DEFAULT NULL, " // realm name - for a domain realm, may be null
+ "realm_addr TEXT DEFAULT NULL, " // a sid/uid or some some other identifier, may be null
+ "realm_signature TEXT NOT NULL, " // Signature exists only to prevent duplicates. It is made up of realm address/name and scope host
+ "scope_host_id " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, " // if the realm scope is a single host
+ "scope_confidence INTEGER, " // indicates whether we know for sure the realm scope or if we are inferring it
+ "db_status INTEGER DEFAULT 0, " // active/merged/deleted
+ "merged_into " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, "
+ "UNIQUE(realm_signature), "
+ "FOREIGN KEY(scope_host_id) REFERENCES tsk_hosts(id) ON DELETE CASCADE,"
+ "FOREIGN KEY(merged_into) REFERENCES tsk_os_account_realms(id) ON DELETE CASCADE )");
// References tsk_objects, tsk_os_account_realms, tsk_persons
stmt.execute("CREATE TABLE tsk_os_accounts (os_account_obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, "
+ "login_name TEXT DEFAULT NULL, " // login name, if available, may be null
+ "full_name TEXT DEFAULT NULL, " // full name, if available, may be null
+ "realm_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " // realm for the account
+ "addr TEXT DEFAULT NULL, " // SID/UID, if available
+ "signature TEXT NOT NULL, " // This exists only to prevent duplicates. It is either the addr or the login_name whichever is not null.
+ "status INTEGER, " // enabled/disabled/deleted
+ "type INTEGER, " // service/interactive
+ "created_date " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, "
+ "db_status INTEGER DEFAULT 0, " // active/merged/deleted
+ "merged_into " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, "
+ "UNIQUE(signature, realm_id), "
+ "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(realm_id) REFERENCES tsk_os_account_realms(id) ON DELETE CASCADE,"
+ "FOREIGN KEY(merged_into) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE )");
}
// Must be called after createAccountTables() and blackboard_attribute_types, blackboard_artifacts creation.
private void createAccountInstancesAndArtifacts(Statement stmt) throws SQLException {
// References tsk_os_accounts, tsk_hosts, tsk_objects, blackboard_attribute_types
stmt.execute("CREATE TABLE tsk_os_account_attributes (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "os_account_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "host_id " + dbQueryHelper.getBigIntType() + ", "
+ "source_obj_id " + dbQueryHelper.getBigIntType() + ", "
+ "attribute_type_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "value_type INTEGER NOT NULL, "
+ "value_byte " + dbQueryHelper.getBlobType() + ", "
+ "value_text TEXT, "
+ "value_int32 INTEGER, value_int64 " + dbQueryHelper.getBigIntType() + ", "
+ "value_double NUMERIC(20, 10), "
+ "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(host_id) REFERENCES tsk_hosts(id) ON DELETE CASCADE, "
+ "FOREIGN KEY(source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE SET NULL, "
+ "FOREIGN KEY(attribute_type_id) REFERENCES blackboard_attribute_types(attribute_type_id))");
// References tsk_os_accounts, tsk_objects, tsk_hosts
stmt.execute("CREATE TABLE tsk_os_account_instances (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "os_account_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "instance_type INTEGER NOT NULL, " // PerformedActionOn/ReferencedOn
+ "UNIQUE(os_account_obj_id, data_source_obj_id, instance_type), "
+ "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE ) ");
// References blackboard_artifacts, tsk_os_accounts
stmt.execute("CREATE TABLE tsk_data_artifacts ( "
+ "artifact_obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, "
+ "os_account_obj_id " + dbQueryHelper.getBigIntType() + ", "
+ "FOREIGN KEY(artifact_obj_id) REFERENCES blackboard_artifacts(artifact_obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE SET NULL) ");
}
private void createEventTables(Statement stmt) throws SQLException {
stmt.execute("CREATE TABLE tsk_event_types ("
+ " event_type_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY,"
+ " display_name TEXT UNIQUE NOT NULL , "
+ " super_type_id INTEGER REFERENCES tsk_event_types(event_type_id) )");
/*
* Regarding the timeline event tables schema, note that several columns
* in the tsk_event_descriptions table seem, at first glance, to be
* attributes of events rather than their descriptions and would appear
* to belong in tsk_events table instead. The rationale for putting the
* data source object ID, content object ID, artifact ID and the flags
* indicating whether or not the event source has a hash set hit or is
* tagged were motivated by the fact that these attributes are identical
* for each event in a set of file system file MAC time events. The
* decision was made to avoid duplication and save space by placing this
* data in the tsk_event-descriptions table.
*/
stmt.execute(
"CREATE TABLE tsk_event_descriptions ( "
+ " event_description_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ " full_description TEXT NOT NULL, "
+ " med_description TEXT, "
+ " short_description TEXT,"
+ " data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ " content_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ " artifact_id " + dbQueryHelper.getBigIntType() + ", "
+ " hash_hit INTEGER NOT NULL, " //boolean
+ " tagged INTEGER NOT NULL, " //boolean
+ " FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id) ON DELETE CASCADE, "
+ " FOREIGN KEY(content_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
+ " FOREIGN KEY(artifact_id) REFERENCES blackboard_artifacts(artifact_id) ON DELETE CASCADE,"
+ " UNIQUE (full_description, content_obj_id, artifact_id))");
stmt.execute(
"CREATE TABLE tsk_events ("
+ " event_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ " event_type_id " + dbQueryHelper.getBigIntType() + " NOT NULL REFERENCES tsk_event_types(event_type_id) ,"
+ " event_description_id " + dbQueryHelper.getBigIntType() + " NOT NULL REFERENCES tsk_event_descriptions(event_description_id) ON DELETE CASCADE ,"
+ " time " + dbQueryHelper.getBigIntType() + " NOT NULL , "
+ " UNIQUE (event_type_id, event_description_id, time))");
}
private void createAttributeTables(Statement stmt) throws SQLException {
/*
* Binary representation of BYTEA is a bunch of bytes, which could
* include embedded nulls so we have to pay attention to field length.
* http://www.postgresql.org/docs/9.4/static/libpq-example.html
*/
stmt.execute("CREATE TABLE tsk_file_attributes ( id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, "
+ "obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "attribute_type_id " + dbQueryHelper.getBigIntType() + " NOT NULL, "
+ "value_type INTEGER NOT NULL, value_byte " + dbQueryHelper.getBlobType() + ", "
+ "value_text TEXT, value_int32 INTEGER, value_int64 " + dbQueryHelper.getBigIntType() + ", value_double NUMERIC(20, 10), "
+ "FOREIGN KEY(obj_id) REFERENCES tsk_files(obj_id) ON DELETE CASCADE, "
+ "FOREIGN KEY(attribute_type_id) REFERENCES blackboard_attribute_types(attribute_type_id))");
}
/**
* Helper class for holding code unique to each database type.
*/
private abstract class DbCreationHelper {
/**
* Create the database itself (if necessary)
*
* @throws TskCoreException
*/
abstract void createDatabase() throws TskCoreException;
/**
* Get an connection to the case database
*
* @return the connection
*/
abstract Connection getConnection() throws TskCoreException;
/**
* Do any needed initialization before creating the tables.
* This is where SQLite pragmas are set up.
*
* @param conn The database connection
*
* @throws TskCoreException
*/
abstract void performPreInitialization(Connection conn) throws TskCoreException;
/**
* Do any additional steps after the tables are created.
*
* @param conn The database connection
* @throws TskCoreException
*/
abstract void performPostTableInitialization(Connection conn) throws TskCoreException;
}
/**
* Implements the PostgreSQL-specific methods for creating the case
*/
private class PostgreSQLDbCreationHelper extends DbCreationHelper {
private final static String JDBC_BASE_URI = "jdbc:postgresql://"; // NON-NLS
private final static String JDBC_DRIVER = "org.postgresql.Driver"; // NON-NLS
final private String caseName;
final private CaseDbConnectionInfo info;
PostgreSQLDbCreationHelper(String caseName, CaseDbConnectionInfo info) {
this.caseName = caseName;
this.info = info;
}
@Override
void createDatabase() throws TskCoreException{
try(Connection conn = getPostgresConnection();
Statement stmt = conn.createStatement()) {
stmt.execute("CREATE DATABASE \"" + caseName + "\" WITH ENCODING='UTF8'");
} catch (SQLException ex) {
throw new TskCoreException("Error creating PostgreSQL case " + caseName, ex);
}
}
@Override
Connection getConnection() throws TskCoreException {
return getConnection(caseName);
}
/**
* Connects to the "postgres" database for creating new databases.
*
* @return the connection to the "postgres" database
*/
Connection getPostgresConnection() throws TskCoreException {
return getConnection("postgres");
}
/**
* Connects to an existing database with the given name.
*
* @param databaseName the name of the database
*
* @return the connection to the database
*/
Connection getConnection(String databaseName) throws TskCoreException {
String encodedDbName;
try {
encodedDbName = URLEncoder.encode(databaseName, "UTF-8");
} catch (UnsupportedEncodingException ex) {
// Print the warning and continue with the unencoded name
logger.log(Level.WARNING, "Error encoding database name " + databaseName, ex);
encodedDbName = databaseName;
}
StringBuilder url = new StringBuilder();
url.append(JDBC_BASE_URI)
.append(info.getHost())
.append(":")
.append(info.getPort())
.append('/') // NON-NLS
.append(encodedDbName);
if (info.isSslEnabled()) {
if (info.isSslVerify()) {
if (info.getCustomSslValidationClassName().isBlank()) {
url.append(SSL_VERIFY_DEFAULT_URL);
} else {
// use custom SSL certificate validation class
url.append(getCustomPostrgesSslVerificationUrl(info.getCustomSslValidationClassName()));
}
} else {
url.append(SSL_NONVERIFY_URL);
}
}
Connection conn;
try {
Properties props = new Properties();
props.setProperty("user", info.getUserName()); // NON-NLS
props.setProperty("password", info.getPassword()); // NON-NLS
Class.forName(JDBC_DRIVER);
conn = DriverManager.getConnection(url.toString(), props);
} catch (ClassNotFoundException | SQLException ex) {
throw new TskCoreException("Failed to acquire ephemeral connection to PostgreSQL database " + databaseName, ex); // NON-NLS
}
return conn;
}
@Override
void performPreInitialization(Connection conn) throws TskCoreException {
// Nothing to do here for PostgreSQL
}
@Override
void performPostTableInitialization(Connection conn) throws TskCoreException {
try (Statement stmt = conn.createStatement()) {
stmt.execute("ALTER SEQUENCE blackboard_artifacts_artifact_id_seq minvalue -9223372036854775808 restart with -9223372036854775808");
} catch (SQLException ex) {
throw new TskCoreException("Error altering artifact ID sequence", ex);
}
}
}
/**
* Implements the SQLite-specific methods for creating the case
*/
private class SQLiteDbCreationHelper extends DbCreationHelper {
private final static String PRAGMA_SYNC_OFF = "PRAGMA synchronous = OFF"; // NON-NLS
private final static String PRAGMA_READ_UNCOMMITTED_TRUE = "PRAGMA read_uncommitted = True"; // NON-NLS
private final static String PRAGMA_ENCODING_UTF8 = "PRAGMA encoding = 'UTF-8'"; // NON-NLS
private final static String PRAGMA_PAGE_SIZE_4096 = "PRAGMA page_size = 4096"; // NON-NLS
private final static String PRAGMA_FOREIGN_KEYS_ON = "PRAGMA foreign_keys = ON"; // NON-NLS
private final static String JDBC_DRIVER = "org.sqlite.JDBC"; // NON-NLS
private final static String JDBC_BASE_URI = "jdbc:sqlite:"; // NON-NLS
String dbPath;
SQLiteDbCreationHelper(String dbPath) {
this.dbPath = dbPath;
}
@Override
void createDatabase() throws TskCoreException {
// SQLite doesn't need to explicitly create the case database but we will
// check that the folder exists and the database does not
File dbFile = new File(dbPath);
if (dbFile.exists()) {
throw new TskCoreException("Case database already exists : " + dbPath);
}
if (dbFile.getParentFile() != null && !dbFile.getParentFile().exists()) {
throw new TskCoreException("Case database folder does not exist : " + dbFile.getParent());
}
}
@Override
Connection getConnection() throws TskCoreException {
StringBuilder url = new StringBuilder();
url.append(JDBC_BASE_URI)
.append(dbPath);
Connection conn;
try {
Class.forName(JDBC_DRIVER);
conn = DriverManager.getConnection(url.toString());
} catch (ClassNotFoundException | SQLException ex) {
throw new TskCoreException("Failed to acquire ephemeral connection SQLite database " + dbPath, ex); // NON-NLS
}
return conn;
}
@Override
void performPreInitialization(Connection conn) throws TskCoreException {
try (Statement stmt = conn.createStatement()) {
stmt.execute(PRAGMA_SYNC_OFF);
stmt.execute(PRAGMA_READ_UNCOMMITTED_TRUE);
stmt.execute(PRAGMA_ENCODING_UTF8);
stmt.execute(PRAGMA_PAGE_SIZE_4096);
stmt.execute(PRAGMA_FOREIGN_KEYS_ON);
} catch (SQLException ex) {
throw new TskCoreException("Error setting pragmas", ex);
}
}
@Override
void performPostTableInitialization(Connection conn) throws TskCoreException {
// Nothing to do here for SQLite
}
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2018-2019 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.datamodel;
import com.google.common.annotations.Beta;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.MessageFormat;
import java.sql.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
import static org.sleuthkit.datamodel.SleuthkitCase.closeStatement;
import org.sleuthkit.datamodel.TskData.DbType;
/**
* This class provides modules with access to the case database
* to create custom tables/indexes and to query them.
*
*/
public final class CaseDbAccessManager {
/**
* Callback interface to process the result of DB query run through DBAccessManager
*/
public interface CaseDbAccessQueryCallback {
/**
* Processes the ResultSet from CaseDbAccessManager query.
*
* This is called synchronously by CaseDbAccessManager,
* and should avoid any long running operations.
*
* @param resultSet ResultSet from query.
*/
void process(ResultSet resultSet);
}
private static final Logger logger = Logger.getLogger(CaseDbAccessManager.class.getName());
private final SleuthkitCase tskDB;
/**
* Constructor
*
* @param skCase The SleuthkitCase
*
*/
CaseDbAccessManager(SleuthkitCase skCase) {
this.tskDB = skCase;
}
/**
* Checks if a column exists in a table.
*
* @param tableName name of the table
* @param columnName column name to check
*
* @return true if the column already exists, false otherwise
* @throws TskCoreException
*/
public boolean columnExists(String tableName, String columnName) throws TskCoreException {
boolean doesColumnExists = false;
CaseDbTransaction localTrans = tskDB.beginTransaction();
try {
doesColumnExists = columnExists(tableName, columnName, localTrans);
localTrans.commit();
localTrans = null;
}
finally {
if (null != localTrans) {
try {
localTrans.rollback();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex);
}
}
}
return doesColumnExists;
}
/**
* Checks if a column exists in a table.
*
* @param tableName name of the table
* @param columnName column name to check
* @param transaction transaction
*
* @return true if the column already exists, false otherwise
* @throws TskCoreException
*/
public boolean columnExists(String tableName, String columnName, CaseDbTransaction transaction) throws TskCoreException {
boolean columnExists = false;
Statement statement = null;
ResultSet resultSet = null;
try {
CaseDbConnection connection = transaction.getConnection();
statement = connection.createStatement();
if (DbType.SQLITE == tskDB.getDatabaseType()) {
String tableInfoQuery = "PRAGMA table_info(%s)"; //NON-NLS
resultSet = statement.executeQuery(String.format(tableInfoQuery, tableName));
while (resultSet.next()) {
if (resultSet.getString("name").equalsIgnoreCase(columnName)) {
columnExists = true;
break;
}
}
}
else {
String tableInfoQueryTemplate = "SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='%s' AND column_name='%s')"; //NON-NLS
resultSet = statement.executeQuery(String.format(tableInfoQueryTemplate, tableName.toLowerCase(), columnName.toLowerCase()));
if (resultSet.next()) {
columnExists = resultSet.getBoolean(1);
}
}
}
catch (SQLException ex) {
throw new TskCoreException("Error checking if column " + columnName + "exists ", ex);
}
finally {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException ex2) {
logger.log(Level.WARNING, "Failed to to close resultset after checking column", ex2);
}
}
closeStatement(statement);
}
return columnExists;
}
/**
* Checks if a table exists in the case database.
*
* @param tableName name of the table to check
*
* @return true if the table already exists, false otherwise
* @throws TskCoreException
*/
public boolean tableExists(String tableName) throws TskCoreException {
boolean doesTableExist = false;
CaseDbTransaction localTrans = tskDB.beginTransaction();
try {
doesTableExist = tableExists(tableName, localTrans);
localTrans.commit();
localTrans = null;
}
finally {
if (null != localTrans) {
try {
localTrans.rollback();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex); //NON-NLS
}
}
}
return doesTableExist;
}
/**
* Checks if a table exists in the case database.
*
* @param tableName name of the table to check
* @param transaction transaction
*
* @return true if the table already exists, false otherwise
* @throws TskCoreException
*/
public boolean tableExists(String tableName, CaseDbTransaction transaction) throws TskCoreException {
boolean tableExists = false;
Statement statement = null;
ResultSet resultSet = null;
try {
CaseDbConnection connection = transaction.getConnection();
statement = connection.createStatement();
if (DbType.SQLITE == tskDB.getDatabaseType()) {
resultSet = statement.executeQuery("SELECT name FROM sqlite_master WHERE type='table'"); //NON-NLS
while (resultSet.next()) {
if (resultSet.getString("name").equalsIgnoreCase(tableName)) { //NON-NLS
tableExists = true;
break;
}
}
}
else {
String tableInfoQueryTemplate = "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name='%s')"; //NON-NLS
resultSet = statement.executeQuery(String.format(tableInfoQueryTemplate, tableName.toLowerCase()));
if (resultSet.next()) {
tableExists = resultSet.getBoolean(1);
}
}
}
catch (SQLException ex) {
throw new TskCoreException("Error checking if table " + tableName + "exists ", ex);
} finally {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException ex2) {
logger.log(Level.WARNING, "Failed to to close resultset after checking table", ex2);
}
}
closeStatement(statement);
}
return tableExists;
}
/**
* Creates a table with the specified name and schema.
*
* If the table already exists, it does nothing, and no error is generated
*
* It is recommended that clients of the API use module specific prefixes
* to prevent name collisions.
*
* @param tableName name of the table to create
* @param tableSchema table schema
*
* @throws TskCoreException
*/
public void createTable(final String tableName, final String tableSchema) throws TskCoreException {
validateTableName(tableName);
validateSQL(tableSchema);
tskDB.acquireSingleUserCaseWriteLock();
String createSQL = "CREATE TABLE IF NOT EXISTS " + tableName + " " + tableSchema;
try (CaseDbConnection connection = tskDB.getConnection();
Statement statement = connection.createStatement();) {
statement.execute(createSQL);
} catch (SQLException ex) {
throw new TskCoreException("Error creating table " + tableName, ex);
} finally {
tskDB.releaseSingleUserCaseWriteLock();
}
}
/**
* Alters a table with the specified name.
*
* @param tableName name of the table to alter
* @param alterSQL SQL to alter the table
*
* @throws TskCoreException
*/
public void alterTable(final String tableName, final String alterSQL) throws TskCoreException {
CaseDbTransaction localTrans = tskDB.beginTransaction();
try {
alterTable(tableName, alterSQL, localTrans);
localTrans.commit();
localTrans = null;
} finally {
if (null != localTrans) {
try {
localTrans.rollback();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex);
}
}
}
}
/**
* Alters a table with the specified name.
*
* @param tableName name of the table to alter
* @param alterSQL SQL to alter the table
* @param transaction transaction
*
* @throws TskCoreException
*/
public void alterTable(final String tableName, final String alterSQL, final CaseDbTransaction transaction) throws TskCoreException {
validateTableName(tableName);
validateSQL(alterSQL);
CaseDbConnection connection = transaction.getConnection();
Statement statement = null;
String sql = "ALTER TABLE " + tableName + " " + alterSQL;
try {
statement = connection.createStatement();
statement.execute(sql);
} catch (SQLException ex) {
// SQLite occasionally returns false for columnExists() if a table was just created with that column
// leading to "duplicate column name" exception.
// We ignore this exception
if (DbType.SQLITE == tskDB.getDatabaseType() &&
alterSQL.toLowerCase().contains("add column") &&
ex.getMessage().toLowerCase().contains("duplicate column name")) {
logger.log(Level.WARNING, String.format("Column being added by SQL = %s already exists in table %s", alterSQL, tableName));
return;
}
throw new TskCoreException(String.format("Error altering table %s with SQL = %s", tableName, sql), ex);
} finally {
closeStatement(statement);
}
}
/**
* Creates an index on the specified table, on specified column(s).
*
* If the index already exists, it does nothing, and no error is generated.
*
* It is recommended that clients of the API use module specific prefixes
* to prevent name collisions.
*
* @param indexName name of index to create
* @param tableName name of table to create the index on
* @param colsSQL - columns on which to index
*
* @throws TskCoreException
*/
public void createIndex(final String indexName, final String tableName, final String colsSQL) throws TskCoreException {
validateTableName(tableName);
validateIndexName(indexName);
validateSQL(colsSQL);
tskDB.acquireSingleUserCaseWriteLock();
String indexSQL = "CREATE INDEX IF NOT EXISTS " + indexName + " ON " + tableName + " " + colsSQL; // NON-NLS
try (CaseDbConnection connection = tskDB.getConnection();
Statement statement = connection.createStatement(); ) {
statement.execute(indexSQL);
} catch (SQLException ex) {
throw new TskCoreException("Error creating index " + tableName, ex);
} finally {
tskDB.releaseSingleUserCaseWriteLock();
}
}
/**
* Inserts a row in the specified table.
*
* @param tableName - table to insert into.
* @param sql - SQL string specifying column values.
*
* @return - rowID of the row
*
* @throws TskCoreException
*/
public long insert(final String tableName, final String sql) throws TskCoreException {
CaseDbTransaction localTrans = tskDB.beginTransaction();
try {
long rowId = insert(tableName, sql, localTrans);
localTrans.commit();
localTrans = null;
return rowId;
} finally {
if (null != localTrans) {
try {
localTrans.rollback();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex);
}
}
}
}
/**
* Inserts a row in the specified table, as part of the specified transaction.
* If the primary key is duplicate, it does nothing.
*
* Note: For PostGreSQL, the caller must include the ON CONFLICT DO NOTHING clause
*
* Caller is responsible for committing the transaction.
*
* @param tableName - table to insert into.
* @param sql - SQL string specifying column values.
* @param transaction transaction in which the insert/update is done
*
* @return - rowID of the row inserted
*
* @throws TskCoreException
*/
public long insert(final String tableName, final String sql, final CaseDbTransaction transaction) throws TskCoreException {
long rowId = 0;
validateTableName(tableName);
validateSQL(sql);
CaseDbConnection connection = transaction.getConnection();
PreparedStatement statement = null;
ResultSet resultSet;
String insertSQL = "INSERT";
if (DbType.SQLITE == tskDB.getDatabaseType()) {
insertSQL += " OR IGNORE";
}
insertSQL = insertSQL+ " INTO " + tableName + " " + sql; // NON-NLS
try {
statement = connection.prepareStatement(insertSQL, Statement.RETURN_GENERATED_KEYS);
connection.executeUpdate(statement);
resultSet = statement.getGeneratedKeys();
if (resultSet.next()) {
rowId = resultSet.getLong(1); //last_insert_rowid()
}
} catch (SQLException ex) {
throw new TskCoreException("Error inserting row in table " + tableName + " with sql = "+ insertSQL, ex);
} finally {
closeStatement(statement);
}
return rowId;
}
/**
* Inserts a row in the specified table.
* If the primary key is duplicate, the existing row is updated.
*
* Note: For PostGreSQL, the caller must include the ON CONFLICT UPDATE clause to handle
* duplicates
*
* @param tableName - table to insert into.
* @param sql - SQL string specifying column values.
*
* @return - rowID of the row inserted/updated
*
* @throws TskCoreException
*/
public long insertOrUpdate(final String tableName, final String sql) throws TskCoreException {
CaseDbTransaction localTrans = tskDB.beginTransaction();
try {
long rowId = insertOrUpdate(tableName, sql, localTrans);
localTrans.commit();
localTrans = null;
return rowId;
} finally {
if (null != localTrans) {
try {
localTrans.rollback();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex);
}
}
}
}
/**
* Inserts a row in the specified table, as part of the specified transaction.
* If the primary key is duplicate, the existing row is updated.
* Caller is responsible for committing the transaction.
*
* Note: For PostGreSQL, the caller must include the ON CONFLICT UPDATE clause to handle
* duplicates
*
* @param tableName - table to insert into.
* @param sql - SQL string specifying column values.
* @param transaction transaction in which the insert/update is done
*
* @return - rowID of the row inserted/updated
*
* @throws TskCoreException
*/
public long insertOrUpdate(final String tableName, final String sql, final CaseDbTransaction transaction) throws TskCoreException {
long rowId = 0;
validateTableName(tableName);
validateSQL(sql);
CaseDbConnection connection = transaction.getConnection();
PreparedStatement statement = null;
ResultSet resultSet;
String insertSQL = "INSERT";
if (DbType.SQLITE == tskDB.getDatabaseType()) {
insertSQL += " OR REPLACE";
}
insertSQL += " INTO " + tableName + " " + sql; // NON-NLS
try {
statement = connection.prepareStatement(insertSQL, Statement.RETURN_GENERATED_KEYS);
connection.executeUpdate(statement);
resultSet = statement.getGeneratedKeys();
resultSet.next();
rowId = resultSet.getLong(1); //last_insert_rowid()
} catch (SQLException ex) {
throw new TskCoreException("Error inserting row in table " + tableName + " with sql = "+ insertSQL, ex);
} finally {
closeStatement(statement);
}
return rowId;
}
/**
* Creates a prepared statement object for the purposes of running an update
* statement. The given SQL should not include the starting "UPDATE"
* or the name of the table.
*
* @param tableName The name of the table being updated.
* @param sql The insert statement without the starting "UPDATE (table name)" part.
* @param trans The open transaction.
*
* @return The prepared statement object.
*
* @throws TskCoreException
*/
@Beta
public CaseDbPreparedStatement prepareUpdate(String tableName, String sql, CaseDbTransaction trans) throws TskCoreException {
validateTableName(tableName);
validateSQL(sql);
String updateSQL = "UPDATE " + tableName + " " + sql; // NON-NLS
try {
return new CaseDbPreparedStatement(StatementType.UPDATE, updateSQL, trans);
} catch (SQLException ex) {
throw new TskCoreException("Error creating update prepared statement for query:\n" + updateSQL, ex);
}
}
/**
* Performs an update statement query with the given case prepared statement.
*
* @param preparedStatement The case prepared statement.
*
* @throws TskCoreException
*/
@Beta
public void update(CaseDbPreparedStatement preparedStatement) throws TskCoreException {
if (!preparedStatement.getType().equals(StatementType.UPDATE)) {
throw new TskCoreException("CaseDbPreparedStatement has incorrect type for update operation");
}
try {
preparedStatement.getStatement().executeUpdate();
} catch (SQLException ex) {
throw new TskCoreException("Error updating row in table " + "" + " with sql = "+ "", ex);
}
}
/**
* Updates row(s) in the specified table.
*
* @param tableName - table to insert into.
* @param sql - SQL string specifying column values and conditions.
*
* @throws TskCoreException
*/
public void update(final String tableName, final String sql) throws TskCoreException {
CaseDbTransaction localTrans = tskDB.beginTransaction();
try {
update(tableName, sql, localTrans);
localTrans.commit();
localTrans = null;
} finally {
if (null != localTrans) {
try {
localTrans.rollback();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex);
}
}
}
}
/**
* Updates row(s) in the specified table, as part of the specified transaction.
* Caller is responsible for committing the transaction.
*
* @param tableName - table to insert into.
* @param sql - SQL string specifying column values and conditions.
* @param transaction - transaction under which the update is performed.
*
* @throws TskCoreException
*/
public void update(final String tableName, final String sql, CaseDbTransaction transaction ) throws TskCoreException {
validateTableName(tableName);
validateSQL(sql);
CaseDbConnection connection = transaction.getConnection();
Statement statement = null;
String updateSQL = "UPDATE " + tableName + " " + sql; // NON-NLS
try {
statement = connection.createStatement();
statement.executeUpdate(updateSQL);
} catch (SQLException ex) {
throw new TskCoreException("Error Updating table " + tableName, ex);
} finally {
closeStatement(statement);
}
}
/**
* Runs the specified SELECT query and then calls the specified callback with the result.
*
* @param sql SQL string specifying the columns to select, tables to select from and the WHERE clause.
* @param queryCallback Callback object to process the result.
*
* @throws TskCoreException
*/
public void select(final String sql, final CaseDbAccessQueryCallback queryCallback) throws TskCoreException {
if (queryCallback == null) {
throw new TskCoreException("Callback is null");
}
validateSQL(sql);
tskDB.acquireSingleUserCaseReadLock();
String selectSQL = "SELECT " + sql; // NON-NLS
try (CaseDbConnection connection = tskDB.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(selectSQL)) {
queryCallback.process(resultSet);
} catch (SQLException ex) {
throw new TskCoreException("Error running SELECT query.", ex);
} finally {
tskDB.releaseSingleUserCaseReadLock();
}
}
/**
* Creates a prepared statement object for the purposes of running a select
* statement.
*
* NOTE: Creating the CaseDbPreparedStatement opens a connection and
* acquires a read lock on the case database. For this reason, it is
* recommended to close the prepared statement as soon as it is no longer
* needed, through either a try-with-resources block or calling close().
* Additionally, calling other methods that access or update the database
* should be avoided while the prepared statement is open to prevent
* possible deadlocks.
*
* @param sql The select statement without the starting select keyword.
*
* @return The prepared statement object.
*
* @throws TskCoreException
*/
@Beta
public CaseDbPreparedStatement prepareSelect(String sql) throws TskCoreException {
String selectSQL = "SELECT " + sql; // NON-NLS
try {
return new CaseDbPreparedStatement(StatementType.SELECT, selectSQL, false);
} catch (SQLException ex) {
throw new TskCoreException("Error creating select prepared statement for query:\n" + selectSQL, ex);
}
}
/**
* Creates a prepared statement object for the purposes of running a select
* statement. The given SQL should not include the starting "SELECT" keyword.
*
* @param sql The select statement without the starting select keyword.
* @param trans The open transaction.
*
* @return The prepared statement object.
*
* @throws TskCoreException
*/
@Beta
public CaseDbPreparedStatement prepareSelect(String sql, CaseDbTransaction trans) throws TskCoreException {
validateSQL(sql);
String selectSQL = "SELECT " + sql; // NON-NLS
try {
return new CaseDbPreparedStatement(StatementType.SELECT, selectSQL, trans);
} catch (SQLException ex) {
throw new TskCoreException("Error creating select prepared statement for query:\n" + selectSQL, ex);
}
}
/**
* Performs a select statement query with the given case prepared statement.
*
* @param preparedStatement The case prepared statement.
* @param queryCallback The callback to handle the result set.
*
* @throws TskCoreException
*/
@Beta
public void select(CaseDbPreparedStatement preparedStatement, CaseDbAccessQueryCallback queryCallback) throws TskCoreException {
if (!preparedStatement.getType().equals(StatementType.SELECT)) {
throw new TskCoreException("CaseDbPreparedStatement has incorrect type for select operation");
}
try (ResultSet resultSet = preparedStatement.getStatement().executeQuery()) {
queryCallback.process(resultSet);
} catch (SQLException ex) {
throw new TskCoreException(MessageFormat.format("Error running SELECT query:\n{0}", preparedStatement.getOriginalSql()), ex);
}
}
/**
* Creates a prepared statement object for the purposes of running an insert
* statement. The given SQL should not include the starting "INSERT INTO"
* or the name of the table.
*
* For PostGreSQL, the caller must include the ON CONFLICT DO NOTHING clause
*
* @param tableName The name of the table being updated.
* @param sql The insert statement without the starting "INSERT INTO (table name)" part.
* @param trans The open transaction.
*
* @return The prepared statement object.
*
* @throws TskCoreException
*/
@Beta
public CaseDbPreparedStatement prepareInsert(String tableName, String sql, CaseDbTransaction trans) throws TskCoreException {
validateTableName(tableName);
validateSQL(sql);
String insertSQL = "INSERT";
if (DbType.SQLITE == tskDB.getDatabaseType()) {
insertSQL += " OR IGNORE";
}
insertSQL = insertSQL + " INTO " + tableName + " " + sql; // NON-NLS
try {
return new CaseDbPreparedStatement(StatementType.INSERT, insertSQL, trans);
} catch (SQLException ex) {
throw new TskCoreException("Error creating insert prepared statement for query:\n" + insertSQL, ex);
}
}
/**
* Performs a insert statement query with the given case prepared statement.
*
* @param preparedStatement The case prepared statement.
*
* @throws TskCoreException
*/
@Beta
public void insert(CaseDbPreparedStatement preparedStatement) throws TskCoreException {
if (!preparedStatement.getType().equals(StatementType.INSERT)) {
throw new TskCoreException("CaseDbPreparedStatement has incorrect type for insert operation");
}
try {
preparedStatement.getStatement().executeUpdate();
} catch (SQLException ex) {
throw new TskCoreException("Error inserting row in table " + "" + " with sql = "+ "", ex);
}
}
/**
* Deletes a row in the specified table.
*
* @param tableName table from which to delete the row
* @param sql - SQL string specifying the condition to identify the row to delete
*
* @throws TskCoreException
*/
public void delete(final String tableName, final String sql ) throws TskCoreException {
validateTableName(tableName);
validateSQL(sql);
tskDB.acquireSingleUserCaseWriteLock();
String deleteSQL = "DELETE FROM " + tableName + " " + sql; // NON-NLS
try (CaseDbConnection connection = tskDB.getConnection();
Statement statement = connection.createStatement();) {
statement.executeUpdate(deleteSQL);
} catch (SQLException ex) {
throw new TskCoreException("Error deleting row from table " + tableName, ex);
} finally {
tskDB.releaseSingleUserCaseWriteLock();
}
}
/**
* Validates table name.
* Specifically, it ensures the table doesn't begin with 'tsk_'
* to avoid modifications to core TSK tables
*
* @param tableName
* @throws TskCoreException, if the table name is invalid.
*/
private void validateTableName(String tableName) throws TskCoreException {
if (SleuthkitCase.getCoreTableNames().contains(tableName.toLowerCase())) {
throw new TskCoreException("Attempt to modify a core TSK table " + tableName);
}
if (tableName.toLowerCase().startsWith("tsk_")) {
throw new TskCoreException("Modifying tables with tsk_ prefix is not allowed. ");
}
}
/**
* Validates index name.
* Specifically, it ensures the index name doesn't collide with any of our core indexes
* in CaseDB
*
* @param indexName
* @throws TskCoreException, if the index name is invalid.
*/
private void validateIndexName(String indexName) throws TskCoreException {
if (indexName.isEmpty()) {
throw new TskCoreException("Invalid index name " + indexName);
}
if (SleuthkitCase.getCoreIndexNames().contains(indexName.toLowerCase())) {
throw new TskCoreException("Attempt to modify a core TSK index " + indexName);
}
}
/**
* Validates given SQL string.
*
* @param sql The SQL to validate.
*
* @throws TskCoreException Thrown if the SQL is not valid.
*/
private void validateSQL(String sql) throws TskCoreException {
/*
* TODO (JIRA-5950): Need SQL injection defense in CaseDbAccessManager
*/
}
/**
* Enum to track which type of lock the CaseDbPreparedStatement holds.
*/
private enum LockType {
READ,
WRITE,
NONE;
}
/**
* Enum to track which type of statement the CaseDbPreparedStatement holds.
*/
private enum StatementType {
SELECT,
INSERT,
UPDATE;
}
/**
* A wrapper around a PreparedStatement to execute queries against the
* database.
*/
@Beta
public class CaseDbPreparedStatement implements AutoCloseable {
private final CaseDbConnection connection;
private final PreparedStatement preparedStatement;
private final String originalSql;
private final LockType lockType;
private final StatementType type;
/**
* Construct a prepared statement. This should not be used if a transaction
* is already open.
*
* NOTE: Creating the CaseDbPreparedStatement opens a connection and
* acquires a read lock on the case database. For this reason, it is
* recommended to close the prepared statement as soon as it is no
* longer needed, through either a try-with-resources block or calling
* close(). Additionally, calling other methods that access or update
* the database should be avoided while the prepared statement is open
* to prevent possible deadlocks.
*
* @param type The type of statement.
* @param query The query string.
* @param isWriteLockRequired Whether or not a write lock is required.
* If a write lock is not required, just a
* read lock is acquired.
*
* @throws SQLException
* @throws TskCoreException
*/
private CaseDbPreparedStatement(StatementType type, String query, boolean isWriteLockRequired) throws SQLException, TskCoreException {
if (isWriteLockRequired) {
CaseDbAccessManager.this.tskDB.acquireSingleUserCaseWriteLock();
this.lockType = LockType.WRITE;
} else {
CaseDbAccessManager.this.tskDB.acquireSingleUserCaseReadLock();
this.lockType = LockType.READ;
}
this.connection = tskDB.getConnection();
this.preparedStatement = connection.getPreparedStatement(query, Statement.NO_GENERATED_KEYS);
this.originalSql = query;
this.type = type;
}
/**
* Construct a prepared statement using an already open transaction.
*
* @param type The type of statement.
* @param query The query string.
* @param trans The open transaction.
*
* @throws SQLException
* @throws TskCoreException
*/
private CaseDbPreparedStatement(StatementType type, String query, CaseDbTransaction trans) throws SQLException, TskCoreException {
this.lockType = LockType.NONE;
this.connection = trans.getConnection();
this.preparedStatement = connection.getPreparedStatement(query, Statement.NO_GENERATED_KEYS);
this.originalSql = query;
this.type = type;
}
/**
* Returns the delegate prepared statement.
*
* @return The delegate prepared statement.
*/
private PreparedStatement getStatement() {
return preparedStatement;
}
/**
* Get the type of statement.
*
* @return The statement type (select or insert).
*/
private StatementType getType() {
return type;
}
/**
* Returns the original sql query.
*
* @return The original sql query.
*/
private String getOriginalSql() {
return originalSql;
}
/**
* Resets the parameters in the prepared statement.
*
* @throws TskCoreException
*/
public void reset() throws TskCoreException {
try {
preparedStatement.clearParameters();
} catch (SQLException ex) {
throw new TskCoreException("An error occurred while clearing parameters.", ex);
}
}
/**
* Sets the value at the given parameter index to the given value.
*
* @param parameterIndex The index.
* @param x The value to set at that index.
*
* @throws TskCoreException
*/
public void setBoolean(int parameterIndex, boolean x) throws TskCoreException {
try {
preparedStatement.setBoolean(parameterIndex, x);
} catch (SQLException ex) {
throw new TskCoreException(MessageFormat.format("There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
}
}
/**
* Sets the value at the given parameter index to the given value.
*
* @param parameterIndex The index.
* @param x The value to set at that index.
*
* @throws TskCoreException
*/
public void setByte(int parameterIndex, byte x) throws TskCoreException {
try {
preparedStatement.setByte(parameterIndex, x);
} catch (SQLException ex) {
throw new TskCoreException(MessageFormat.format("There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
}
}
/**
* Sets the value at the given parameter index to the given value.
*
* @param parameterIndex The index.
* @param x The value to set at that index.
*
* @throws TskCoreException
*/
public void setInt(int parameterIndex, int x) throws TskCoreException {
try {
preparedStatement.setInt(parameterIndex, x);
} catch (SQLException ex) {
throw new TskCoreException(MessageFormat.format("There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
}
}
/**
* Sets the value at the given parameter index to the given value.
*
* @param parameterIndex The index.
* @param x The value to set at that index.
*
* @throws TskCoreException
*/
public void setLong(int parameterIndex, long x) throws TskCoreException {
try {
preparedStatement.setLong(parameterIndex, x);
} catch (SQLException ex) {
throw new TskCoreException(MessageFormat.format("There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
}
}
/**
* Sets the value at the given parameter index to the given value.
*
* @param parameterIndex The index.
* @param x The value to set at that index.
*
* @throws TskCoreException
*/
public void setDouble(int parameterIndex, double x) throws TskCoreException {
try {
preparedStatement.setDouble(parameterIndex, x);
} catch (SQLException ex) {
throw new TskCoreException(MessageFormat.format("There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
}
}
/**
* Sets the value at the given parameter index to the given value.
*
* @param parameterIndex The index.
* @param x The value to set at that index.
*
* @throws TskCoreException
*/
public void setString(int parameterIndex, String x) throws TskCoreException {
try {
preparedStatement.setString(parameterIndex, x);
} catch (SQLException ex) {
throw new TskCoreException(MessageFormat.format("There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
}
}
/**
* Sets the value at the given parameter index to the given value.
*
* @param parameterIndex The index.
* @param x The value to set at that index.
*
* @throws TskCoreException
*/
public void setDate(int parameterIndex, Date x) throws TskCoreException {
try {
preparedStatement.setDate(parameterIndex, x);
} catch (SQLException ex) {
throw new TskCoreException(MessageFormat.format("There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
}
}
/**
* Sets the value at the given parameter index to the given value.
*
* @param parameterIndex The index.
* @param x The value to set at that index.
*
* @throws TskCoreException
*/
public void setTime(int parameterIndex, Time x) throws TskCoreException {
try {
preparedStatement.setTime(parameterIndex, x);
} catch (SQLException ex) {
throw new TskCoreException(MessageFormat.format("There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
}
}
/**
* Sets the value at the given parameter index to the given value.
*
* @param parameterIndex The index.
* @param x The value to set at that index.
*
* @throws TskCoreException
*/
public void setTimestamp(int parameterIndex, Timestamp x) throws TskCoreException {
try {
preparedStatement.setTimestamp(parameterIndex, x);
} catch (SQLException ex) {
throw new TskCoreException(MessageFormat.format("There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
}
}
/**
* Sets the value at the given parameter index to the given value. The
* sql type is determined in the same manner as
* java.sql.PreparedStatement.setObject.
*
* @param parameterIndex The index.
* @param x The value to set at that index.
*
* @throws TskCoreException
*/
public void setObject(int parameterIndex, Object x) throws TskCoreException {
try {
preparedStatement.setObject(parameterIndex, x);
} catch (SQLException ex) {
throw new TskCoreException(MessageFormat.format("There was an error setting the value at index: {0} to {1}", parameterIndex, x), ex);
}
}
@Override
public void close() throws SQLException {
// Don't close the statement/connection or release a lock if we were supplied a transaction.
// Everything will be handled when the transaction is closed.
if (lockType.equals(LockType.NONE)) {
return;
}
connection.close();
if (lockType.equals(LockType.WRITE)) {
CaseDbAccessManager.this.tskDB.releaseSingleUserCaseWriteLock();
} else {
CaseDbAccessManager.this.tskDB.releaseSingleUserCaseReadLock();
}
}
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2011-2023 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.datamodel;
import org.sleuthkit.datamodel.TskData.DbType;
/**
* The intent of this class is to hold any information needed to connect to a
* remote database server, except for the actual database name. This does not
* hold information to connect to a local database such as SQLite.
*
* It can be used generically to hold remote database connection information.
*/
public class CaseDbConnectionInfo {
private String hostNameOrIP;
private String portNumber;
private String userName;
private String password;
private DbType dbType;
private boolean sslEnabled = false;
private boolean sslVerify = false;
private String customSslValidationClassName;
/**
* The intent of this class is to hold any information needed to connect to
* a remote database server, except for the actual database name. This does
* not hold information to connect to a local database such as SQLite.
*
* It can be used generically to hold remote database connection
* information.
*
* @param hostNameOrIP the host name
* @param portNumber the port number
* @param userName the user name
* @param password the password
* @param dbType the database type
*/
public CaseDbConnectionInfo(String hostNameOrIP, String portNumber, String userName, String password, DbType dbType) {
this.hostNameOrIP = hostNameOrIP;
this.portNumber = portNumber;
this.userName = userName;
this.password = password;
this.sslEnabled = false;
this.sslVerify = false;
if (dbType == DbType.SQLITE) {
throw new IllegalArgumentException("SQLite database type invalid for CaseDbConnectionInfo. CaseDbConnectionInfo should be used only for remote database types.");
}
this.dbType = dbType;
this.customSslValidationClassName = "";
}
/**
* The intent of this class is to hold any information needed to connect to
* a remote database server, except for the actual database name. This
* constructor allows user to specify whether to use SSL to connect to
* database. This does not hold information to connect to a local database
* such as SQLite.
*
* This constructor allows to specify a Java class that performs custom
* PostgreQSL server SSL certificate validation (if 'sslVerify' is set to
* 'true'). If not specified, the application's default JRE keystore will be
* used.
*
* It can be used generically to hold remote database connection
* information.
*
* @param hostNameOrIP the host name
* @param portNumber the port number
* @param userName the user name
* @param password the password
* @param dbType the database type
* @param sslEnabled a flag whether SSL is enabled
* @param sslVerify 'true' if SSL certificate needs to be CA verified. 'false' if self-signed certificates should be accepted.
* @param customSslValidationClassName full canonical name of a Java class
* that performs custom SSL certificate
* validation. If blank, the
* application's default JRE keystore
* will be used.
*/
public CaseDbConnectionInfo(String hostNameOrIP, String portNumber, String userName, String password, DbType dbType,
boolean sslEnabled, boolean sslVerify, String customSslValidationClassName) {
this.hostNameOrIP = hostNameOrIP;
this.portNumber = portNumber;
this.userName = userName;
this.password = password;
this.sslEnabled = sslEnabled;
this.sslVerify = sslVerify;
if (dbType == DbType.SQLITE) {
throw new IllegalArgumentException("SQLite database type invalid for CaseDbConnectionInfo. CaseDbConnectionInfo should be used only for remote database types.");
}
this.dbType = dbType;
this.customSslValidationClassName = customSslValidationClassName;
}
public DbType getDbType() {
return this.dbType;
}
public String getHost() {
return this.hostNameOrIP;
}
public String getPort() {
return this.portNumber;
}
public String getUserName() {
return this.userName;
}
public String getPassword() {
return this.password;
}
public void setDbType(DbType db) {
this.dbType = db;
}
public void setHost(String host) {
this.hostNameOrIP = host;
}
public void setPort(String port) {
this.portNumber = port;
}
public void setUserName(String user) {
this.userName = user;
}
public void setPassword(String pass) {
this.password = pass;
}
public boolean isSslEnabled() {
return sslEnabled;
}
public void setSslEnabled(boolean sslEnabled) {
this.sslEnabled = sslEnabled;
}
public boolean isSslVerify() {
return sslVerify;
}
public void setSslVerify(boolean sslVerify) {
this.sslVerify = sslVerify;
}
public String getCustomSslValidationClassName() {
return customSslValidationClassName;
}
public void setCustomSslValidationClassName(String customSslValidationClassName) {
this.customSslValidationClassName = customSslValidationClassName;
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2017 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.datamodel;
/**
* As of SleuthKit 4.5.0 database schema versions are two part: Major.Minor.
* This versioning schema is based on semantic versioning, but without using the
* patch number (in practice it is always the default value of zero for case database versions).
*
* The major part is incremented for incompatible changes, i.e., the case
* database will not be usable by an older version. For example, the major
* number should be incremented if tables and/or columns are removed, the
* meanings of values changes, or new records are added to lookup tables
* that will not be convertible to older versions of the corresponding Java
* enums.
*
* The minor version is incremented for compatible changes that are usable by
* older versions of the SleuthKit, although the new schema may not be fully taken
* advantage of. For example, adding an index should be backwards compatible:
* an older version of the software will still be able to open and use the case database, but
* query performance may or may not be affected. Also, adding a column to a
* table should be backwards compatible as older versions of the software should
* simply ignore it.
*/
public final class CaseDbSchemaVersionNumber extends VersionNumber {
/**
* Constructor for CaseDBSchemaVersionNumber. The patch version is unused
* and immutably 0.
*
* @param majorVersion The major version part.
* @param minorVersion The minor version part.
*/
public CaseDbSchemaVersionNumber(int majorVersion, int minorVersion) {
super(majorVersion, minorVersion, 0);
}
/**
* Is a database with the given schema version openable by this version
* number?
*
* @param dbSchemaVersion The schema version of the db want to check for
* compatibility.
*
* @return true if the db schema version is compatible with this version.
*/
public boolean isCompatible(CaseDbSchemaVersionNumber dbSchemaVersion) {
/*
* Since we provide upgrade paths for schema versions greater than 1, this
* amounts to checking if the major version part is greater than 1 and less
* than this version's major number.
*/
final int dbMajor = dbSchemaVersion.getMajor();
return 1 < dbMajor && dbMajor <= getMajor();
}
@Override
public String toString() {
return getMajor() + "." + getMinor();
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2017 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.datamodel;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
/**
* Static utilities for dealing with Collections. At some point this could be
* replaced with apache commons or guava...
*/
final class CollectionUtils {
@SuppressWarnings("unchecked")
static <T> HashSet<T> hashSetOf(T... values) {
return new HashSet<>(Arrays.asList(values));
}
static <T> boolean isNotEmpty(Collection<T> collection) {
return collection.isEmpty() == false;
}
private CollectionUtils() {
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2017-18 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.datamodel;
import java.util.Collection;
import java.util.Collections;
/**
* Collection of string utility methods for use by CVT, CommunicationsManager
* and Timeline.
*/
final class CommManagerSqlStringUtils {
private CommManagerSqlStringUtils() {
}
/**
* Utility method to convert a list to an CSV string.
*
* Null entries in the values collection will be removed before
* the string is created.
*
* @param values - collection of objects .
*
* @return a CSV string.
*/
static <T> String buildCSVString(Collection<T> values) {
return joinAsStrings(values, ",");
}
/**
* Utility method to join a collection into a string using a supplied
* separator. Null entries in the values collection will be removed before
* the string is created.
*
* @param <T> The type of the values in the collection to be joined
* @param values The collection to be joined
* @param separator The separator to insert between each value in the result
* string
*
* @return a string with the elements of values separated by separator
*/
static <T> String joinAsStrings(Collection<T> values, String separator) {
if (values == null || values.isEmpty()) {
return "";
}
values.removeAll(Collections.singleton(null));
return org.apache.commons.lang3.StringUtils.join(values, separator);
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2017-2018 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.datamodel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Defines an aggregate of filters to apply to a CommunicationsManager query.
*/
final public class CommunicationsFilter {
/**
* For now all filters are anded together
*/
private final List<SubFilter> andFilters;
/**
* Create a new empty CommunicationsFilter.
*/
public CommunicationsFilter() {
this(Collections.<SubFilter>emptyList());
}
CommunicationsFilter(List<? extends SubFilter> andSubFilters) {
this.andFilters = new ArrayList<SubFilter>(andSubFilters);
}
/**
* Returns the list of filters that will be ANDed together when applied to a
* query.
*
* NOTE: The returned list is unmodifiable, new filters should be added via
* addAndFilter.
*
* @return An unmodifiable list of the filter.
*/
public List<SubFilter> getAndFilters() {
return Collections.unmodifiableList(andFilters);
}
/**
* Adds a filter to list of filters that will be ANDed together when applied
* to a query.
*
* @param subFilter The SubFilter to add.
*/
public void addAndFilter(SubFilter subFilter) {
andFilters.add(subFilter);
}
/**
* Unit level filter.
*/
public static abstract class SubFilter {
/**
* Returns a string description of the filter.
*
* @return A string description of the filter.
*/
public abstract String getDescription();
/**
* Get the SQL string for the filter.
*
* @param commsManager Communications manager.
*
* @return SQL String for the filter.
*/
abstract String getSQL(CommunicationsManager commsManager);
}
/**
* Filters relationships by relationship type.
*
*/
final public static class RelationshipTypeFilter extends SubFilter {
private final Set<Relationship.Type> relationshipTypes;
/**
* Constructs a RelationshipTypeFilter.
*
* @param relationshipTypes set of relationship types
*/
public RelationshipTypeFilter(Collection<Relationship.Type> relationshipTypes) {
this.relationshipTypes = new HashSet<Relationship.Type>(relationshipTypes);
}
@Override
public String getDescription() {
return "Filters relationships by relationship type.";
}
/**
* Get the SQL string for the filter.
*
* @param commsManager Communications manager.
*
* @return SQL String for the filter.
*/
@Override
public String getSQL(CommunicationsManager commsManager) {
if (relationshipTypes.isEmpty()) {
return "";
}
List<Integer> relationShipTypeIds = new ArrayList<Integer>();
for (Relationship.Type relType : relationshipTypes) {
relationShipTypeIds.add(relType.getTypeID());
}
return " relationships.relationship_type IN ( "
+ CommManagerSqlStringUtils.buildCSVString(relationShipTypeIds) + " )";
}
}
/**
* Filters communications by date range
*/
final public static class DateRangeFilter extends SubFilter {
private final long startDate;
private final long endDate;
private static final long SECS_PER_DAY = 86400;
/**
* Constructs a DateRangeFilter.
*
* @param startDate start date in epoch. Use 0 to not specify a date
* @param endDate end date in epoch. Use 0 to not specify a date.
*/
public DateRangeFilter(long startDate, long endDate) {
this.startDate = startDate;
// Add a day to end date to make it inclusive in the range
if (endDate > 0) {
this.endDate = endDate + SECS_PER_DAY;
} else {
this.endDate = endDate;
}
}
/**
* Get the start date.
*
* @return Seconds from java epoch or zero if no value was set
*/
public long getStartDate() {
return startDate;
}
/**
* Get the end date.
* @return Seconds from java epoch or zero if no value was set
*/
public long getEndDate() {
return endDate;
}
@Override
public String getDescription() {
return "Filters communications by date range.";
}
/**
* Get the SQL string for the filter.
*
* @param commsManager Communications manager.
*
* @return SQL String for the filter.
*/
@Override
public String getSQL(CommunicationsManager commsManager) {
if ((0 == startDate) && (0 == endDate)) {
return "";
}
String sql = "";
if (startDate > 0) {
sql = "(" + " relationships.date_time IS NULL OR relationships.date_time >= " + startDate + ")";
}
if (endDate > 0) {
if (!sql.isEmpty()) {
sql += " AND ";
}
sql += "(" + " relationships.date_time IS NULL OR relationships.date_time < " + endDate + ")";
}
return sql;
}
}
/**
* Filter accounts and relationships by account type.
*
*/
final public static class AccountTypeFilter extends SubFilter {
private final Set<Account.Type> accountTypes;
/**
* Constructs a AccountTypeFilter.
*
* @param accountTypes set of account types to filter on.
*/
public AccountTypeFilter(Collection<Account.Type> accountTypes) {
super();
this.accountTypes = new HashSet<Account.Type>(accountTypes);
}
/**
* Get the selected Account Types.
*
* @return A Set of Type values
*/
public Set<Account.Type> getAccountTypes() {
return accountTypes;
}
@Override
public String getDescription() {
return "Filters accounts and relationships by account type.";
}
/**
* Get the SQL string for the filter.
*
* @param commsManager Communications manager.
*
* @return SQL String for the filter.
*/
@Override
public String getSQL(CommunicationsManager commsManager) {
if (accountTypes.isEmpty()) {
return "";
}
List<Integer> type_ids = new ArrayList<>();
for (Account.Type accountType : accountTypes) {
type_ids.add(commsManager.getAccountTypeId(accountType));
}
String account_type_ids_list = CommManagerSqlStringUtils.buildCSVString(type_ids);
return " account_types.account_type_id IN ( " + account_type_ids_list + " )";
}
}
/**
* Filter by device ids.
*
*/
final public static class DeviceFilter extends SubFilter {
private final Set<String> deviceIds;
/**
* Constructs a device filter.
*
* @param deviceIds set of device Ids to filter on.
*/
public DeviceFilter(Collection<String> deviceIds) {
super();
this.deviceIds = new HashSet<String>(deviceIds);
}
@Override
public String getDescription() {
return "Filters accounts and relationships by device id.";
}
/**
* Gets a set of device ids
*
* @return Collection of device ids
*/
public Collection<String> getDevices() {
return deviceIds;
}
/**
* Get the SQL string for the filter.
*
* @param commsManager Communications manager.
*
* @return SQL String for the filter.
*/
@Override
public String getSQL(CommunicationsManager commsManager) {
if (deviceIds.isEmpty()) {
return "";
}
String sql = "";
List<Long> ds_ids = new ArrayList<Long>();
for (String deviceId : deviceIds) {
try {
ds_ids.addAll(commsManager.getSleuthkitCase().getDataSourceObjIds(deviceId));
} catch (TskCoreException ex) {
Logger.getLogger(DeviceFilter.class.getName()).log(Level.WARNING, "failed to get datasource object ids for deviceId", ex);
}
}
String datasource_obj_ids_list = CommManagerSqlStringUtils.buildCSVString(ds_ids);
if (!datasource_obj_ids_list.isEmpty()) {
sql = " relationships.data_source_obj_id IN ( " + datasource_obj_ids_list + " )";
}
return sql;
}
}
/**
* Filters by the most recent given relationships.
*/
final public static class MostRecentFilter extends SubFilter {
private final int limit;
/**
* Constructs a MostRecentFilter.
*
* @param limit An integer limit value or -1 for no limit.
*
*/
public MostRecentFilter(int limit) {
super();
this.limit = limit;
}
/**
* Returns the filter limit.
*
* @return Integer filter limit
*/
public int getLimit() {
return limit;
}
@Override
public String getDescription() {
return "Filters accounts and relationships by the most recent given relationships.";
}
@Override
String getSQL(CommunicationsManager commsManager) {
if(limit > 0) {
return "ORDER BY relationships.date_time DESC LIMIT " + limit;
} else {
return "";
}
}
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2017-2021 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.datamodel;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
import static org.sleuthkit.datamodel.SleuthkitCase.closeConnection;
import static org.sleuthkit.datamodel.SleuthkitCase.closeResultSet;
import static org.sleuthkit.datamodel.SleuthkitCase.closeStatement;
/**
* Provides an API to create Accounts and communications/relationships between
* accounts.
*/
public final class CommunicationsManager {
private static final Logger LOGGER = Logger.getLogger(CommunicationsManager.class.getName());
private static final BlackboardArtifact.Type ACCOUNT_TYPE = new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT);
private final SleuthkitCase db;
private final Map<Account.Type, Integer> accountTypeToTypeIdMap
= new ConcurrentHashMap<>();
private final Map<String, Account.Type> typeNameToAccountTypeMap
= new ConcurrentHashMap<>();
// Artifact types that can represent a relationship between accounts.
private static final Set<Integer> RELATIONSHIP_ARTIFACT_TYPE_IDS = new HashSet<Integer>(Arrays.asList(
BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID()
));
private static final String RELATIONSHIP_ARTIFACT_TYPE_IDS_CSV_STR = CommManagerSqlStringUtils.buildCSVString(RELATIONSHIP_ARTIFACT_TYPE_IDS);
/**
* Construct a CommunicationsManager for the given SleuthkitCase.
*
* @param skCase The SleuthkitCase
*
* @throws TskCoreException if there is in error initializing the account
* types.
*/
CommunicationsManager(SleuthkitCase skCase) throws TskCoreException {
this.db = skCase;
initAccountTypes();
}
/**
* Make sure the predefined account types are in the account types table.
*
* @throws TskCoreException if there is an error reading the pre-existing
* account types from the db.
*/
private void initAccountTypes() throws TskCoreException {
db.acquireSingleUserCaseWriteLock();
try (CaseDbConnection connection = db.getConnection();
Statement statement = connection.createStatement();) {
// Read the table
int count = readAccountTypes();
if (0 == count) {
// Table is empty, populate it with predefined types
for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) {
try {
statement.execute("INSERT INTO account_types (type_name, display_name) VALUES ( '" + type.getTypeName() + "', '" + type.getDisplayName() + "')"); //NON-NLS
} catch (SQLException ex) {
try (ResultSet resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM account_types WHERE type_name = '" + type.getTypeName() + "'")) { //NON-NLS
resultSet.next();
if (resultSet.getLong("count") == 0) {
throw ex;
}
}
}
try (ResultSet rs2 = connection.executeQuery(statement, "SELECT account_type_id FROM account_types WHERE type_name = '" + type.getTypeName() + "'")) { //NON-NLS
rs2.next();
int typeID = rs2.getInt("account_type_id");
Account.Type accountType = new Account.Type(type.getTypeName(), type.getDisplayName());
this.accountTypeToTypeIdMap.put(accountType, typeID);
this.typeNameToAccountTypeMap.put(type.getTypeName(), accountType);
}
}
}
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "Failed to add row to account_types", ex);
} finally {
db.releaseSingleUserCaseWriteLock();
}
}
/**
* Reads in in the account types table and returns the number of account
* types read in.
*
* @return The number of account types read.
*
* @throws TskCoreException if there is a problem reading the account types.
*/
private int readAccountTypes() throws TskCoreException {
CaseDbConnection connection = null;
Statement statement = null;
ResultSet resultSet = null;
int count = 0;
db.acquireSingleUserCaseReadLock();
try {
connection = db.getConnection();
statement = connection.createStatement();
// If the account_types table is already populated, say when opening a case, then load it
resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM account_types"); //NON-NLS
resultSet.next();
if (resultSet.getLong("count") > 0) {
resultSet.close();
resultSet = connection.executeQuery(statement, "SELECT * FROM account_types");
while (resultSet.next()) {
Account.Type accountType = new Account.Type(resultSet.getString("type_name"), resultSet.getString("display_name"));
this.accountTypeToTypeIdMap.put(accountType, resultSet.getInt("account_type_id"));
this.typeNameToAccountTypeMap.put(accountType.getTypeName(), accountType);
}
count = this.typeNameToAccountTypeMap.size();
}
} catch (SQLException ex) {
throw new TskCoreException("Failed to read account_types", ex);
} finally {
closeResultSet(resultSet);
closeStatement(statement);
closeConnection(connection);
db.releaseSingleUserCaseReadLock();
}
return count;
}
/**
* Gets the SleuthKit case.
*
* @return The SleuthKit case (case database) object.
*/
SleuthkitCase getSleuthkitCase() {
return this.db;
}
/**
* Add a custom account type that is not already defined in Account.Type.
* Will not allow duplicates and will return existing type if the name is
* already defined.
*
* @param accountTypeName account type that must be unique
* @param displayName account type display name
*
* @return Account.Type
*
* @throws TskCoreException if a critical error occurs within TSK core
*/
// NOTE: Full name given for Type for doxygen linking
public org.sleuthkit.datamodel.Account.Type addAccountType(String accountTypeName, String displayName) throws TskCoreException {
Account.Type accountType = new Account.Type(accountTypeName, displayName);
// check if already in map
if (this.accountTypeToTypeIdMap.containsKey(accountType)) {
return accountType;
}
CaseDbTransaction trans = db.beginTransaction();
Statement s = null;
ResultSet rs = null;
try {
s = trans.getConnection().createStatement();
rs = trans.getConnection().executeQuery(s, "SELECT * FROM account_types WHERE type_name = '" + accountTypeName + "'"); //NON-NLS
if (!rs.next()) {
rs.close();
s.execute("INSERT INTO account_types (type_name, display_name) VALUES ( '" + accountTypeName + "', '" + displayName + "')"); //NON-NLS
// Read back the typeID
rs = trans.getConnection().executeQuery(s, "SELECT * FROM account_types WHERE type_name = '" + accountTypeName + "'"); //NON-NLS
rs.next();
int typeID = rs.getInt("account_type_id");
accountType = new Account.Type(rs.getString("type_name"), rs.getString("display_name"));
this.accountTypeToTypeIdMap.put(accountType, typeID);
this.typeNameToAccountTypeMap.put(accountTypeName, accountType);
trans.commit();
return accountType;
} else {
int typeID = rs.getInt("account_type_id");
accountType = new Account.Type(rs.getString("type_name"), rs.getString("display_name"));
this.accountTypeToTypeIdMap.put(accountType, typeID);
return accountType;
}
} catch (SQLException ex) {
trans.rollback();
throw new TskCoreException("Error adding account type", ex);
} finally {
closeResultSet(rs);
closeStatement(s);
}
}
/**
* Records that an account was used in a specific file. Behind the scenes,
* it will create a case-specific Account object if it does not already
* exist, and it will create the needed database entries (which currently
* includes making a TSK_ACCOUNT data artifact).
*
* @param accountType The account type.
* @param accountUniqueID The unique account identifier (such as an email
* address).
* @param moduleName The module creating the account.
* @param sourceFile The source file the account was found in.
* @param attributes List of blackboard attributes to add to the data artifact (may be empty or null).
* @param ingestJobId The ingest job in which the analysis that found
* the account was performed, may be null.
*
* @return An AccountFileInstance object.
*
* @throws TskCoreException The exception is thrown if there is an
* issue updating the case database.
* @throws InvalidAccountIDException The exception is thrown if the account
* ID is not valid for the account type.
*/
// NOTE: Full name given for Type for doxygen linking
public AccountFileInstance createAccountFileInstance(org.sleuthkit.datamodel.Account.Type accountType, String accountUniqueID,
String moduleName, Content sourceFile, List<BlackboardAttribute> attributes, Long ingestJobId) throws TskCoreException, InvalidAccountIDException {
// make or get the Account (unique at the case-level)
Account account = getOrCreateAccount(accountType, normalizeAccountID(accountType, accountUniqueID));
/*
* make or get the artifact. Will not create one if it already exists
* for the sourceFile. Such as an email PST that has the same email
* address multiple times. Only one artifact is created for each email
* message in that PST.
*/
BlackboardArtifact accountArtifact = getOrCreateAccountFileInstanceArtifact(accountType, normalizeAccountID(accountType, accountUniqueID), moduleName, sourceFile, attributes, ingestJobId);
// The account instance map was unused so we have removed it from the database,
// but we expect we may need it so I am preserving this method comment and usage here.
// add a row to Accounts to Instances mapping table
// @@@ BC: Seems like we should only do this if we had to create the artifact.
// But, it will probably fail to create a new one based on unique constraints.
// addAccountFileInstanceMapping(account.getAccountID(), accountArtifact.getArtifactID());
return new AccountFileInstance(accountArtifact, account);
}
/**
* Records that an account was used in a specific file. Behind the scenes,
* it will create a case-specific Account object if it does not already
* exist, and it will create the needed database entries (which currently
* includes making a TSK_ACCOUNT data artifact).
*
* @param accountType The account type.
* @param accountUniqueID The unique account identifier (such as an email
* address).
* @param moduleName The module creating the account.
* @param sourceFile The source file the account was found in.
*
* @return An AccountFileInstance object.
*
* @throws TskCoreException The exception is thrown if there is an
* issue updating the case database.
* @throws InvalidAccountIDException The exception is thrown if the account
* ID is not valid for the account type.
* @deprecated Use
* createAccountFileInstance(org.sleuthkit.datamodel.Account.Type
* accountType, String accountUniqueID, String moduleName, Content
* sourceFile, Long ingestJobId) instead.
*/
@Deprecated
// NOTE: Full name given for Type for doxygen linking
public AccountFileInstance createAccountFileInstance(org.sleuthkit.datamodel.Account.Type accountType, String accountUniqueID, String moduleName, Content sourceFile) throws TskCoreException, InvalidAccountIDException {
return createAccountFileInstance(accountType, accountUniqueID, moduleName, sourceFile, null, null);
}
/**
* Get the Account with the given account type and account ID.
*
* @param accountType account type
* @param accountUniqueID unique account identifier (such as an email
* address)
*
* @return Account, returns NULL is no matching account found
*
* @throws TskCoreException If a critical error occurs within TSK
* core.
* @throws InvalidAccountIDException If the account identifier is not valid.
*/
// NOTE: Full name given for Type for doxygen linking
public Account getAccount(org.sleuthkit.datamodel.Account.Type accountType, String accountUniqueID) throws TskCoreException, InvalidAccountIDException {
Account account = null;
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, "SELECT * FROM accounts WHERE account_type_id = " + getAccountTypeId(accountType)
+ " AND account_unique_identifier = '" + normalizeAccountID(accountType, accountUniqueID) + "'");) { //NON-NLS
if (rs.next()) {
account = new Account(rs.getInt("account_id"), accountType,
rs.getString("account_unique_identifier"));
}
} catch (SQLException ex) {
throw new TskCoreException("Error getting account type id", ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
return account;
}
/**
* Adds relationships between the sender and each of the recipient account
* instances and between all recipient account instances. All account
* instances must be from the same data source.
*
* @param sender Sender account, may be null.
* @param recipients List of recipients, may be empty.
* @param sourceArtifact Artifact that relationships were derived from.
* @param relationshipType The type of relationships to be created.
* @param dateTime Date of communications/relationship, as epoch
* seconds.
*
*
* @throws org.sleuthkit.datamodel.TskCoreException
* @throws org.sleuthkit.datamodel.TskDataException If the all the accounts
* and the relationship are
* not from the same data
* source, or if the
* sourceArtifact and
* relationshipType are not
* compatible.
*/
// NOTE: Full name given for Type for doxygen linking
public void addRelationships(AccountFileInstance sender, List<AccountFileInstance> recipients,
BlackboardArtifact sourceArtifact, org.sleuthkit.datamodel.Relationship.Type relationshipType, long dateTime) throws TskCoreException, TskDataException {
if (sourceArtifact.getDataSourceObjectID() == null) {
throw new TskDataException("Source Artifact does not have a valid data source.");
}
if (relationshipType.isCreatableFrom(sourceArtifact) == false) {
throw new TskDataException("Can not make a " + relationshipType.getDisplayName()
+ " relationship from a" + sourceArtifact.getDisplayName());
}
/*
* Enforce that all accounts and the relationship between them are from
* the same 'source'. This is required for the queries to work
* correctly.
*/
// Currently we do not save the direction of communication
List<Long> accountIDs = new ArrayList<>();
if (null != sender) {
accountIDs.add(sender.getAccount().getAccountID());
if (!sender.getDataSourceObjectID().equals(sourceArtifact.getDataSourceObjectID())) {
throw new TskDataException("Sender and relationship are from different data sources :"
+ "Sender source ID" + sender.getDataSourceObjectID() + " != relationship source ID" + sourceArtifact.getDataSourceObjectID());
}
}
for (AccountFileInstance recipient : recipients) {
accountIDs.add(recipient.getAccount().getAccountID());
if (!recipient.getDataSourceObjectID().equals(sourceArtifact.getDataSourceObjectID())) {
throw new TskDataException("Recipient and relationship are from different data sources :"
+ "Recipient source ID" + recipient.getDataSourceObjectID() + " != relationship source ID" + sourceArtifact.getDataSourceObjectID());
}
}
// Set up the query for the prepared statement
String query = "INTO account_relationships (account1_id, account2_id, relationship_source_obj_id, date_time, relationship_type, data_source_obj_id ) "
+ "VALUES (?,?,?,?,?,?)";
switch (db.getDatabaseType()) {
case POSTGRESQL:
query = "INSERT " + query + " ON CONFLICT DO NOTHING";
break;
case SQLITE:
query = "INSERT OR IGNORE " + query;
break;
default:
throw new TskCoreException("Unknown DB Type: " + db.getDatabaseType().name());
}
CaseDbTransaction trans = db.beginTransaction();
try {
SleuthkitCase.CaseDbConnection connection = trans.getConnection();
PreparedStatement preparedStatement = connection.getPreparedStatement(query, Statement.NO_GENERATED_KEYS);
for (int i = 0; i < accountIDs.size(); i++) {
for (int j = i + 1; j < accountIDs.size(); j++) {
long account1_id = accountIDs.get(i);
long account2_id = accountIDs.get(j);
preparedStatement.clearParameters();
preparedStatement.setLong(1, account1_id);
preparedStatement.setLong(2, account2_id);
preparedStatement.setLong(3, sourceArtifact.getId());
if (dateTime > 0) {
preparedStatement.setLong(4, dateTime);
} else {
preparedStatement.setNull(4, java.sql.Types.BIGINT);
}
preparedStatement.setInt(5, relationshipType.getTypeID());
preparedStatement.setLong(6, sourceArtifact.getDataSourceObjectID());
connection.executeUpdate(preparedStatement);
}
}
trans.commit();
} catch (SQLException ex) {
trans.rollback();
throw new TskCoreException("Error adding accounts relationship", ex);
}
}
/**
* Get the Account for the given account type and account ID. Create an a
* new account if one doesn't exist
*
* @param accountType account type
* @param accountUniqueID unique account identifier
*
* @return A matching account, either existing or newly created.
*
* @throws TskCoreException exception thrown if a critical error
* occurs within TSK core
* @throws InvalidAccountIDException If the account identifier is not valid.
*
*/
private Account getOrCreateAccount(Account.Type accountType, String accountUniqueID) throws TskCoreException, InvalidAccountIDException {
Account account = getAccount(accountType, accountUniqueID);
if (null == account) {
String query = " INTO accounts (account_type_id, account_unique_identifier) "
+ "VALUES ( " + getAccountTypeId(accountType) + ", '"
+ normalizeAccountID(accountType, accountUniqueID) + "'" + ")";
switch (db.getDatabaseType()) {
case POSTGRESQL:
query = "INSERT " + query + " ON CONFLICT DO NOTHING"; //NON-NLS
break;
case SQLITE:
query = "INSERT OR IGNORE " + query;
break;
default:
throw new TskCoreException("Unknown DB Type: " + db.getDatabaseType().name());
}
CaseDbTransaction trans = db.beginTransaction();
Statement s = null;
ResultSet rs = null;
try {
s = trans.getConnection().createStatement();
s.execute(query);
trans.commit();
account = getAccount(accountType, accountUniqueID);
} catch (SQLException ex) {
trans.rollback();
throw new TskCoreException("Error adding an account", ex);
} finally {
closeResultSet(rs);
closeStatement(s);
}
}
return account;
}
/**
* Gets or creates an account artifact for an instance of an account found
* in a file.
*
* @param accountType The account type of the account instance.
* @param accountUniqueID The account ID of the account instance, should be
* unique for the account type (e.g., an email
* address for an email account).
* @param moduleName The name of the module that found the account
* instance.
* @param sourceFile The file in which the account instance was found.
* @param originalAttrs List of blackboard attributes to add to the data artifact (may be empty or null).
* @param ingestJobId The ingest job in which the analysis that found
* the account was performed, may be null.
*
* @return The account artifact.
*
* @throws TskCoreException If there is an error querying or updating the
* case database.
*/
private BlackboardArtifact getOrCreateAccountFileInstanceArtifact(Account.Type accountType, String accountUniqueID, String moduleName,
Content sourceFile, List<BlackboardAttribute> originalAttrs, Long ingestJobId) throws TskCoreException {
if (sourceFile == null) {
throw new TskCoreException("Source file not provided.");
}
BlackboardArtifact accountArtifact = getAccountFileInstanceArtifact(accountType, accountUniqueID, sourceFile);
if (accountArtifact == null) {
List<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(BlackboardAttribute.Type.TSK_ACCOUNT_TYPE, moduleName, accountType.getTypeName()));
attributes.add(new BlackboardAttribute(BlackboardAttribute.Type.TSK_ID, moduleName, accountUniqueID));
if (originalAttrs != null) {
attributes.addAll(originalAttrs);
}
accountArtifact = sourceFile.newDataArtifact(ACCOUNT_TYPE, attributes);
try {
db.getBlackboard().postArtifact(accountArtifact, moduleName, ingestJobId);
} catch (BlackboardException ex) {
LOGGER.log(Level.SEVERE, String.format("Error posting new account artifact to the blackboard (object ID = %d)", accountArtifact.getId()), ex);
}
}
return accountArtifact;
}
/**
* Get the blackboard artifact for the given account type, account ID, and
* source file
*
* @param accountType account type
* @param accountUniqueID Unique account ID (such as email address)
* @param sourceFile Source file (for the artifact)
*
* @return blackboard artifact, returns NULL is no matching account found
*
* @throws TskCoreException exception thrown if a critical error occurs
* within TSK core
*/
private BlackboardArtifact getAccountFileInstanceArtifact(Account.Type accountType, String accountUniqueID, Content sourceFile) throws TskCoreException {
BlackboardArtifact accountArtifact = null;
String queryStr = "SELECT artifacts.artifact_id AS artifact_id,"
+ " artifacts.obj_id AS obj_id,"
+ " artifacts.artifact_obj_id AS artifact_obj_id,"
+ " artifacts.data_source_obj_id AS data_source_obj_id,"
+ " artifacts.artifact_type_id AS artifact_type_id,"
+ " artifacts.review_status_id AS review_status_id,"
+ " tsk_data_artifacts.os_account_obj_id AS os_account_obj_id"
+ " FROM blackboard_artifacts AS artifacts"
+ " JOIN blackboard_attributes AS attr_account_type"
+ " ON artifacts.artifact_id = attr_account_type.artifact_id"
+ " JOIN blackboard_attributes AS attr_account_id"
+ " ON artifacts.artifact_id = attr_account_id.artifact_id"
+ " AND attr_account_id.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID.getTypeID()
+ " AND attr_account_id.value_text = '" + accountUniqueID + "'"
+ " LEFT JOIN tsk_data_artifacts ON tsk_data_artifacts.artifact_obj_id = artifacts.artifact_obj_id"
+ " WHERE artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()
+ " AND attr_account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()
+ " AND attr_account_type.value_text = '" + accountType.getTypeName() + "'"
+ " AND artifacts.obj_id = " + sourceFile.getId(); //NON-NLS
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, queryStr);) { //NON-NLS
if (rs.next()) {
BlackboardArtifact.Type bbartType = db.getBlackboard().getArtifactType(rs.getInt("artifact_type_id"));
accountArtifact = new DataArtifact(db, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"),
rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null,
bbartType.getTypeID(), bbartType.getTypeName(), bbartType.getDisplayName(),
BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")), rs.getLong("os_account_obj_id"), false);
}
} catch (SQLException ex) {
throw new TskCoreException("Error getting account", ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
return accountArtifact;
}
/**
* Get the Account.Type for the give type name.
*
* @param accountTypeName An account type name.
*
* @return An Account.Type or null if the account type does not exist.
*
* @throws TskCoreException If an error occurs accessing the case database.
*/
// NOTE: Full name given for Type for doxygen linking
public org.sleuthkit.datamodel.Account.Type getAccountType(String accountTypeName) throws TskCoreException {
if (this.typeNameToAccountTypeMap.containsKey(accountTypeName)) {
return this.typeNameToAccountTypeMap.get(accountTypeName);
}
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, "SELECT account_type_id, type_name, display_name FROM account_types WHERE type_name = '" + accountTypeName + "'");) { //NON-NLS
Account.Type accountType = null;
if (rs.next()) {
accountType = new Account.Type(accountTypeName, rs.getString("display_name"));
this.accountTypeToTypeIdMap.put(accountType, rs.getInt("account_type_id"));
this.typeNameToAccountTypeMap.put(accountTypeName, accountType);
}
return accountType;
} catch (SQLException ex) {
throw new TskCoreException("Error getting account type id", ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Returns a list of AccountDeviceInstances that have at least one
* relationship that meets the criteria listed in the filters.
*
* Applicable filters: DeviceFilter, AccountTypeFilter, DateRangeFilter,
* RelationshipTypeFilter, MostRecentFilter
*
* @param filter filters to apply
*
* @return list of AccountDeviceInstances
*
* @throws TskCoreException exception thrown if a critical error occurs
* within TSK core
*/
public List<AccountDeviceInstance> getAccountDeviceInstancesWithRelationships(CommunicationsFilter filter) throws TskCoreException {
//set up applicable filters
Set<String> applicableInnerQueryFilters = new HashSet<String>(Arrays.asList(
CommunicationsFilter.DateRangeFilter.class.getName(),
CommunicationsFilter.DeviceFilter.class.getName(),
CommunicationsFilter.RelationshipTypeFilter.class.getName()
));
String relationshipFilterSQL = getCommunicationsFilterSQL(filter, applicableInnerQueryFilters);
String relationshipLimitSQL = getMostRecentFilterLimitSQL(filter);
String relTblfilterQuery
= "SELECT * "
+ "FROM account_relationships as relationships"
+ (relationshipFilterSQL.isEmpty() ? "" : " WHERE " + relationshipFilterSQL)
+ (relationshipLimitSQL.isEmpty() ? "" : relationshipLimitSQL);
String uniqueAccountQueryTemplate
= " SELECT %1$1s as account_id,"
+ " data_source_obj_id"
+ " FROM ( " + relTblfilterQuery + ")AS %2$s";
String relationshipTableFilterQuery1 = String.format(uniqueAccountQueryTemplate, "account1_id", "union_query_1");
String relationshipTableFilterQuery2 = String.format(uniqueAccountQueryTemplate, "account2_id", "union_query_2");
//this query groups by account_id and data_source_obj_id across both innerQueries
String uniqueAccountQuery
= "SELECT DISTINCT account_id, data_source_obj_id"
+ " FROM ( " + relationshipTableFilterQuery1 + " UNION " + relationshipTableFilterQuery2 + " ) AS inner_union"
+ " GROUP BY account_id, data_source_obj_id";
// set up applicable filters
Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
CommunicationsFilter.AccountTypeFilter.class.getName()
));
String accountTypeFilterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
String queryStr
= //account info
" accounts.account_id AS account_id,"
+ " accounts.account_unique_identifier AS account_unique_identifier,"
//account type info
+ " account_types.type_name AS type_name,"
//Account device instance info
+ " data_source_info.device_id AS device_id"
+ " FROM ( " + uniqueAccountQuery + " ) AS account_device_instances"
+ " JOIN accounts AS accounts"
+ " ON accounts.account_id = account_device_instances.account_id"
+ " JOIN account_types AS account_types"
+ " ON accounts.account_type_id = account_types.account_type_id"
+ " JOIN data_source_info AS data_source_info"
+ " ON account_device_instances.data_source_obj_id = data_source_info.obj_id"
+ (accountTypeFilterSQL.isEmpty() ? "" : " WHERE " + accountTypeFilterSQL);
switch (db.getDatabaseType()) {
case POSTGRESQL:
queryStr = "SELECT DISTINCT ON ( accounts.account_id, data_source_info.device_id) " + queryStr;
break;
case SQLITE:
queryStr = "SELECT " + queryStr + " GROUP BY accounts.account_id, data_source_info.device_id";
break;
default:
throw new TskCoreException("Unknown DB Type: " + db.getDatabaseType().name());
}
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, queryStr);) { //NON-NLS
ArrayList<AccountDeviceInstance> accountDeviceInstances = new ArrayList<AccountDeviceInstance>();
while (rs.next()) {
long account_id = rs.getLong("account_id");
String deviceID = rs.getString("device_id");
final String type_name = rs.getString("type_name");
final String account_unique_identifier = rs.getString("account_unique_identifier");
Account.Type accountType = typeNameToAccountTypeMap.get(type_name);
Account account = new Account(account_id, accountType, account_unique_identifier);
accountDeviceInstances.add(new AccountDeviceInstance(account, deviceID));
}
return accountDeviceInstances;
} catch (SQLException ex) {
throw new TskCoreException("Error getting account device instances. " + ex.getMessage(), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Get the number of relationships between all pairs of accounts in the
* given set. For each pair of accounts <a2,a1> == <a1,a2>, find the number
* of relationships between those two accounts that pass the given filter,.
*
* Applicable filters: DeviceFilter, DateRangeFilter, RelationshipTypeFilter
*
* @param accounts The set of accounts to count the relationships (pairwise)
* between.
* @param filter The filter that relationships must pass to be included in
* the count.
*
* @return The number of relationships (that pass the filter) between each
* pair of accounts, organized in a map where the key is an
* unordered pair of account ids, and the value is the number of
* relationships.
*
* @throws TskCoreException if there is a problem querying the DB.
*/
public Map<AccountPair, Long> getRelationshipCountsPairwise(Set<AccountDeviceInstance> accounts, CommunicationsFilter filter) throws TskCoreException {
Set<Long> accountIDs = new HashSet<Long>();
Set<String> accountDeviceIDs = new HashSet<String>();
for (AccountDeviceInstance adi : accounts) {
accountIDs.add(adi.getAccount().getAccountID());
accountDeviceIDs.add("'" + adi.getDeviceId() + "'");
}
//set up applicable filters
Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
CommunicationsFilter.DateRangeFilter.class.getName(),
CommunicationsFilter.DeviceFilter.class.getName(),
CommunicationsFilter.RelationshipTypeFilter.class.getName()
));
String accountIDsCSL = CommManagerSqlStringUtils.buildCSVString(accountIDs);
String accountDeviceIDsCSL = CommManagerSqlStringUtils.buildCSVString(accountDeviceIDs);
String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
final String queryString
= " SELECT count(DISTINCT relationships.relationship_source_obj_id) AS count," //realtionship count
+ " data_source_info.device_id AS device_id,"
//account 1 info
+ " accounts1.account_id AS account1_id,"
+ " accounts1.account_unique_identifier AS account1_unique_identifier,"
+ " account_types1.type_name AS type_name1,"
+ " account_types1.display_name AS display_name1,"
//account 2 info
+ " accounts2.account_id AS account2_id,"
+ " accounts2.account_unique_identifier AS account2_unique_identifier,"
+ " account_types2.type_name AS type_name2,"
+ " account_types2.display_name AS display_name2"
+ " FROM account_relationships AS relationships"
+ " JOIN data_source_info AS data_source_info"
+ " ON relationships.data_source_obj_id = data_source_info.obj_id "
//account1 aliases
+ " JOIN accounts AS accounts1 "
+ " ON accounts1.account_id = relationships.account1_id"
+ " JOIN account_types AS account_types1"
+ " ON accounts1.account_type_id = account_types1.account_type_id"
//account2 aliases
+ " JOIN accounts AS accounts2 "
+ " ON accounts2.account_id = relationships.account2_id"
+ " JOIN account_types AS account_types2"
+ " ON accounts2.account_type_id = account_types2.account_type_id"
+ " WHERE (( relationships.account1_id IN (" + accountIDsCSL + ")) "
+ " AND ( relationships.account2_id IN ( " + accountIDsCSL + " ))"
+ " AND ( data_source_info.device_id IN (" + accountDeviceIDsCSL + "))) "
+ (filterSQL.isEmpty() ? "" : " AND " + filterSQL)
+ " GROUP BY data_source_info.device_id, "
+ " accounts1.account_id, "
+ " account_types1.type_name, "
+ " account_types1.display_name, "
+ " accounts2.account_id, "
+ " account_types2.type_name, "
+ " account_types2.display_name";
Map<AccountPair, Long> results = new HashMap<AccountPair, Long>();
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, queryString);) { //NON-NLS
while (rs.next()) {
//make account 1
Account.Type type1 = new Account.Type(rs.getString("type_name1"), rs.getString("display_name1"));
AccountDeviceInstance adi1 = new AccountDeviceInstance(new Account(rs.getLong("account1_id"), type1,
rs.getString("account1_unique_identifier")),
rs.getString("device_id"));
//make account 2
Account.Type type2 = new Account.Type(rs.getString("type_name2"), rs.getString("display_name2"));
AccountDeviceInstance adi2 = new AccountDeviceInstance(new Account(rs.getLong("account2_id"), type2,
rs.getString("account2_unique_identifier")),
rs.getString("device_id"));
AccountPair relationshipKey = new AccountPair(adi1, adi2);
long count = rs.getLong("count");
//merge counts for relationships that have the accounts flipped.
Long oldCount = results.get(relationshipKey);
if (oldCount != null) {
count += oldCount;
}
results.put(relationshipKey, count);
}
return results;
} catch (SQLException ex) {
throw new TskCoreException("Error getting relationships between accounts. " + ex.getMessage(), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Get the number of unique relationship sources (such as EMAIL artifacts)
* associated with an account on a given device (AccountDeviceInstance) that
* meet the filter criteria.
*
* Applicable filters: RelationshipTypeFilter, DateRangeFilter
*
* @param accountDeviceInstance Account of interest
* @param filter Filters to apply.
*
* @return number of account relationships found for this account.
*
* @throws org.sleuthkit.datamodel.TskCoreException
*
*/
public long getRelationshipSourcesCount(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter) throws TskCoreException {
long account_id = accountDeviceInstance.getAccount().getAccountID();
// Get the list of Data source objects IDs correpsonding to this DeviceID.
String datasourceObjIdsCSV = CommManagerSqlStringUtils.buildCSVString(
db.getDataSourceObjIds(accountDeviceInstance.getDeviceId()));
// set up applicable filters
Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
CommunicationsFilter.RelationshipTypeFilter.class.getName(),
CommunicationsFilter.DateRangeFilter.class.getName()
));
String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
String innerQuery = " account_relationships AS relationships";
String limitStr = getMostRecentFilterLimitSQL(filter);
if (!limitStr.isEmpty()) {
innerQuery = "(SELECT * FROM account_relationships as relationships " + limitStr + ") as relationships";
}
String queryStr
= "SELECT count(DISTINCT relationships.relationship_source_obj_id) as count "
+ " FROM" + innerQuery
+ " WHERE relationships.data_source_obj_id IN ( " + datasourceObjIdsCSV + " )"
+ " AND ( relationships.account1_id = " + account_id
+ " OR relationships.account2_id = " + account_id + " )"
+ (filterSQL.isEmpty() ? "" : " AND " + filterSQL);
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, queryStr);) { //NON-NLS
rs.next();
return (rs.getLong("count"));
} catch (SQLException ex) {
throw new TskCoreException("Error getting relationships count for account device instance. " + ex.getMessage(), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Get the unique relationship sources (such as EMAIL artifacts) associated
* with accounts on specific devices (AccountDeviceInstance) that meet the
* filter criteria.
*
* Applicable filters: RelationshipTypeFilter, DateRangeFilter,
* MostRecentFilter
*
* @param accountDeviceInstanceList set of account device instances for
* which to get the relationship sources.
* @param filter Filters to apply.
*
* @return relationship sources found for given account(s).
*
* @throws org.sleuthkit.datamodel.TskCoreException
*/
public Set<Content> getRelationshipSources(Set<AccountDeviceInstance> accountDeviceInstanceList, CommunicationsFilter filter) throws TskCoreException {
if (accountDeviceInstanceList.isEmpty()) {
LOGGER.log(Level.WARNING, "Empty accountDeviceInstanceList passed to getRelationshipSources");
return Collections.emptySet();
}
// Build a map of account ids to device ids. For each account id there
// will be a set of device ids.
Map<Long, Set<Long>> accountIdToDatasourceObjIdMap = new HashMap<>();
for (AccountDeviceInstance accountDeviceInstance : accountDeviceInstanceList) {
long accountID = accountDeviceInstance.getAccount().getAccountID();
List<Long> dataSourceObjIds = db.getDataSourceObjIds(accountDeviceInstance.getDeviceId());
if (accountIdToDatasourceObjIdMap.containsKey(accountID)) {
accountIdToDatasourceObjIdMap.get(accountID).addAll(dataSourceObjIds);
} else {
accountIdToDatasourceObjIdMap.put(accountID, new HashSet<>(dataSourceObjIds));
}
}
// Create the OR cause that limits the accounts for a given data source.
List<String> adiSQLClauses = new ArrayList<>();
for (Map.Entry<Long, Set<Long>> entry : accountIdToDatasourceObjIdMap.entrySet()) {
final Long accountID = entry.getKey();
String datasourceObjIdsCSV = CommManagerSqlStringUtils.buildCSVString(entry.getValue());
adiSQLClauses.add(
"( "
+ (!datasourceObjIdsCSV.isEmpty() ? "( relationships.data_source_obj_id IN ( " + datasourceObjIdsCSV + " ) ) AND" : "")
+ " ( relationships.account1_id = " + accountID
+ " OR relationships.account2_id = " + accountID + " ) )"
);
}
String adiSQLClause = CommManagerSqlStringUtils.joinAsStrings(adiSQLClauses, " OR ");
if(adiSQLClause.isEmpty()) {
LOGGER.log(Level.SEVERE, "There set of AccountDeviceInstances had no valid data source ids.");
return Collections.emptySet();
}
// Build the filter part of the query.
Set<String> applicableFilters = new HashSet<>(Arrays.asList(
CommunicationsFilter.RelationshipTypeFilter.class
.getName(),
CommunicationsFilter.DateRangeFilter.class
.getName()
));
String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
// Basic join.
String limitQuery = " account_relationships AS relationships";
// If the user set filters expand this to be a subquery that selects
// accounts based on the filter.
String limitStr = getMostRecentFilterLimitSQL(filter);
if (!limitStr.isEmpty()) {
limitQuery = "(SELECT * FROM account_relationships as relationships " + limitStr + ") as relationships";
}
String queryStr
= "SELECT DISTINCT artifacts.artifact_id AS artifact_id,"
+ " artifacts.obj_id AS obj_id,"
+ " artifacts.artifact_obj_id AS artifact_obj_id,"
+ " artifacts.data_source_obj_id AS data_source_obj_id, "
+ " artifacts.artifact_type_id AS artifact_type_id, "
+ " artifacts.review_status_id AS review_status_id,"
+ " tsk_data_artifacts.os_account_obj_id as os_account_obj_id"
+ " FROM blackboard_artifacts as artifacts"
+ " JOIN " + limitQuery
+ " ON artifacts.artifact_obj_id = relationships.relationship_source_obj_id"
+ " LEFT JOIN tsk_data_artifacts ON artifacts.artifact_obj_id = tsk_data_artifacts.artifact_obj_id"
// append sql to restrict search to specified account device instances
+ " WHERE (" + adiSQLClause + " )"
// plus other filters
+ (filterSQL.isEmpty() ? "" : " AND (" + filterSQL + " )");
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, queryStr);) { //NON-NLS
Set<Content> relationshipSources = new HashSet<>();
relationshipSources.addAll(getDataArtifactsFromResult(rs));
return relationshipSources;
} catch (SQLException ex) {
throw new TskCoreException("Error getting relationships for account. " + ex.getMessage(), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Get a set of AccountDeviceInstances that have relationships with the
* given AccountDeviceInstance and meet the criteria of the given filter.
*
* Applicable filters: DeviceFilter, DateRangeFilter, RelationshipTypeFilter
*
* @param accountDeviceInstance The account device instance.
* @param filter The filters to apply.
*
* @return A set of AccountDeviceInstances that have relationships with the
* given AccountDeviceInstance and meet the criteria of the given
* filter.
*
* @throws TskCoreException if there is a serious error executing he query.
*/
public List<AccountDeviceInstance> getRelatedAccountDeviceInstances(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter) throws TskCoreException {
final List<Long> dataSourceObjIds
= getSleuthkitCase().getDataSourceObjIds(accountDeviceInstance.getDeviceId());
//set up applicable filters
Set<String> applicableInnerQueryFilters = new HashSet<String>(Arrays.asList(
CommunicationsFilter.DateRangeFilter.class.getName(),
CommunicationsFilter.DeviceFilter.class.getName(),
CommunicationsFilter.RelationshipTypeFilter.class.getName()
));
String innerQueryfilterSQL = getCommunicationsFilterSQL(filter, applicableInnerQueryFilters);
String innerQueryTemplate
= " SELECT %1$1s as account_id,"
+ " data_source_obj_id"
+ " FROM account_relationships as relationships"
+ " WHERE %2$1s = " + accountDeviceInstance.getAccount().getAccountID() + ""
+ " AND data_source_obj_id IN (" + CommManagerSqlStringUtils.buildCSVString(dataSourceObjIds) + ")"
+ (innerQueryfilterSQL.isEmpty() ? "" : " AND " + innerQueryfilterSQL);
String innerQuery1 = String.format(innerQueryTemplate, "account1_id", "account2_id");
String innerQuery2 = String.format(innerQueryTemplate, "account2_id", "account1_id");
//this query groups by account_id and data_source_obj_id across both innerQueries
String combinedInnerQuery
= "SELECT account_id, data_source_obj_id "
+ " FROM ( " + innerQuery1 + " UNION " + innerQuery2 + " ) AS inner_union"
+ " GROUP BY account_id, data_source_obj_id";
// set up applicable filters
Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
CommunicationsFilter.AccountTypeFilter.class.getName()
));
String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
String queryStr
= //account info
" accounts.account_id AS account_id,"
+ " accounts.account_unique_identifier AS account_unique_identifier,"
//account type info
+ " account_types.type_name AS type_name,"
//Account device instance info
+ " data_source_info.device_id AS device_id"
+ " FROM ( " + combinedInnerQuery + " ) AS account_device_instances"
+ " JOIN accounts AS accounts"
+ " ON accounts.account_id = account_device_instances.account_id"
+ " JOIN account_types AS account_types"
+ " ON accounts.account_type_id = account_types.account_type_id"
+ " JOIN data_source_info AS data_source_info"
+ " ON account_device_instances.data_source_obj_id = data_source_info.obj_id"
+ (filterSQL.isEmpty() ? "" : " WHERE " + filterSQL);
switch (db.getDatabaseType()) {
case POSTGRESQL:
queryStr = "SELECT DISTINCT ON ( accounts.account_id, data_source_info.device_id) " + queryStr;
break;
case SQLITE:
queryStr = "SELECT " + queryStr + " GROUP BY accounts.account_id, data_source_info.device_id";
break;
default:
throw new TskCoreException("Unknown DB Type: " + db.getDatabaseType().name());
}
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, queryStr);) {
ArrayList<AccountDeviceInstance> accountDeviceInstances = new ArrayList<AccountDeviceInstance>();
while (rs.next()) {
long account_id = rs.getLong("account_id");
String deviceID = rs.getString("device_id");
final String type_name = rs.getString("type_name");
final String account_unique_identifier = rs.getString("account_unique_identifier");
Account.Type accountType = typeNameToAccountTypeMap.get(type_name);
Account account = new Account(account_id, accountType, account_unique_identifier);
accountDeviceInstances.add(new AccountDeviceInstance(account, deviceID));
}
return accountDeviceInstances;
} catch (SQLException ex) {
throw new TskCoreException("Error getting account device instances. " + ex.getMessage(), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Get the sources (artifacts, content) of relationships between the given
* account device instances.
*
* Applicable filters: DeviceFilter, DateRangeFilter,
* RelationshipTypeFilter, MostRecentFilter
*
* @param account1 First AccountDeviceInstance
* @param account2 Second AccountDeviceInstance
* @param filter Filters to apply.
*
* @return relationship sources for relationships between account1 and
* account2.
*
* @throws org.sleuthkit.datamodel.TskCoreException
*/
public List<Content> getRelationshipSources(AccountDeviceInstance account1, AccountDeviceInstance account2, CommunicationsFilter filter) throws TskCoreException {
//set up applicable filters
Set<String> applicableFilters = new HashSet<>(Arrays.asList(
CommunicationsFilter.DateRangeFilter.class.getName(),
CommunicationsFilter.DeviceFilter.class.getName(),
CommunicationsFilter.RelationshipTypeFilter.class.getName()
));
String limitQuery = " account_relationships AS relationships";
String limitStr = getMostRecentFilterLimitSQL(filter);
if (!limitStr.isEmpty()) {
limitQuery = "(SELECT * FROM account_relationships as relationships " + limitStr + ") as relationships";
}
String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
final String queryString = "SELECT artifacts.artifact_id AS artifact_id,"
+ " artifacts.obj_id AS obj_id,"
+ " artifacts.artifact_obj_id AS artifact_obj_id,"
+ " artifacts.data_source_obj_id AS data_source_obj_id,"
+ " artifacts.artifact_type_id AS artifact_type_id,"
+ " artifacts.review_status_id AS review_status_id,"
+ " tsk_data_artifacts.os_account_obj_id AS os_account_obj_id"
+ " FROM blackboard_artifacts AS artifacts"
+ " JOIN " + limitQuery
+ " ON artifacts.artifact_obj_id = relationships.relationship_source_obj_id"
+ " LEFT JOIN tsk_data_artifacts ON artifacts.artifact_obj_id = tsk_data_artifacts.artifact_obj_id"
+ " WHERE (( relationships.account1_id = " + account1.getAccount().getAccountID()
+ " AND relationships.account2_id = " + account2.getAccount().getAccountID()
+ " ) OR ( relationships.account2_id = " + account1.getAccount().getAccountID()
+ " AND relationships.account1_id =" + account2.getAccount().getAccountID() + " ))"
+ (filterSQL.isEmpty() ? "" : " AND " + filterSQL);
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, queryString);) {
ArrayList<Content> artifacts = new ArrayList<>();
artifacts.addAll(getDataArtifactsFromResult(rs));
return artifacts;
} catch (SQLException ex) {
throw new TskCoreException("Error getting relationships between accounts. " + ex.getMessage(), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Get a list AccountFileInstance for the given accounts.
*
* @param account List of accounts
*
* @return A lit of AccountFileInstances for the given accounts or null if
* none are found.
*
* @throws org.sleuthkit.datamodel.TskCoreException
*/
public List<AccountFileInstance> getAccountFileInstances(Account account) throws TskCoreException {
List<AccountFileInstance> accountFileInstanceList = new ArrayList<>();
@SuppressWarnings("deprecation")
List<BlackboardArtifact> artifactList = getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID, account.getTypeSpecificID());
if (artifactList != null && !artifactList.isEmpty()) {
for (BlackboardArtifact artifact : artifactList) {
accountFileInstanceList.add(new AccountFileInstance(artifact, account));
}
}
if (!accountFileInstanceList.isEmpty()) {
return accountFileInstanceList;
} else {
return null;
}
}
/**
* Gets a list of the distinct account types that can currently be found in
* the case db.
*
* @return A list of distinct accounts or an empty list.
*
* @throws TskCoreException
*/
public List<Account.Type> getAccountTypesInUse() throws TskCoreException {
String query = "SELECT DISTINCT accounts.account_type_id, type_name, display_name FROM accounts JOIN account_types ON accounts.account_type_id = account_types.account_type_id";
List<Account.Type> inUseAccounts = new ArrayList<>();
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, query);) {
Account.Type accountType;
while (rs.next()) {
String accountTypeName = rs.getString("type_name");
accountType = this.typeNameToAccountTypeMap.get(accountTypeName);
if (accountType == null) {
accountType = new Account.Type(accountTypeName, rs.getString("display_name"));
this.accountTypeToTypeIdMap.put(accountType, rs.getInt("account_type_id"));
}
inUseAccounts.add(accountType);
}
return inUseAccounts;
} catch (SQLException ex) {
throw new TskCoreException("Error getting account type id", ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Gets a list of accounts that are related to the given artifact.
*
* @param artifact
*
* @return A list of distinct accounts or an empty list if none where found.
*
* @throws TskCoreException
*/
public List<Account> getAccountsRelatedToArtifact(BlackboardArtifact artifact) throws TskCoreException {
if (artifact == null) {
throw new IllegalArgumentException("null arugment passed to getAccountsRelatedToArtifact");
}
List<Account> accountList = new ArrayList<>();
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = db.getConnection()) {
try {
// In order to get a list of all the unique accounts in a relationship with the given aritfact
// we must first union a list of the unique account1_id in the relationship with artifact
// then the unique account2_id (inner select with union). The outter select assures the list
// of the inner select only contains unique accounts.
String query = String.format("SELECT DISTINCT (account_id), account_type_id, account_unique_identifier"
+ " FROM ("
+ " SELECT DISTINCT (account_id), account_type_id, account_unique_identifier"
+ " FROM accounts"
+ " JOIN account_relationships ON account1_id = account_id"
+ " WHERE relationship_source_obj_id = %d"
+ " UNION "
+ " SELECT DISTINCT (account_id), account_type_id, account_unique_identifier"
+ " FROM accounts"
+ " JOIN account_relationships ON account2_id = account_id"
+ " WHERE relationship_source_obj_id = %d) AS unionOfRelationships", artifact.getId(), artifact.getId());
try (Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
Account.Type accountType = null;
int accountTypeId = rs.getInt("account_type_id");
for (Map.Entry<Account.Type, Integer> entry : accountTypeToTypeIdMap.entrySet()) {
if (entry.getValue() == accountTypeId) {
accountType = entry.getKey();
break;
}
}
accountList.add(new Account(rs.getInt("account_id"), accountType, rs.getString("account_unique_identifier")));
}
} catch (SQLException ex) {
throw new TskCoreException("Unable to get account list for give artifact " + artifact.getId(), ex);
}
} finally {
db.releaseSingleUserCaseReadLock();
}
}
return accountList;
}
/**
* Get account_type_id for the given account type.
*
* @param accountType account type to lookup.
*
* @return account_type_id for the given account type. 0 if not known.
*/
int getAccountTypeId(Account.Type accountType) {
if (accountTypeToTypeIdMap.containsKey(accountType)) {
return accountTypeToTypeIdMap.get(accountType);
}
return 0;
}
/**
* Normalize the given account ID according to the rules of the given
* Account.Type.
*
* @param accountType The type of account to normalize for
* @param accountUniqueID The account id to normalize
*
* @return The normalized account id.
*
* @throws InvalidAccountIDException If the account identifier is not valid.
*/
private String normalizeAccountID(Account.Type accountType, String accountUniqueID) throws InvalidAccountIDException {
if (accountUniqueID == null || accountUniqueID.isEmpty()) {
throw new InvalidAccountIDException("Account id is null or empty.");
}
String normalizedAccountID;
if (accountType.equals(Account.Type.PHONE)) {
normalizedAccountID = CommunicationsUtils.normalizePhoneNum(accountUniqueID);
} else if (accountType.equals(Account.Type.EMAIL)) {
normalizedAccountID = CommunicationsUtils.normalizeEmailAddress(accountUniqueID);
} else {
normalizedAccountID = accountUniqueID.toLowerCase().trim();
}
return normalizedAccountID;
}
/**
* Builds the SQL for the given CommunicationsFilter.
*
* Gets the SQL for each subfilter and combines using AND.
*
* @param commFilter The CommunicationsFilter to get the SQL for.
* @param applicableFilters A Set of names of classes of subfilters that are
* applicable. SubFilters not in this list will be
* ignored.
*
* @return return SQL suitible for use IN a where clause.
*/
private String getCommunicationsFilterSQL(CommunicationsFilter commFilter, Set<String> applicableFilters) {
if (null == commFilter || commFilter.getAndFilters().isEmpty()) {
return "";
}
String sqlStr = "";
StringBuilder sqlSB = new StringBuilder();
boolean first = true;
for (CommunicationsFilter.SubFilter subFilter : commFilter.getAndFilters()) {
// If the filter is applicable
if (applicableFilters.contains(subFilter.getClass().getName())) {
String subfilterSQL = subFilter.getSQL(this);
if (!subfilterSQL.isEmpty()) {
if (first) {
first = false;
} else {
sqlSB.append(" AND ");
}
sqlSB.append("( ");
sqlSB.append(subfilterSQL);
sqlSB.append(" )");
}
}
}
if (!sqlSB.toString().isEmpty()) {
sqlStr = "( " + sqlSB.toString() + " )";
}
return sqlStr;
}
/**
* Builds the SQL for the MostRecentFilter.
*
* @param filter The CommunicationsFilter to get the SQL for.
*
* @return Order BY and LIMIT clause or empty string if no filter is
* available.
*/
private String getMostRecentFilterLimitSQL(CommunicationsFilter filter) {
String limitStr = "";
if (filter != null && !filter.getAndFilters().isEmpty()) {
for (CommunicationsFilter.SubFilter subFilter : filter.getAndFilters()) {
if (subFilter.getClass().getName().equals(CommunicationsFilter.MostRecentFilter.class.getName())) {
limitStr = subFilter.getSQL(this);
break;
}
}
}
return limitStr;
}
/**
* A helper method that will return a set of BlackboardArtifact objects for
* the given ResultSet.
*
* @param resultSet The results of executing a query.
*
* @return A list of BlackboardArtifact objects.
*
* @throws SQLException
* @throws TskCoreException
*/
private List<BlackboardArtifact> getDataArtifactsFromResult(ResultSet resultSet) throws SQLException, TskCoreException {
List<BlackboardArtifact> artifacts = new ArrayList<>();
while (resultSet.next()) {
BlackboardArtifact.Type bbartType = db.getBlackboard().getArtifactType(resultSet.getInt("artifact_type_id"));
artifacts.add(new DataArtifact(db, resultSet.getLong("artifact_id"),
resultSet.getLong("obj_id"), resultSet.getLong("artifact_obj_id"),
resultSet.getObject("data_source_obj_id") != null ? resultSet.getLong("data_source_obj_id") : null,
bbartType.getTypeID(),
bbartType.getTypeName(), bbartType.getDisplayName(),
BlackboardArtifact.ReviewStatus.withID(resultSet.getInt("review_status_id")),
resultSet.getLong("os_account_obj_id"), false));
}
return artifacts;
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2020 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.datamodel;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
/**
* Provides general utility methods related to communications artifacts.
*
*/
public final class CommunicationsUtils {
// These symbols are allowed in dialed or written forms of phone numbers.
// A '+' is allowed only as a leading digit and hence not inlcuded here.
private static final Set<String> TELEPHONY_CHARS = new HashSet<>(Arrays.asList(
"-", "(", ")", "#", "*", ","
));
private static final int MIN_PHONENUMBER_LEN = 3;
/**
* Empty private constructor.
*/
private CommunicationsUtils() {
}
/**
* Normalize the given phone number by removing all non numeric characters,
* except: a leading + # or * or ,
*
* Note: this method intentionally performs a rather lenient validation of
* the phone number in order to not drop any collected data.
*
* @param phoneNumber The string to normalize.
*
* @return The normalized phone number.
*
* @throws InvalidAccountIDException If the given string is not a valid
* phone number.
*
*/
public static String normalizePhoneNum(String phoneNumber) throws InvalidAccountIDException {
if (StringUtils.isEmpty(phoneNumber)) {
throw new InvalidAccountIDException(String.format("Input phone number is empty or null."));
}
if (isValidPhoneNumber(phoneNumber) == false) {
throw new InvalidAccountIDException(String.format("Input string is not a valid phone number: %s", phoneNumber));
}
String normalizedNumber = phoneNumber.trim();
normalizedNumber = normalizedNumber.replaceAll("\\s+", ""); // remove spaces.
normalizedNumber = normalizedNumber.replaceAll("[\\-()]", ""); // remove parens & dashes.
// ensure a min length
if (normalizedNumber.length() < MIN_PHONENUMBER_LEN) {
throw new InvalidAccountIDException("Invalid phone number string " + phoneNumber);
}
return normalizedNumber;
}
/**
* Normalizes the given email address.
*
* @param emailAddress The email address string to be normalized.
*
* @return The normalized email address.
*
* @throws InvalidAccountIDException If the given string is not a valid
* email address.
*/
public static String normalizeEmailAddress(String emailAddress) throws InvalidAccountIDException {
if (StringUtils.isEmpty(emailAddress)) {
throw new InvalidAccountIDException(String.format("Input email address is empty or null."));
}
if (isValidEmailAddress(emailAddress) == false) {
throw new InvalidAccountIDException(String.format("Input string is not a valid email address: %s", emailAddress));
}
return emailAddress.toLowerCase().replace(";", "").trim();
}
/**
* Checks if the given accountId is a valid id for the specified account
* type.
*
* @param accountType Account type.
* @param accountUniqueID Id to check.
*
* @return True, if the id is a valid id for the given account type, False
* otherwise.
*/
public static boolean isValidAccountId(Account.Type accountType, String accountUniqueID) {
if (accountType == Account.Type.PHONE) {
return isValidPhoneNumber(accountUniqueID);
}
if (accountType == Account.Type.EMAIL) {
return isValidEmailAddress(accountUniqueID);
}
return !StringUtils.isEmpty(accountUniqueID);
}
/**
* Checks if the given string is a valid phone number.
*
* NOTE: this method intentionally performs a rather lenient validation of
* the phone number in order to not drop any collected data.
*
* @param phoneNum Phone number string to check.
*
* @return True if the given string is a valid phone number, false
* otherwise.
*/
public static boolean isValidPhoneNumber(String phoneNum) {
if (StringUtils.isEmpty(phoneNum)) {
return false;
}
String trimmedPhoneNum = phoneNum.trim();
// A phone number may have a leading '+', special telephony chars, or digits.
// Anything else implies an invalid phone number.
for (int i = 0; i < trimmedPhoneNum.length(); i++) {
if (!((trimmedPhoneNum.charAt(i) == '+' && i == 0) // a '+' is allowed only at the beginning
|| isValidPhoneChar(trimmedPhoneNum.charAt(i)))) {
return false;
}
}
return true;
}
/**
* Checks if the given character is a valid character for a phone number.
*
* @param ch Character to check.
*
* @return True, if its a valid phone number character, false, otherwise.
*/
private static boolean isValidPhoneChar(char ch) {
return Character.isSpaceChar(ch)
|| Character.isDigit(ch)
|| TELEPHONY_CHARS.contains(String.valueOf(ch));
}
/**
* Checks if the given string is a valid email address.
*
* Note: this method intentionally performs a rather lenient validation in
* order to not drop any collected data.
*
* Note: We are requiring that an email address have a "." on the right-hand
* side to allow us to differentiate between app-specific identifiers and
* email addresses. We realize that some emails can be sent within
* enterprises without a ".', but that this is less common than encountering
* app-specific identifiers of the form a(at)b.
*
* @param emailAddress String to check.
*
* @return True if the given string is a valid email address, false
* otherwise.
*/
public static boolean isValidEmailAddress(String emailAddress) {
if (StringUtils.isEmpty(emailAddress)) {
return false;
}
if (emailAddress.contains("@") == false
|| emailAddress.contains(".") == false ) {
return false;
}
// emsure there's a username and domain
String[] tokens = emailAddress.split("@");
if (tokens.length < 2
|| StringUtils.isEmpty(tokens[0])
|| StringUtils.isEmpty(tokens[1])) {
return false;
}
// ensure domain has name and suffix
String[] tokens2 = tokens[1].split("\\.");
return !(tokens2.length < 2
|| StringUtils.isEmpty(tokens2[0])
|| StringUtils.isEmpty(tokens2[1]));
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2011-2016 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.datamodel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* Interface for all datatypes that can be found in the database. Content
* objects make up a tree and each object can have a parent and children. For
* example, the child of an Image object is a Volume or File System. This
* interface defines the basic methods for reading the content associated with
* this object, the parent and children, and adding artifacts.
*/
public interface Content extends SleuthkitVisitableItem {
/**
* Reads data that this content object is associated with (file contents,
* volume contents, etc.).
*
* @param buf a character array of data (in bytes) to copy read data to
* @param offset byte offset in the content to start reading from
* @param len number of bytes to read into buf.
*
* @return num of bytes read, or -1 on error
*
* @throws TskCoreException if critical error occurred during read in the
* tsk core
*/
public int read(byte[] buf, long offset, long len) throws TskCoreException;
/**
* Free native resources after read is done on the Content object. After
* closing, read can be called again on the same Content object, which
* should result in re-opening of new native resources.
*/
public void close();
/**
* Get the (reported) size of the content object and, in theory, how much
* you should be able to read from it. In some cases, data corruption may
* mean that you cannot read this much data.
*
* @return size of the content in bytes
*/
public long getSize();
/**
* Visitor pattern support
*
* @param v visitor supplying an algorithm to run on the content object
*
* @return visitor return value resulting from running the algorithm
*/
public <T> T accept(ContentVisitor<T> v);
/**
* Get the name of this content object (does not include parent path)
*
* @return the name
*/
public String getName();
/**
* @return returns the full path to this Content object starting with a "/"
* followed by the Image name and similarly for all other segments
* in the hierarchy.
*/
public String getUniquePath() throws TskCoreException;
/**
* Returns the unique object ID that was assigned to it in the database.
* This is a Sleuth Kit database-assigned number.
*
* @return object id
*/
public long getId();
/**
* Gets the root data source (image, virtual directory, etc.) of this
* content.
*
* @return Content associated with data source or null if one can't be found
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public Content getDataSource() throws TskCoreException;
/**
* Gets the child content objects of this content.
*
* @return List of children
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public List<Content> getChildren() throws TskCoreException;
/**
* Returns true if the content object has children objects. Note, this
* should be more efficient than getting children and checking it empty.
*
* @return true if has children, false otherwise.
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public boolean hasChildren() throws TskCoreException;
/**
* Returns count of children objects. Note, this should be more efficient
* than getting children and counting them.
*
* @return children count
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public int getChildrenCount() throws TskCoreException;
/**
* @return returns the parent of this Content object or null if there isn't
* one as is the case for Image.
*
* @throws TskCoreException
*/
public Content getParent() throws TskCoreException;
/**
* Gets the child content ids of this content.
*
* @return List of children ids
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public List<Long> getChildrenIds() throws TskCoreException;
/**
* Create and add an artifact associated with this content to the blackboard
*
* @param artifactTypeID id of the artifact type (if the id doesn't already
* exist an exception will be thrown)
*
* @return the blackboard artifact created (the artifact type id can be
* looked up from this)
*
* @throws TskCoreException if critical error occurred within tsk core
* @deprecated Please use newDataArtifact or newAnalysisResult.
*/
@Deprecated
public BlackboardArtifact newArtifact(int artifactTypeID) throws TskCoreException;
/**
* Create and add an artifact associated with this content to the blackboard
*
* @param type artifact enum tyoe
*
* @return the blackboard artifact created (the artifact type id can be
* looked up from this)
*
* @throws TskCoreException if critical error occurred within tsk core
* @deprecated Please use newDataArtifact or newAnalysisResult.
*/
@Deprecated
public BlackboardArtifact newArtifact(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException;
/**
* Create and add an analysis result associated with this content.
*
*
* @param artifactType Type of analysis result artifact to create.
* @param score Score associated with this analysis.
* @param conclusion Conclusion from the analysis, may be empty.
* @param configuration Configuration element associated with this
* analysis, may be empty.
* @param justification Justification
* @param attributesList Additional attributes to attach to this analysis
* result artifact.
*
* @return AnalysisResultAdded The analysis return added and the current
* aggregate score of content.
*
* @throws TskCoreException if critical error occurred within tsk core.
*/
public AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, Score score, String conclusion, String configuration, String justification, Collection<BlackboardAttribute> attributesList) throws TskCoreException;
/**
* Create and add an analysis result associated with this content.
*
*
* @param artifactType Type of analysis result artifact to create.
* @param score Score associated with this analysis.
* @param conclusion Conclusion from the analysis, may be empty.
* @param configuration Configuration element associated with this
* analysis, may be empty.
* @param justification Justification
* @param attributesList Additional attributes to attach to this analysis
* result artifact.
* @param dataSourceId The data source for the analysis result
*
* @return AnalysisResultAdded The analysis return added and the current
* aggregate score of content.
*
* @throws TskCoreException if critical error occurred within tsk core.
*/
public AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, Score score, String conclusion, String configuration, String justification, Collection<BlackboardAttribute> attributesList, long dataSourceId) throws TskCoreException;
/**
* Create and add a data artifact associated with this abstract file. This
* method creates the data artifact with the os account id associated with
* this abstract file if one exists.
*
* @param artifactType Type of data artifact to create.
* @param attributesList Additional attributes to attach to this data
* artifact.
*
* @return DataArtifact New data artifact.
*
* @throws TskCoreException If a critical error occurred within tsk core.
*/
public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection<BlackboardAttribute> attributesList) throws TskCoreException;
/**
* Create and add a data artifact associated with this content.
*
* @param artifactType Type of analysis result artifact to create.
* @param attributesList Additional attributes to attach to this data
* artifact.
* @param osAccountId The OS account id associated with the artifact. May
* be null.
*
* @return DataArtifact New data artifact.
*
* @throws TskCoreException If a critical error occurred within tsk core.
*/
public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection<BlackboardAttribute> attributesList, Long osAccountId) throws TskCoreException;
/**
* Create and add a data artifact associated with this content.
*
* @param artifactType Type of analysis result artifact to create.
* @param attributesList Additional attributes to attach to this data
* artifact.
* @param osAccountId The OS account id associated with the artifact. May
* be null.
* @param dataSourceId The data source id of the artifact
*
* @return DataArtifact New data artifact.
*
* @throws TskCoreException If a critical error occurred within tsk core.
*/
public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection<BlackboardAttribute> attributesList, Long osAccountId, long dataSourceId) throws TskCoreException;
/**
* Returns the final score for the content object.
*
* @return Score.
*
* @throws TskCoreException if critical error occurred within tsk core.
*/
public Score getAggregateScore() throws TskCoreException;
/**
* Get all artifacts associated with this content that have the given type
* name
*
* @param artifactTypeName name of the type to look up
*
* @return a list of blackboard artifacts matching the type
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public ArrayList<BlackboardArtifact> getArtifacts(String artifactTypeName) throws TskCoreException;
/**
* Get all analysis results associated with this content, that have the
* given type.
*
* @param artifactType Type to look up.
*
* @return A list of analysis result artifacts matching the type.
*
* @throws TskCoreException If critical error occurred within tsk core.
*/
public List<AnalysisResult> getAnalysisResults(BlackboardArtifact.Type artifactType) throws TskCoreException;
/**
* Return the TSK_GEN_INFO artifact for the file so that individual
* attributes can be added to it. Creates one if it does not already exist.
*
* @return Instance of the TSK_GEN_INFO artifact
*
* @throws TskCoreException
*/
public BlackboardArtifact getGenInfoArtifact() throws TskCoreException;
/**
* Return the TSK_GEN_INFO artifact for the file so that individual
* attributes can be added to it. If one does not create, behavior depends
* on the create argument.
*
* @param create If true, an artifact will be created if it does not already
* exist.
*
* @return Instance of the TSK_GEN_INFO artifact or null if artifact does
* not already exist and create was set to false
*
* @throws TskCoreException
*/
public BlackboardArtifact getGenInfoArtifact(boolean create) throws TskCoreException;
/**
* Return attributes of a given type from TSK_GEN_INFO.
*
* @param attr_type Attribute type to find inside of the TSK_GEN_INFO
* artifact.
*
* @return Attributes
*/
public ArrayList<BlackboardAttribute> getGenInfoAttributes(BlackboardAttribute.ATTRIBUTE_TYPE attr_type) throws TskCoreException;
/**
* Get all artifacts associated with this content that have the given type
* id
*
* @param artifactTypeID type id to look up
*
* @return a list of blackboard artifacts matching the type
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public ArrayList<BlackboardArtifact> getArtifacts(int artifactTypeID) throws TskCoreException;
/**
* Get all artifacts associated with this content that have the given type
*
* @param type type to look up
*
* @return a list of blackboard artifacts matching the type
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public ArrayList<BlackboardArtifact> getArtifacts(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException;
/**
* Get all artifacts associated with this content
*
* @return a list of blackboard artifacts
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public ArrayList<BlackboardArtifact> getAllArtifacts() throws TskCoreException;
/**
* Get all data artifacts associated with this content.
*
* @return A list of data artifacts.
*
* @throws TskCoreException If critical error occurred within tsk core.
*/
public List<DataArtifact> getAllDataArtifacts() throws TskCoreException;
/**
* Get all analysis results associated with this content.
*
* @return A list of analysis results.
*
* @throws TskCoreException If critical error occurred within tsk core.
*/
public List<AnalysisResult> getAllAnalysisResults() throws TskCoreException;
/**
* Get the names of all the hashsets that this content is in.
*
* @return the names of the hashsets that this content is in
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public Set<String> getHashSetNames() throws TskCoreException;
/**
* Get count of all artifacts associated with this content that have the
* given type name
*
* @param artifactTypeName name of the type to look up
*
* @return count of blackboard artifacts matching the type
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public long getArtifactsCount(String artifactTypeName) throws TskCoreException;
/**
* Get count of all artifacts associated with this content that have the
* given type id
*
* @param artifactTypeID type id to look up
*
* @return count of blackboard artifacts matching the type
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public long getArtifactsCount(int artifactTypeID) throws TskCoreException;
/**
* Get count of all artifacts associated with this content that have the
* given type
*
* @param type type to look up
*
* @return count of blackboard artifacts matching the type
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public long getArtifactsCount(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException;
/**
* Get count of all artifacts associated with this content
*
* @return count of all blackboard artifacts for this content
*
* @throws TskCoreException if critical error occurred within tsk core
*/
public long getAllArtifactsCount() throws TskCoreException;
}
/*
* SleuthKit Java Bindings
*
* Copyright 2023 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.datamodel;
/**
* Custom provider for content bytes.
*/
@SuppressWarnings("try")
public interface ContentProviderStream extends AutoCloseable {
/**
* Reads data that this content object is associated with (file contents,
* volume contents, etc.).
*
* @param buf a character array of data (in bytes) to copy read data to
* @param offset byte offset in the content to start reading from
* @param len number of bytes to read into buf.
*
* @return num of bytes read, or -1 on error
*
* @throws TskCoreException if critical error occurred during read in the
* tsk core
*/
public int read(byte[] buf, long offset, long len) throws TskCoreException;
}
/*
* SleuthKit Java Bindings
*
* Copyright 2023 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.datamodel;
import java.util.Optional;
/**
* Custom provider for bytes of an abstract file.
*/
public interface ContentStreamProvider {
/**
* Provides a content stream for a content object or empty if this provider
* has none to provide.
*
* @param content The content.
*
* @return The content stream or empty if no stream can be provided for this
* content.
*/
Optional<ContentProviderStream> getContentStream(Content content) throws TskCoreException;
}
/*
* Sleuth Kit Data Model
*
* Copyright 2013-2018 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.datamodel;
/**
* Instances of this class are data transfer objects (DTOs) that represent tags
* a user can apply to content.
*/
public class ContentTag extends Tag {
private final Content content;
private final long beginByteOffset;
private final long endByteOffset;
// Clients of the org.sleuthkit.datamodel package should not directly create these objects.
ContentTag(long tagID, Content content, TagName name, String comment, long beginByteOffset, long endByteOffset, String userName) {
super(tagID, name, comment, userName);
this.content = content;
this.beginByteOffset = beginByteOffset;
this.endByteOffset = endByteOffset;
}
/**
* Return the tagged content
*
* @return tagged content
*/
public Content getContent() {
return content;
}
/**
* Returns whether the tag has a byte range
*
* @return true if the tag has a byte range, false otherwise
*/
public boolean hasByteExtent() {
return (beginByteOffset > 0) && (endByteOffset > 0) && (endByteOffset > beginByteOffset);
}
/**
* Returns starting offset of the byte range
*
* @return start offset
*/
public long getBeginByteOffset() {
return beginByteOffset;
}
/**
* Returns end offset of the byte range
*
* @return end offset
*/
public long getEndByteOffset() {
return endByteOffset;
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2011-2018 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.datamodel;
/**
* Interface for implementing a visitor pattern on all Content implementations.
* Visitor implements an algorithm on the content object. The algorithm is
* completely decoupled from the content object. The visitor pattern emulates
* double dispatch mechanism. It allows to act differently depending on the
* instance type, without need to test what the actual type is. E.g. it allows
* for processing a Content object hierarchy without using instanceof
* statements. Generic type parameter T is a return type from the visit methods.
*
* @param <T> return type of visit methods
*/
public interface ContentVisitor<T> {
/**
* Act on (visit) a Directory content object
*
* @param d the directory to visit / act on
*
* @return result of the visit
*/
T visit(Directory d);
/**
* Act on (visit) a File content object
*
* @param f File to visit / act on
*
* @return result of the visit
*/
T visit(File f);
/**
* Act on (visit) a FileSystem content object
*
* @param fs file system to visit / act on
*
* @return result of the visit
*/
T visit(FileSystem fs);
/**
* Act on (visit) an Image content object
*
* @param i image to visit / act on
*
* @return result of the visit
*/
T visit(Image i);
/**
* Act on (visit) a Pool content object
*
* @param p pool to visit / act on
*
* @return result of the visit
*/
T visit(Pool p);
/**
* Act on (visit) a Volume content object
*
* @param v volume to visit / act on
*
* @return result of the visit
*/
T visit(Volume v);
/**
* Act on (visit) a VolumeSystem content object
*
* @param vs volume system to visit / act on
*
* @return result of the visit
*/
T visit(VolumeSystem vs);
/**
* Act on (visit) a LayoutFile content object
*
* @param lf layout file to visit / act on
*
* @return result of the visit
*/
T visit(LayoutFile lf);
/**
* Act on (visit) a VirtualDirectory content object
*
* @param vd virtual dir to visit / act on
*
* @return result of the visit
*/
T visit(VirtualDirectory vd);
/**
* Act on (visit) a LocalDirectory content object
*
* @param ld local dir to visit / act on
*
* @return result of the visit
*/
T visit(LocalDirectory ld);
/**
* Act on (visit) a DerivedFile content object
*
* @param lf local file to visit / act on
*
* @return result of the visit
*/
T visit(DerivedFile lf);
/**
* Act on (visit) a LocalFile content object
*
* @param df derived file to visit / act on
*
* @return result of the visit
*/
T visit(LocalFile df);
/**
* Act on (visit) a SlackFile content object
*
* @param sf slack file to visit / act on
*
* @return result of the visit
*/
T visit(SlackFile sf);
/**
* Act on (visit) a blackboard artifact object
*
* @param ba blackboard artifact object to visit / act on
*
* @return result of the visit
*/
T visit(BlackboardArtifact ba);
/**
* Act on (visit) a Report object
*
* @param r report object to visit / act on
*
* @return result of the visit
*/
T visit(Report r);
/**
* Act on (visit) a OsAccount object
*
* @param act OsAccount object to visit / act on
*
* @return result of the visit
*/
T visit(OsAccount act);
/**
* Act on (visit) an UnsupportedContent object
*
* @param uc UnsupportedContent object to visit / act on
*
* @return result of the visit
*/
T visit(UnsupportedContent uc);
/**
* The default content visitor - quickest method for implementing a custom
* visitor. Every visit method delegates to the defaultVisit method, the
* only required method to be implemented. Then, implement the specific
* visit methods for the objects on which the algorithm needs to act
* differently.
*
* @param <T> generic type, signifies the object type to be returned from
* visit()
*/
static abstract public class Default<T> implements ContentVisitor<T> {
protected abstract T defaultVisit(Content c);
@Override
public T visit(Directory d) {
return defaultVisit(d);
}
@Override
public T visit(File f) {
return defaultVisit(f);
}
@Override
public T visit(FileSystem fs) {
return defaultVisit(fs);
}
@Override
public T visit(Image i) {
return defaultVisit(i);
}
@Override
public T visit(Volume v) {
return defaultVisit(v);
}
@Override
public T visit(Pool p) {
return defaultVisit(p);
}
@Override
public T visit(VolumeSystem vs) {
return defaultVisit(vs);
}
@Override
public T visit(LayoutFile lf) {
return defaultVisit(lf);
}
@Override
public T visit(VirtualDirectory ld) {
return defaultVisit(ld);
}
@Override
public T visit(LocalDirectory ld) {
return defaultVisit(ld);
}
@Override
public T visit(DerivedFile df) {
return defaultVisit(df);
}
@Override
public T visit(LocalFile lf) {
return defaultVisit(lf);
}
@Override
public T visit(SlackFile sf) {
return defaultVisit(sf);
}
@Override
public T visit(BlackboardArtifact ba) {
return defaultVisit(ba);
}
@Override
public T visit(Report r) {
return defaultVisit(r);
}
@Override
public T visit(OsAccount act) {
return defaultVisit(act);
}
@Override
public T visit(UnsupportedContent uc) {
return defaultVisit(uc);
}
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2020-2021 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.datamodel;
import java.util.Optional;
/**
* DataArtifact is a category of artifact types that are simply data directly
* extracted from a data source.
*/
public final class DataArtifact extends BlackboardArtifact {
// data artifacts may have a OS Account associated with them.
private final Long osAccountObjId;
/**
* Constructs a DataArtifact.
*
* @param sleuthkitCase The SleuthKit case (case database) that contains
* the artifact data.
* @param artifactID The unique id for this artifact.
* @param sourceObjId The unique id of the content with which this
* artifact is associated.
* @param artifactObjId The object id of artifact, in tsk_objects.
* @param dataSourceObjId Object ID of the data source where the artifact
* was found. May be null.
* @param artifactTypeID The type id of this artifact.
* @param artifactTypeName The type name of this artifact.
* @param displayName The display name of this artifact.
* @param reviewStatus The review status of this artifact.
* @param osAccountObjId OsAccount associated with this artifact, may be
* null.
* @param isNew The object is newly created.
*/
DataArtifact(SleuthkitCase sleuthkitCase, long artifactID, long sourceObjId, long artifactObjId, Long dataSourceObjId, int artifactTypeID, String artifactTypeName, String displayName, ReviewStatus reviewStatus, Long osAccountObjId, boolean isNew) {
super(sleuthkitCase, artifactID, sourceObjId, artifactObjId, dataSourceObjId, artifactTypeID, artifactTypeName, displayName, reviewStatus, isNew);
this.osAccountObjId = osAccountObjId;
}
/**
* Gets the OS Account for this artifact.
*
* @return Optional with OsAccount, Optional.empty if there is no account.
*
* @throws TskCoreException If there is an error getting the account.
*/
public Optional<Long> getOsAccountObjectId() throws TskCoreException {
return Optional.ofNullable(osAccountObjId);
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2011-2018 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.datamodel;
/**
* A data source (e.g., an image, a local disk, a virtual directory of logical
* files, etc.).
*/
public interface DataSource extends Content {
/**
* Gets the ASCII-printable identifier for the device associated with the
* data source. This identifier is intended to be unique across multiple
* cases (e.g., a UUID).
*
* @return The device id.
*/
String getDeviceId();
/**
* Gets the time zone that was used to process the data source.
*
* @return The time zone.
*/
String getTimeZone();
/**
* Set the name for this data source.
*
* @param newName The new name for the data source
*
* @throws TskCoreException Thrown if an error occurs while updating the database
*/
void setDisplayName(String newName) throws TskCoreException;
/**
* Gets the size of the contents of the data source in bytes. This size can
* change as archive files within the data source are expanded, files are
* carved, etc., and is different from the size of the data source as
* returned by Content.getSize, which is the size of the data source as a
* file.
*
* @param sleuthkitCase The sleuthkit case instance from which to make calls
* to the database.
*
* @return The size in bytes.
*
* @throws TskCoreException Thrown when there is an issue trying to retrieve
* data from the database.
*/
long getContentSize(SleuthkitCase sleuthkitCase) throws TskCoreException;
/**
* Sets the acquisition details field in the case database.
*
* @param details The acquisition details
*
* @throws TskCoreException Thrown if the data can not be written
*/
void setAcquisitionDetails(String details) throws TskCoreException;
/**
* Sets the acquisition tool details such as its name, version number and
* any settings used during the acquisition to acquire data.
*
* @param name The name of the acquisition tool. May be NULL.
* @param version The acquisition tool version number. May be NULL.
* @param settings The settings used by the acquisition tool. May be NULL.
*
* @throws TskCoreException Thrown if the data can not be written
*/
void setAcquisitionToolDetails(String name, String version, String settings) throws TskCoreException;
/**
* Gets the acquisition details field from the case database.
*
* @return The acquisition details
*
* @throws TskCoreException Thrown if the data can not be read
*/
String getAcquisitionDetails() throws TskCoreException;
/**
* Gets the acquisition tool settings field from the case database.
*
* @return The acquisition tool settings. May be Null if not set.
*
* @throws TskCoreException Thrown if the data can not be read
*/
String getAcquisitionToolSettings() throws TskCoreException;
/**
* Gets the acquisition tool name field from the case database.
*
* @return The acquisition tool name. May be Null if not set.
*
* @throws TskCoreException Thrown if the data can not be read
*/
String getAcquisitionToolName() throws TskCoreException;
/**
* Gets the acquisition tool version field from the case database.
*
* @return The acquisition tool version. May be Null if not set.
*
* @throws TskCoreException Thrown if the data can not be read
*/
String getAcquisitionToolVersion() throws TskCoreException;
/**
* Gets the added date field from the case database.
*
* @return The date time when the image was added in epoch seconds.
*
* @throws TskCoreException Thrown if the data can not be read
*/
Long getDateAdded() throws TskCoreException;
/**
* Gets the host for this data source.
*
* @return The host
*
* @throws TskCoreException
*/
Host getHost() throws TskCoreException;
}