Compare commits

..

1 Commits

Author SHA1 Message Date
Michael Kirk
40eb2a0f4c return unseen for list of recipient ids
// FREEBIE
2017-06-02 09:46:20 -07:00
124 changed files with 1628 additions and 4419 deletions

1
.gitignore vendored
View File

@ -17,7 +17,6 @@ DerivedData
*.hmap
*.ipa
*.xcuserstate
xcshareddata
Pods/

View File

@ -1,17 +1,17 @@
language: objective-c
osx_image: xcode8.3
osx_image: xcode8
env:
-EARLY_START_SIMULATOR=1 # early starting simulator reduces false negatives due to test timeouts
before_install:
- brew update
- bundle
- bundle exec pod repo update --silent
- gem install cocoapods xcpretty --no-ri --no-rdoc
- pod repo update --silent
after_failure:
- sleep 10 # This prevents the occasional output truncation that happens when piping to xcpretty.
script: make scan_test
script: make

View File

@ -11,19 +11,3 @@ target 'TSKitiOSTestApp' do
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.to_s == "SignalServiceKit"
puts "--[!] Disabling singleton enforcement for SSK."
target.build_configurations.each do |config|
existing_definitions = config.build_settings['GCC_PREPROCESSOR_DEFINITIONS']
if existing_definitions == nil || existing.length == 0
existing_definitions = "$(inheritied)"
end
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = "#{existing_definitions} SSK_BUILDING_FOR_TESTS=1"
config.build_settings['OTHER_SWIFT_FLAGS'] = ['$(inherited)', '-DSSK_BUILDING_FOR_TESTS']
end
end
end
end

View File

@ -17,7 +17,6 @@ PODS:
- AFNetworking/NSURLSession
- AxolotlKit (0.8.1):
- 25519 (~> 2.0.1)
- CocoaLumberjack
- HKDFKit (~> 0.0.3)
- ProtocolBuffers (~> 1.9.8)
- CocoaLumberjack (2.4.0):
@ -29,11 +28,12 @@ PODS:
- CocoaLumberjack/Extensions (2.4.0):
- CocoaLumberjack/Default
- HKDFKit (0.0.3)
- libPhoneNumber-iOS (0.9.10)
- libPhoneNumber-iOS (0.9.4)
- Mantle (2.1.0):
- Mantle/extobjc (= 2.1.0)
- Mantle/extobjc (2.1.0)
- ProtocolBuffers (1.9.11)
- Reachability (3.2)
- SAMKeychain (1.5.2)
- SignalServiceKit (0.9.0):
- '25519'
@ -45,7 +45,7 @@ PODS:
- SAMKeychain
- SocketRocket
- TwistedOakCollapsingFutures
- YapDatabase/SQLCipher (~> 2.9.3)
- YapDatabase/SQLCipher
- SocketRocket (0.5.1)
- SQLCipher/common (3.4.1)
- SQLCipher/fts (3.4.1):
@ -53,53 +53,54 @@ PODS:
- TwistedOakCollapsingFutures (1.0.0):
- UnionFind (~> 1.0)
- UnionFind (1.0.1)
- YapDatabase/SQLCipher (2.9.3):
- YapDatabase/SQLCipher/Core (= 2.9.3)
- YapDatabase/SQLCipher/Extensions (= 2.9.3)
- YapDatabase/SQLCipher/Core (2.9.3):
- YapDatabase/SQLCipher (2.9.2):
- YapDatabase/SQLCipher/Core (= 2.9.2)
- YapDatabase/SQLCipher/Extensions (= 2.9.2)
- YapDatabase/SQLCipher/Core (2.9.2):
- CocoaLumberjack (~> 2)
- SQLCipher/fts
- YapDatabase/SQLCipher/Extensions (2.9.3):
- YapDatabase/SQLCipher/Extensions (2.9.2):
- YapDatabase/SQLCipher/Core
- YapDatabase/SQLCipher/Extensions/ActionManager (= 2.9.3)
- YapDatabase/SQLCipher/Extensions/CloudKit (= 2.9.3)
- YapDatabase/SQLCipher/Extensions/ConnectionProxy (= 2.9.3)
- YapDatabase/SQLCipher/Extensions/CrossProcessNotification (= 2.9.3)
- YapDatabase/SQLCipher/Extensions/FilteredViews (= 2.9.3)
- YapDatabase/SQLCipher/Extensions/FullTextSearch (= 2.9.3)
- YapDatabase/SQLCipher/Extensions/Hooks (= 2.9.3)
- YapDatabase/SQLCipher/Extensions/Relationships (= 2.9.3)
- YapDatabase/SQLCipher/Extensions/RTreeIndex (= 2.9.3)
- YapDatabase/SQLCipher/Extensions/SearchResults (= 2.9.3)
- YapDatabase/SQLCipher/Extensions/SecondaryIndex (= 2.9.3)
- YapDatabase/SQLCipher/Extensions/Views (= 2.9.3)
- YapDatabase/SQLCipher/Extensions/ActionManager (2.9.3):
- YapDatabase/SQLCipher/Extensions/ActionManager (= 2.9.2)
- YapDatabase/SQLCipher/Extensions/CloudKit (= 2.9.2)
- YapDatabase/SQLCipher/Extensions/ConnectionProxy (= 2.9.2)
- YapDatabase/SQLCipher/Extensions/CrossProcessNotification (= 2.9.2)
- YapDatabase/SQLCipher/Extensions/FilteredViews (= 2.9.2)
- YapDatabase/SQLCipher/Extensions/FullTextSearch (= 2.9.2)
- YapDatabase/SQLCipher/Extensions/Hooks (= 2.9.2)
- YapDatabase/SQLCipher/Extensions/Relationships (= 2.9.2)
- YapDatabase/SQLCipher/Extensions/RTreeIndex (= 2.9.2)
- YapDatabase/SQLCipher/Extensions/SearchResults (= 2.9.2)
- YapDatabase/SQLCipher/Extensions/SecondaryIndex (= 2.9.2)
- YapDatabase/SQLCipher/Extensions/Views (= 2.9.2)
- YapDatabase/SQLCipher/Extensions/ActionManager (2.9.2):
- Reachability (~> 3)
- YapDatabase/SQLCipher/Core
- YapDatabase/SQLCipher/Extensions/Views
- YapDatabase/SQLCipher/Extensions/CloudKit (2.9.3):
- YapDatabase/SQLCipher/Extensions/CloudKit (2.9.2):
- YapDatabase/SQLCipher/Core
- YapDatabase/SQLCipher/Extensions/ConnectionProxy (2.9.3):
- YapDatabase/SQLCipher/Extensions/ConnectionProxy (2.9.2):
- YapDatabase/SQLCipher/Core
- YapDatabase/SQLCipher/Extensions/CrossProcessNotification (2.9.3):
- YapDatabase/SQLCipher/Extensions/CrossProcessNotification (2.9.2):
- YapDatabase/SQLCipher/Core
- YapDatabase/SQLCipher/Extensions/FilteredViews (2.9.3):
- YapDatabase/SQLCipher/Extensions/FilteredViews (2.9.2):
- YapDatabase/SQLCipher/Core
- YapDatabase/SQLCipher/Extensions/Views
- YapDatabase/SQLCipher/Extensions/FullTextSearch (2.9.3):
- YapDatabase/SQLCipher/Extensions/FullTextSearch (2.9.2):
- YapDatabase/SQLCipher/Core
- YapDatabase/SQLCipher/Extensions/Hooks (2.9.3):
- YapDatabase/SQLCipher/Extensions/Hooks (2.9.2):
- YapDatabase/SQLCipher/Core
- YapDatabase/SQLCipher/Extensions/Relationships (2.9.3):
- YapDatabase/SQLCipher/Extensions/Relationships (2.9.2):
- YapDatabase/SQLCipher/Core
- YapDatabase/SQLCipher/Extensions/RTreeIndex (2.9.3):
- YapDatabase/SQLCipher/Extensions/RTreeIndex (2.9.2):
- YapDatabase/SQLCipher/Core
- YapDatabase/SQLCipher/Extensions/SearchResults (2.9.3):
- YapDatabase/SQLCipher/Extensions/SearchResults (2.9.2):
- YapDatabase/SQLCipher/Core
- YapDatabase/SQLCipher/Extensions/FullTextSearch
- YapDatabase/SQLCipher/Extensions/Views
- YapDatabase/SQLCipher/Extensions/SecondaryIndex (2.9.3):
- YapDatabase/SQLCipher/Extensions/SecondaryIndex (2.9.2):
- YapDatabase/SQLCipher/Core
- YapDatabase/SQLCipher/Extensions/Views (2.9.3):
- YapDatabase/SQLCipher/Extensions/Views (2.9.2):
- YapDatabase/SQLCipher/Core
DEPENDENCIES:
@ -111,13 +112,13 @@ EXTERNAL SOURCES:
AxolotlKit:
:git: https://github.com/WhisperSystems/SignalProtocolKit.git
SignalServiceKit:
:path: ../../SignalServiceKit.podspec
:path: "../../SignalServiceKit.podspec"
SocketRocket:
:git: https://github.com/facebook/SocketRocket.git
CHECKOUT OPTIONS:
AxolotlKit:
:commit: 28afe5c1dbcfdea73d147e464c53d191d1e3ea50
:commit: a3c843cc8a423c5924c663490978f81dba34d04e
:git: https://github.com/WhisperSystems/SignalProtocolKit.git
SocketRocket:
:commit: 877ac7438be3ad0b45ef5ca3969574e4b97112bf
@ -126,20 +127,21 @@ CHECKOUT OPTIONS:
SPEC CHECKSUMS:
'25519': dc4bad7e2dbcbf1efa121068a705a44cd98c80fc
AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67
AxolotlKit: a9530d6835baae0f204b1f6b9dd79b7901176f0d
AxolotlKit: 240c7d761e4b1be9c6de78ebec498aaeedc978f4
CocoaLumberjack: aa9dcab71bdf9eaf2a63bbd9ddc87863efe45457
HKDFKit: c058305d6f64b84f28c50bd7aa89574625bcb62a
libPhoneNumber-iOS: f721ae4d5854bce60934f9fb9b0b28e8e68913cb
libPhoneNumber-iOS: 63bab980d1fc9783d82d955800ac9d7c1d81fde3
Mantle: 2fa750afa478cd625a94230fbf1c13462f29395b
ProtocolBuffers: d509225eb2ea43d9582a59e94348fcf86e2abd65
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
SAMKeychain: 1865333198217411f35327e8da61b43de79b635b
SignalServiceKit: 2ad8d86da055e24ac3ea0354ec1d4b13251af28f
SignalServiceKit: 59a79a51b89b963ba94db30cc99ed5212da0bb9f
SocketRocket: dbb1554b8fc288ef8ef370d6285aeca7361be31e
SQLCipher: 43d12c0eb9c57fb438749618fc3ce0065509a559
TwistedOakCollapsingFutures: f359b90f203e9ab13dfb92c9ff41842a7fe1cd0c
UnionFind: c33be5adb12983981d6e827ea94fc7f9e370f52d
YapDatabase: cd911121580ff16675f65ad742a9eb0ab4d9e266
YapDatabase: b1e43555a34a5298e23a045be96817a5ef0da58f
PODFILE CHECKSUM: a0f4507b6b4e6f9da3250901b06187a67236e083
PODFILE CHECKSUM: 1a7633963dbcaa43f298949d83c42c1cd1dce940
COCOAPODS: 1.2.1
COCOAPODS: 1.2.0

View File

@ -457,7 +457,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
276B029791E679B0E87877B7 /* [CP] Copy Pods Resources */ = {
@ -532,7 +532,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */

View File

@ -28,7 +28,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FF3F51F81980908EDE1836B76AA3A1EC"
BlueprintIdentifier = "1D0826C97B09E58C83B3C228FBC535FA"
BuildableName = "libSignalServiceKit.a"
BlueprintName = "SignalServiceKit"
ReferencedContainer = "container:Pods/Pods.xcodeproj">

View File

@ -1,5 +0,0 @@
source 'https://rubygems.org'
gem 'fastlane'
gem 'cocoapods'

View File

@ -1,189 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (2.3.5)
activesupport (4.2.9)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.5.1)
public_suffix (~> 2.0, >= 2.0.2)
babosa (1.0.2)
claide (1.0.2)
cocoapods (1.2.1)
activesupport (>= 4.0.2, < 5)
claide (>= 1.0.1, < 2.0)
cocoapods-core (= 1.2.1)
cocoapods-deintegrate (>= 1.0.1, < 2.0)
cocoapods-downloader (>= 1.1.3, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-stats (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.2.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (~> 2.0.1)
gh_inspector (~> 1.0)
molinillo (~> 0.5.7)
nap (~> 1.0)
ruby-macho (~> 1.1)
xcodeproj (>= 1.4.4, < 2.0)
cocoapods-core (1.2.1)
activesupport (>= 4.0.2, < 5)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
cocoapods-deintegrate (1.0.1)
cocoapods-downloader (1.1.3)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.0)
cocoapods-stats (1.0.0)
cocoapods-trunk (1.2.0)
nap (>= 0.8, < 2.0)
netrc (= 0.7.8)
cocoapods-try (1.1.0)
colored (1.2)
colored2 (3.1.2)
commander-fastlane (4.4.5)
highline (~> 1.7.2)
declarative (0.0.9)
declarative-option (0.1.0)
domain_name (0.5.20170404)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.2.1)
escape (0.0.4)
excon (0.57.1)
faraday (0.12.1)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4)
http-cookie (~> 1.0.0)
faraday_middleware (0.11.0.1)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.0)
fastlane (2.46.0)
CFPropertyList (>= 2.3, < 3.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
bundler (>= 1.12.0, < 2.0.0)
colored
commander-fastlane (>= 4.4.5, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0)
excon (>= 0.45.0, < 1.0.0)
faraday (~> 0.9)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 0.9)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.0.1, < 2.0.0)
google-api-client (>= 0.12.0, < 0.13.0)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
mini_magick (~> 4.5.1)
multi_json
multi_xml (~> 0.5)
multipart-post (~> 2.0.0)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 1.1.0, < 2.0.0)
security (= 0.1.3)
slack-notifier (>= 1.3, < 2.0.0)
terminal-notifier (>= 1.6.2, < 2.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (~> 0.5.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.4.4, < 2.0.0)
xcpretty (>= 0.2.4, < 1.0.0)
xcpretty-travis-formatter (>= 0.0.3)
fourflusher (2.0.1)
fuzzy_match (2.0.4)
gh_inspector (1.0.3)
google-api-client (0.12.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.5)
httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
googleauth (0.5.1)
faraday (~> 0.9)
jwt (~> 1.4)
logging (~> 2.0)
memoist (~> 0.12)
multi_json (~> 1.11)
os (~> 0.9)
signet (~> 0.7)
highline (1.7.8)
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
i18n (0.8.6)
json (2.1.0)
jwt (1.5.6)
little-plugger (1.1.4)
logging (2.2.2)
little-plugger (~> 1.1)
multi_json (~> 1.10)
memoist (0.16.0)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mini_magick (4.5.1)
minitest (5.10.2)
molinillo (0.5.7)
multi_json (1.12.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
nanaimo (0.2.3)
nap (1.1.0)
netrc (0.7.8)
os (0.9.6)
plist (3.3.0)
public_suffix (2.0.5)
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
retriable (3.0.2)
rouge (2.0.7)
ruby-macho (1.1.0)
rubyzip (1.2.1)
security (0.1.3)
signet (0.7.3)
addressable (~> 2.3)
faraday (~> 0.9)
jwt (~> 1.5)
multi_json (~> 1.10)
slack-notifier (1.5.1)
terminal-notifier (1.8.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
tty-screen (0.5.0)
tzinfo (1.2.3)
thread_safe (~> 0.1)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.4)
unicode-display_width (1.3.0)
word_wrap (1.0.0)
xcodeproj (1.5.0)
CFPropertyList (~> 2.3.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.2.3)
xcpretty (0.2.8)
rouge (~> 2.0.7)
xcpretty-travis-formatter (0.0.4)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
ruby
DEPENDENCIES
cocoapods
fastlane
BUNDLED WITH
1.14.6

View File

@ -14,11 +14,9 @@ default: test
test: pod_install retest
scan_test: pod_install scan
pod_install:
cd $(WORKING_DIR) && \
bundle exec pod install
pod install
build: pod_install
cd $(WORKING_DIR) && \
@ -30,9 +28,6 @@ retest: optional_early_start_simulator
-destination '${BUILD_DESTINATION}' \
test | xcpretty
scan:
bundle exec fastlane scan
clean:
cd $(WORKING_DIR) && \
$(XCODE_BUILD) \

View File

@ -1,27 +1,3 @@
# SignalServiceKit has Moved
Per https://github.com/WhisperSystems/Signal-iOS/pull/2341 we've moved
the SignalServiceKit codebase into the primary Signal-iOS repository at
https://github.com/WhisperSystems/Signal-iOS. As such, this repository
will no longer be updated.
Don't worry - we will continue to make updates to SignalServiceKit, and
you can continue to use it in your projects as before. The only
difference is where the code lives.
If you are using Cocoapods, staying up to date is as simple as modifying
a line in your Podfile from this:
```
- pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git'
```
To this:
```
+ pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/Signal-iOS.git'
```
# SignalServiceKit
SignalServiceKit is an Objective-C library for communicating with the Signal

View File

@ -17,37 +17,15 @@ An Objective-C library for communicating with the Signal messaging service.
s.homepage = "https://github.com/WhisperSystems/SignalServiceKit"
s.license = 'GPLv3'
s.author = { "Whisper Systems" => "ios@whispersystems.com" }
s.author = { "Frederic Jacobs" => "github@fredericjacobs.com" }
s.source = { :git => "https://github.com/WhisperSystems/SignalServiceKit.git", :tag => s.version.to_s }
s.social_media_url = 'https://twitter.com/WhipserSystems'
deprecation_message = <<EOS
installing SignalServiceKit via the Signal-iOS repository.
To get future updates, point your Podfile at the new location. Simply change this:
pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git'
To this:
pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/Signal-iOS.git'
Sorry for the disruption!
EOS
s.deprecated_in_favor_of = deprecation_message
s.social_media_url = 'https://twitter.com/FredericJacobs'
s.platform = :ios, '8.0'
#s.ios.deployment_target = '8.0'
#s.osx.deployment_target = '10.9'
s.requires_arc = true
# By not including any actual files, upgrading users will see
# that they need to point upgrades to the new source at
# https://github.com/WhisperSystems/Signal-iOS
# Details in README.md
s.source_files = 'README.md'
s.source_files = 'src/**/*.{h,m,mm}'
s.resources = ['src/Security/PinningCertificate/textsecure.cer',
'src/Security/PinningCertificate/GIAG2.crt']

1
fastlane/.gitignore vendored
View File

@ -1 +0,0 @@
test_output

View File

@ -1,8 +0,0 @@
# For more information about this configuration visit
# https://github.com/fastlane/fastlane/tree/master/scan#scanfile
workspace "Example/TSKitiOSTestApp/TSKitiOSTestApp.xcworkspace"
scheme "TSKitiOSTestApp"
devices ["iPhone SE"]

View File

@ -34,24 +34,6 @@ message Content {
optional DataMessage dataMessage = 1;
optional SyncMessage syncMessage = 2;
optional CallMessage callMessage = 3;
optional NullMessage nullMessage = 4;
}
message NullMessage {
optional bytes padding = 1;
}
message Verified {
enum State {
DEFAULT = 0;
VERIFIED = 1;
UNVERIFIED = 2;
}
optional string destination = 1;
optional bytes identityKey = 2;
optional State state = 3;
optional bytes nullMessage = 4;
}
message CallMessage {
@ -148,8 +130,6 @@ message SyncMessage {
optional Request request = 4;
repeated Read read = 5;
optional Blocked blocked = 6;
optional Verified verified = 7;
optional bytes padding = 8;
}
message AttachmentPointer {
@ -192,7 +172,6 @@ message ContactDetails {
optional string name = 2;
optional Avatar avatar = 3;
optional string color = 4;
optional Verified verified = 5;
}
message GroupDetails {

View File

@ -4,7 +4,6 @@
#import "TSPreKeyManager.h"
#import "NSURLSessionDataTask+StatusCode.h"
#import "OWSIdentityManager.h"
#import "TSNetworkManager.h"
#import "TSRegisterSignedPrekeyRequest.h"
#import "TSStorageHeaders.h"
@ -128,11 +127,11 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
RefreshPreKeysMode modeCopy = mode;
TSStorageManager *storageManager = [TSStorageManager sharedManager];
ECKeyPair *identityKeyPair = [[OWSIdentityManager sharedManager] identityKeyPair];
ECKeyPair *identityKeyPair = [storageManager identityKeyPair];
if (!identityKeyPair) {
[[OWSIdentityManager sharedManager] generateNewIdentityKey];
identityKeyPair = [[OWSIdentityManager sharedManager] identityKeyPair];
[storageManager generateNewIdentityKey];
identityKeyPair = [storageManager identityKeyPair];
// Switch modes if necessary.
modeCopy = RefreshPreKeysMode_SignedAndOneTime;
@ -153,12 +152,11 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
// Store the new one-time keys immediately, before they are sent to the
// service to prevent race conditions and other edge cases.
[storageManager storePreKeyRecords:preKeys];
request = [[TSRegisterPrekeysRequest alloc]
initWithPrekeyArray:preKeys
identityKey:identityKeyPair.publicKey
signedPreKeyRecord:signedPreKey
preKeyLastResort:lastResortPreKey];
request = [[TSRegisterPrekeysRequest alloc] initWithPrekeyArray:preKeys
identityKey:[storageManager identityKeyPair].publicKey
signedPreKeyRecord:signedPreKey
preKeyLastResort:lastResortPreKey];
} else {
description = @"just signed prekey";
request = [[TSRegisterSignedPrekeyRequest alloc] initWithSignedPreKeyRecord:signedPreKey];

View File

@ -244,12 +244,13 @@ NS_ASSUME_NONNULL_BEGIN
- (NSArray<NSString *> *)textSecureIdentifiers {
__block NSMutableArray *identifiers = [NSMutableArray array];
[[TSStorageManager sharedManager].dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
for (PhoneNumber *number in self.parsedPhoneNumbers) {
if ([SignalRecipient recipientWithTextSecureIdentifier:number.toE164 withTransaction:transaction]) {
[identifiers addObject:number.toE164];
}
}
[[TSStorageManager sharedManager]
.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
for (PhoneNumber *number in self.parsedPhoneNumbers) {
if ([SignalRecipient recipientWithTextSecureIdentifier:number.toE164 withTransaction:transaction]) {
[identifiers addObject:number.toE164];
}
}
}];
return [identifiers copy];
}

View File

@ -122,11 +122,11 @@ NS_ASSUME_NONNULL_BEGIN
}
NSMutableSet *recipientIds = [NSMutableSet set];
[[TSStorageManager sharedManager].dbReadConnection
readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
NSArray *allRecipientKeys = [transaction allKeysInCollection:[SignalRecipient collection]];
[recipientIds addObjectsFromArray:allRecipientKeys];
}];
[[TSStorageManager sharedManager]
.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
NSArray *allRecipientKeys = [transaction allKeysInCollection:[SignalRecipient collection]];
[recipientIds addObjectsFromArray:allRecipientKeys];
}];
NSMutableSet<NSString *> *allContacts = [[abPhoneNumbers setByAddingObjectsFromSet:recipientIds] mutableCopy];
@ -135,7 +135,7 @@ NS_ASSUME_NONNULL_BEGIN
[recipientIds minusSet:matchedIds];
// Cleaning up unregistered identifiers
[[TSStorageManager sharedManager].dbReadWriteConnection
[[TSStorageManager sharedManager].dbConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (NSString *identifier in recipientIds) {
SignalRecipient *recipient =
@ -185,22 +185,23 @@ NS_ASSUME_NONNULL_BEGIN
}
// Insert or update contact attributes
[[TSStorageManager sharedManager].dbReadWriteConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (NSString *identifier in attributesForIdentifier) {
SignalRecipient *recipient =
[SignalRecipient recipientWithTextSecureIdentifier:identifier withTransaction:transaction];
if (!recipient) {
recipient = [[SignalRecipient alloc] initWithTextSecureIdentifier:identifier relay:nil];
}
[[TSStorageManager sharedManager]
.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (NSString *identifier in attributesForIdentifier) {
SignalRecipient *recipient =
[SignalRecipient recipientWithTextSecureIdentifier:identifier withTransaction:transaction];
if (!recipient) {
recipient = [[SignalRecipient alloc] initWithTextSecureIdentifier:identifier
relay:nil];
}
NSDictionary *attributes = [attributesForIdentifier objectForKey:identifier];
NSDictionary *attributes = [attributesForIdentifier objectForKey:identifier];
recipient.relay = attributes[@"relay"];
recipient.relay = attributes[@"relay"];
[recipient saveWithTransaction:transaction];
}
}];
[recipient saveWithTransaction:transaction];
}
}];
success([NSSet setWithArray:attributesForIdentifier.allKeys]);
}

View File

@ -22,6 +22,7 @@ static NSString *const RPDefaultsKeyPhoneNumberCanonical = @"RPDefaultsKeyPhoneN
NBPhoneNumber *number = [phoneUtil parse:text defaultRegion:regionCode error:&parseError];
if (parseError) {
DDLogDebug(@"Issue while parsing number: %@", [parseError description]);
return nil;
}

View File

@ -85,9 +85,6 @@
NSDictionary *countryCodeComponent = @{NSLocaleCountryCode : countryCode};
NSString *identifier = [NSLocale localeIdentifierFromComponents:countryCodeComponent];
NSString *country = [NSLocale.currentLocale displayNameForKey:NSLocaleIdentifier value:identifier];
if (country.length < 1) {
country = [NSLocale.systemLocale displayNameForKey:NSLocaleIdentifier value:identifier];
}
return country;
}

View File

@ -3,8 +3,8 @@
//
#import "SignalRecipient.h"
#import "OWSIdentityManager.h"
#import "TSStorageHeaders.h"
#import "TSStorageManager+IdentityKeyStore.h"
NS_ASSUME_NONNULL_BEGIN
@ -37,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (nullable instancetype)recipientWithTextSecureIdentifier:(NSString *)textSecureIdentifier
{
__block SignalRecipient *recipient;
[self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
recipient = [self recipientWithTextSecureIdentifier:textSecureIdentifier withTransaction:transaction];
}];
return recipient;

View File

@ -72,6 +72,7 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)hasSafetyNumbers;
- (void)markAllAsRead;
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
/**

View File

@ -6,7 +6,6 @@
#import "OWSReadTracking.h"
#import "TSDatabaseView.h"
#import "TSIncomingMessage.h"
#import "TSInfoMessage.h"
#import "TSInteraction.h"
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
#import "TSOutgoingMessage.h"
@ -131,7 +130,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)enumerateInteractionsUsingBlock:(void (^)(TSInteraction *interaction))block
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self enumerateInteractionsWithTransaction:transaction
usingBlock:^(
TSInteraction *interaction, YapDatabaseReadTransaction *transaction) {
@ -172,7 +171,7 @@ NS_ASSUME_NONNULL_BEGIN
- (NSUInteger)numberOfInteractions
{
__block NSUInteger count;
[[self dbReadConnection] readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[[self dbConnection] readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName];
count = [interactionsByThread numberOfItemsInGroup:self.uniqueId];
}];
@ -193,13 +192,14 @@ NS_ASSUME_NONNULL_BEGIN
- (NSArray<id<OWSReadTracking>> *)unseenMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction
{
NSMutableArray<id<OWSReadTracking>> *messages = [NSMutableArray new];
[[TSDatabaseView unseenDatabaseViewExtension:transaction]
[[transaction ext:TSUnseenDatabaseViewExtensionName]
enumerateRowsInGroup:self.uniqueId
usingBlock:^(
NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) {
if (![object conformsToProtocol:@protocol(OWSReadTracking)]) {
OWSFail(@"%@ Unexpected object in unseen messages: %@", self.tag, object);
DDLogError(@"%@ Unexpected object in unseen messages: %@", self.tag, object);
OWSFail(@"Unexpected object in unseen messages.");
return;
}
[messages addObject:(id<OWSReadTracking>)object];
@ -228,42 +228,26 @@ NS_ASSUME_NONNULL_BEGIN
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
for (id<OWSReadTracking> message in [self unseenMessagesWithTransaction:transaction]) {
[message markAsReadWithTransaction:transaction sendReadReceipt:YES updateExpiration:YES];
[message markAsReadLocallyWithTransaction:transaction];
}
// Just to be defensive, we'll also check for unread messages.
OWSAssert([self unseenMessagesWithTransaction:transaction].count < 1);
}
- (TSInteraction *) lastInteraction {
__block TSInteraction *last;
[TSStorageManager.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
last = [[transaction ext:TSMessageDatabaseViewExtensionName] lastObjectInGroup:self.uniqueId];
- (void)markAllAsRead
{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self markAllAsReadWithTransaction:transaction];
}];
return last;
}
- (TSInteraction *)lastInteractionForInbox
{
__block TSInteraction *last = nil;
[TSStorageManager.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[[transaction ext:TSMessageDatabaseViewExtensionName]
enumerateRowsInGroup:self.uniqueId
withOptions:NSEnumerationReverse
usingBlock:^(
NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) {
OWSAssert([object isKindOfClass:[TSInteraction class]]);
TSInteraction *interaction = (TSInteraction *)object;
if ([TSThread shouldInteractionAppearInInbox:interaction]) {
last = interaction;
*stop = YES;
}
}];
- (TSInteraction *) lastInteraction {
__block TSInteraction *last;
[TSStorageManager.sharedManager.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction){
last = [[transaction ext:TSMessageDatabaseViewExtensionName] lastObjectInGroup:self.uniqueId];
}];
return last;
return (TSInteraction *)last;
}
- (NSDate *)lastMessageDate {
@ -275,49 +259,29 @@ NS_ASSUME_NONNULL_BEGIN
}
- (NSString *)lastMessageLabel {
TSInteraction *interaction = self.lastInteractionForInbox;
if (interaction == nil) {
if (self.lastInteraction == nil) {
return @"";
} else {
return interaction.description;
return [self lastInteraction].description;
}
}
// Returns YES IFF the interaction should show up in the inbox as the last message.
+ (BOOL)shouldInteractionAppearInInbox:(TSInteraction *)interaction
{
OWSAssert(interaction);
if (interaction.isDynamicInteraction) {
return NO;
}
if ([interaction isKindOfClass:[TSErrorMessage class]]) {
TSErrorMessage *errorMessage = (TSErrorMessage *)interaction;
if (errorMessage.errorType == TSErrorMessageNonBlockingIdentityChange) {
// Otherwise all group threads with the recipient will percolate to the top of the inbox, even though
// there was no meaningful interaction.
return NO;
}
} else if ([interaction isKindOfClass:[TSInfoMessage class]]) {
TSInfoMessage *infoMessage = (TSInfoMessage *)interaction;
if (infoMessage.messageType == TSInfoMessageVerificationStateChange) {
return NO;
}
}
return YES;
}
- (void)updateWithLastMessage:(TSInteraction *)lastMessage transaction:(YapDatabaseReadWriteTransaction *)transaction {
OWSAssert(lastMessage);
OWSAssert(transaction);
NSDate *lastMessageDate = [lastMessage dateForSorting];
if (![self.class shouldInteractionAppearInInbox:lastMessage]) {
return;
if ([lastMessage isKindOfClass:[TSErrorMessage class]]) {
TSErrorMessage *errorMessage = (TSErrorMessage *)lastMessage;
if (errorMessage.errorType == TSErrorMessageNonBlockingIdentityChange) {
// Otherwise all group threads with the recipient will percolate to the top of the inbox, even though
// there was no meaningful interaction.
DDLogDebug(@"%@ not updating lastMessage for thread:%@ nonblocking identity change: %@",
self.tag,
self,
errorMessage.debugDescription);
return;
}
}
NSDate *lastMessageDate = [lastMessage dateForSorting];
if (!_lastMessageDate || [lastMessageDate timeIntervalSinceDate:self.lastMessageDate] > 0) {
_lastMessageDate = lastMessageDate;
@ -397,7 +361,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)updateWithMutedUntilDate:(NSDate *)mutedUntilDate
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self applyChangeToSelfAndLatestThread:transaction
changeBlock:^(TSThread *thread) {
[thread setMutedUntilDate:mutedUntilDate];

View File

@ -6,7 +6,7 @@
#import "ContactsManagerProtocol.h"
#import "ContactsUpdater.h"
#import "NotificationsProtocol.h"
#import "OWSIdentityManager.h"
#import "TSStorageManager+identityKeyStore.h"
#import "TextSecureKitEnv.h"
#import <YapDatabase/YapDatabaseConnection.h>
#import <YapDatabase/YapDatabaseTransaction.h>
@ -20,8 +20,6 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithContactId:(NSString *)contactId {
NSString *uniqueIdentifier = [[self class] threadIdFromContactId:contactId];
OWSAssert(contactId.length > 0);
self = [super initWithUniqueId:uniqueIdentifier];
return self;
@ -31,8 +29,7 @@ NS_ASSUME_NONNULL_BEGIN
transaction:(YapDatabaseReadWriteTransaction *)transaction
relay:(nullable NSString *)relay
{
OWSAssert(contactId.length > 0);
OWSAssert(contactId);
SignalRecipient *recipient =
[SignalRecipient recipientWithTextSecureIdentifier:contactId withTransaction:transaction];
@ -58,8 +55,6 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)getOrCreateThreadWithContactId:(NSString *)contactId
transaction:(YapDatabaseReadWriteTransaction *)transaction {
OWSAssert(contactId.length > 0);
TSContactThread *thread =
[self fetchObjectWithUniqueID:[self threadIdFromContactId:contactId] transaction:transaction];
@ -73,10 +68,8 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)getOrCreateThreadWithContactId:(NSString *)contactId
{
OWSAssert(contactId.length > 0);
__block TSContactThread *thread;
[[self dbReadWriteConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[[self dbConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
thread = [self getOrCreateThreadWithContactId:contactId transaction:transaction];
}];
@ -98,7 +91,7 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)hasSafetyNumbers
{
return !![[OWSIdentityManager sharedManager] identityKeyForRecipientId:self.contactIdentifier];
return !![self.storageManager identityKeyForRecipientId:self.contactIdentifier];
}
- (NSString *)name

View File

@ -18,13 +18,6 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithGroupModel:(TSGroupModel *)groupModel
{
OWSAssert(groupModel);
OWSAssert(groupModel.groupId.length > 0);
OWSAssert(groupModel.groupMemberIds.count > 0);
for (NSString *recipientId in groupModel.groupMemberIds) {
OWSAssert(recipientId.length > 0);
}
NSString *uniqueIdentifier = [[self class] threadIdFromGroupId:groupModel.groupId];
self = [super initWithUniqueId:uniqueIdentifier];
if (!self) {
@ -38,8 +31,6 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithGroupIdData:(NSData *)groupId
{
OWSAssert(groupId.length > 0);
TSGroupModel *groupModel = [[TSGroupModel alloc] initWithTitle:nil memberIds:nil image:nil groupId:groupId];
self = [self initWithGroupModel:groupModel];
@ -52,16 +43,11 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)threadWithGroupModel:(TSGroupModel *)groupModel transaction:(YapDatabaseReadTransaction *)transaction
{
OWSAssert(groupModel);
OWSAssert(groupModel.groupId.length > 0);
return [self fetchObjectWithUniqueID:[self threadIdFromGroupId:groupModel.groupId] transaction:transaction];
}
+ (instancetype)getOrCreateThreadWithGroupIdData:(NSData *)groupId
{
OWSAssert(groupId.length > 0);
TSGroupThread *thread = [self fetchObjectWithUniqueID:[self threadIdFromGroupId:groupId]];
if (!thread) {
thread = [[self alloc] initWithGroupIdData:groupId];
@ -72,9 +58,6 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)getOrCreateThreadWithGroupModel:(TSGroupModel *)groupModel
transaction:(YapDatabaseReadWriteTransaction *)transaction {
OWSAssert(groupModel);
OWSAssert(groupModel.groupId.length > 0);
TSGroupThread *thread =
[self fetchObjectWithUniqueID:[self threadIdFromGroupId:groupModel.groupId] transaction:transaction];
@ -87,11 +70,8 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)getOrCreateThreadWithGroupModel:(TSGroupModel *)groupModel
{
OWSAssert(groupModel);
OWSAssert(groupModel.groupId.length > 0);
__block TSGroupThread *thread;
[[self dbReadWriteConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[[self dbConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
thread = [self getOrCreateThreadWithGroupModel:groupModel transaction:transaction];
}];
return thread;
@ -99,15 +79,11 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSString *)threadIdFromGroupId:(NSData *)groupId
{
OWSAssert(groupId.length > 0);
return [TSGroupThreadPrefix stringByAppendingString:[groupId base64EncodedString]];
}
+ (NSData *)groupIdFromThreadId:(NSString *)threadId
{
OWSAssert(threadId.length > 0);
return [NSData dataFromBase64String:[threadId substringWithRange:NSMakeRange(1, threadId.length - 1)]];
}

View File

@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
- (OWSSignalServiceProtosSyncMessageBuilder *)syncMessageBuilder
- (OWSSignalServiceProtosSyncMessage *)buildSyncMessage
{
OWSSignalServiceProtosSyncMessageBlockedBuilder *blockedPhoneNumbersBuilder =
[OWSSignalServiceProtosSyncMessageBlockedBuilder new];
@ -35,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSSignalServiceProtosSyncMessageBuilder *syncMessageBuilder = [OWSSignalServiceProtosSyncMessageBuilder new];
[syncMessageBuilder setBlocked:[blockedPhoneNumbersBuilder build]];
return syncMessageBuilder;
return [syncMessageBuilder build];
}
@end

View File

@ -7,12 +7,10 @@
NS_ASSUME_NONNULL_BEGIN
@class SignalAccount;
@class OWSRecipientIdentity;
@interface OWSContactsOutputStream : OWSChunkedOutputStream
- (void)writeSignalAccount:(SignalAccount *)signalAccount
recipientIdentity:(nullable OWSRecipientIdentity *)recipientIdentity;
- (void)writeSignalAccount:(SignalAccount *)signalAccount;
@end

View File

@ -5,8 +5,6 @@
#import "OWSContactsOutputStream.h"
#import "Contact.h"
#import "MIMETypeUtil.h"
#import "NSData+keyVersionByte.h"
#import "OWSRecipientIdentity.h"
#import "OWSSignalServiceProtos.pb.h"
#import "SignalAccount.h"
#import <ProtocolBuffers/CodedOutputStream.h>
@ -16,23 +14,14 @@ NS_ASSUME_NONNULL_BEGIN
@implementation OWSContactsOutputStream
- (void)writeSignalAccount:(SignalAccount *)signalAccount
recipientIdentity:(nullable OWSRecipientIdentity *)recipientIdentity
{
OWSAssert(signalAccount);
OWSAssert(signalAccount.contact);
OWSSignalServiceProtosContactDetailsBuilder *contactBuilder = [OWSSignalServiceProtosContactDetailsBuilder new];
[contactBuilder setName:signalAccount.contact.fullName];
[contactBuilder setNumber:signalAccount.recipientId];
if (recipientIdentity != nil) {
OWSSignalServiceProtosVerifiedBuilder *verifiedBuilder = [OWSSignalServiceProtosVerifiedBuilder new];
verifiedBuilder.destination = recipientIdentity.recipientId;
verifiedBuilder.identityKey = [recipientIdentity.identityKey prependKeyType];
verifiedBuilder.state = OWSVerificationStateToProtoState(recipientIdentity.verificationState);
contactBuilder.verifiedBuilder = verifiedBuilder;
}
NSData *avatarPng;
if (signalAccount.contact.image) {
OWSSignalServiceProtosContactDetailsAvatarBuilder *avatarBuilder =

View File

@ -1,6 +1,4 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSReadReceipt.h"
#import <YapDatabase/YapDatabase.h>
@ -106,7 +104,7 @@ NSString *const OWSReadReceiptColumnSenderId = @"senderId";
stringWithFormat:@"WHERE %@ = ? AND %@ = ?", OWSReadReceiptColumnSenderId, OWSReadReceiptColumnTimestamp];
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:queryFormat, senderId, @(timestamp)];
[[self dbReadConnection] readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[[self dbConnection] readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[[transaction ext:OWSReadReceiptIndexOnSenderIdAndTimestamp]
enumerateKeysAndObjectsMatchingQuery:query
usingBlock:^(NSString *collection, NSString *key, id object, BOOL *stop) {

View File

@ -1,6 +1,4 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSReadReceiptsMessage.h"
#import "OWSReadReceipt.h"
@ -28,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
- (OWSSignalServiceProtosSyncMessageBuilder *)syncMessageBuilder
- (OWSSignalServiceProtosSyncMessage *)buildSyncMessage
{
OWSSignalServiceProtosSyncMessageBuilder *syncMessageBuilder = [OWSSignalServiceProtosSyncMessageBuilder new];
for (OWSReadReceipt *readReceipt in self.readReceipts) {
@ -39,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN
[syncMessageBuilder addRead:[readProtoBuilder build]];
}
return syncMessageBuilder;
return [syncMessageBuilder build];
}
@end

View File

@ -7,10 +7,7 @@
#import "OWSReadReceipt.h"
#import "OWSSignalServiceProtos.pb.h"
#import "TSContactThread.h"
#import "TSDatabaseView.h"
#import "TSIncomingMessage.h"
#import "TSStorageManager.h"
#import <YapDatabase/YapDatabaseConnection.h>
NS_ASSUME_NONNULL_BEGIN
@ -85,61 +82,14 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
TSIncomingMessage *message =
[TSIncomingMessage findMessageWithAuthorId:readReceipt.senderId timestamp:readReceipt.timestamp];
if (message) {
OWSAssert(message.thread);
// Mark all unread messages in this thread that are older than message specified in the read
// receipt.
NSMutableArray<id<OWSReadTracking>> *interactionsToMarkAsRead = [NSMutableArray new];
// Always mark the message specified by the read receipt as read.
[interactionsToMarkAsRead addObject:message];
[self.storageManager.dbReadWriteConnection readWriteWithBlock:^(
YapDatabaseReadWriteTransaction *transaction) {
[[TSDatabaseView unseenDatabaseViewExtension:transaction]
enumerateRowsInGroup:message.uniqueThreadId
usingBlock:^(NSString *collection,
NSString *key,
id object,
id metadata,
NSUInteger index,
BOOL *stop) {
TSInteraction *interaction = object;
if (interaction.timestampForSorting > message.timestampForSorting) {
*stop = YES;
return;
}
id<OWSReadTracking> possiblyRead = (id<OWSReadTracking>)object;
OWSAssert(!possiblyRead.read);
[interactionsToMarkAsRead addObject:possiblyRead];
}];
for (id<OWSReadTracking> interaction in interactionsToMarkAsRead) {
// * Don't send a read receipt in response to a read receipt.
// * Don't update expiration; we'll do that in the next statement.
[interaction markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:NO];
if ([interaction isKindOfClass:[TSMessage class]]) {
TSMessage *otherMessage = (TSMessage *)interaction;
// Update expiration using the timestamp from the readReceipt.
[OWSDisappearingMessagesJob setExpirationForMessage:otherMessage
expirationStartedAt:readReceipt.timestamp];
// Fire event that will cancel any pending notifications for this message.
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]
postNotificationName:OWSReadReceiptsProcessorMarkedMessageAsReadNotification
object:otherMessage];
});
}
}
}];
[message markAsReadFromReadReceipt];
[OWSDisappearingMessagesJob setExpirationForMessage:message expirationStartedAt:readReceipt.timestamp];
// If it was previously saved, no need to keep it around any longer.
[readReceipt remove];
[[NSNotificationCenter defaultCenter]
postNotificationName:OWSReadReceiptsProcessorMarkedMessageAsReadNotification
object:message];
} else {
DDLogDebug(@"%@ Received read receipt for an unknown message. Saving it for later.", self.tag);
[readReceipt save];

View File

@ -1,24 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSOutgoingSyncMessage.h"
#import "OWSRecipientIdentity.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSVerificationStateSyncMessage : OWSOutgoingSyncMessage
- (instancetype)initWithVerificationState:(OWSVerificationState)verificationState
identityKey:(NSData *)identityKey
verificationForRecipientId:(NSString *)recipientId;
// This is a clunky name, but we want to differentiate it from `recipientIdentifier` inherited from `TSOutgoingMessage`
@property (nonatomic, readonly) NSString *verificationForRecipientId;
@property (nonatomic, readonly) size_t paddingBytesLength;
@property (nonatomic, readonly) size_t unpaddedVerifiedLength;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,101 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSVerificationStateSyncMessage.h"
#import "Cryptography.h"
#import "OWSIdentityManager.h"
#import "OWSSignalServiceProtos.pb.h"
NS_ASSUME_NONNULL_BEGIN
#pragma mark -
@interface OWSVerificationStateSyncMessage ()
@property (nonatomic, readonly) OWSVerificationState verificationState;
@property (nonatomic, readonly) NSData *identityKey;
@end
#pragma mark -
@implementation OWSVerificationStateSyncMessage
- (instancetype)initWithVerificationState:(OWSVerificationState)verificationState
identityKey:(NSData *)identityKey
verificationForRecipientId:(NSString *)verificationForRecipientId
{
OWSAssert(identityKey.length == kIdentityKeyLength);
OWSAssert(verificationForRecipientId.length > 0);
// we only sync user's marking as un/verified. Never sync the conflicted state, the sibling device
// will figure that out on it's own.
OWSAssert(verificationState != OWSVerificationStateNoLongerVerified);
self = [super init];
if (!self) {
return self;
}
_verificationState = verificationState;
_identityKey = identityKey;
_verificationForRecipientId = verificationForRecipientId;
// This sync message should be 1-512 bytes longer than the corresponding NullMessage
// we store this values so the corresponding NullMessage can subtract it from the total length.
_paddingBytesLength = arc4random_uniform(512) + 1;
return self;
}
- (OWSSignalServiceProtosSyncMessageBuilder *)syncMessageBuilder
{
OWSAssert(self.identityKey.length == kIdentityKeyLength);
OWSAssert(self.verificationForRecipientId.length > 0);
// we only sync user's marking as un/verified. Never sync the conflicted state, the sibling device
// will figure that out on it's own.
OWSAssert(self.verificationState != OWSVerificationStateNoLongerVerified);
OWSSignalServiceProtosSyncMessageBuilder *syncMessageBuilder = [OWSSignalServiceProtosSyncMessageBuilder new];
OWSSignalServiceProtosVerifiedBuilder *verifiedBuilder = [OWSSignalServiceProtosVerifiedBuilder new];
verifiedBuilder.destination = self.verificationForRecipientId;
verifiedBuilder.identityKey = self.identityKey;
verifiedBuilder.state = OWSVerificationStateToProtoState(self.verificationState);
OWSAssert(self.paddingBytesLength != 0);
// We add the same amount of padding in the VerificationStateSync message and it's coresponding NullMessage so that
// the sync message is indistinguishable from an outgoing Sent transcript corresponding to the NullMessage. We pad
// the NullMessage so as to obscure it's content. The sync message (like all sync messages) will be *additionally*
// padded by the superclass while being sent. The end result is we send a NullMessage of a non-distinct size, and a
// verification sync which is ~1-512 bytes larger then that.
verifiedBuilder.nullMessage = [Cryptography generateRandomBytes:self.paddingBytesLength];
syncMessageBuilder.verifiedBuilder = verifiedBuilder;
return syncMessageBuilder;
}
- (size_t)unpaddedVerifiedLength
{
OWSAssert(self.identityKey.length == kIdentityKeyLength);
OWSAssert(self.verificationForRecipientId.length > 0);
// we only sync user's marking as un/verified. Never sync the conflicted state, the sibling device
// will figure that out on it's own.
OWSAssert(self.verificationState != OWSVerificationStateNoLongerVerified);
OWSSignalServiceProtosVerifiedBuilder *verifiedBuilder = [OWSSignalServiceProtosVerifiedBuilder new];
verifiedBuilder.destination = self.verificationForRecipientId;
verifiedBuilder.identityKey = self.identityKey;
verifiedBuilder.state = OWSVerificationStateToProtoState(self.verificationState);
return [verifiedBuilder build].data.length;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -27,8 +27,6 @@ NS_ASSUME_NONNULL_BEGIN
// This only applies for attachments being uploaded.
@property (atomic) BOOL isUploaded;
@property (nonatomic, readonly) NSDate *creationTimestamp;
#if TARGET_OS_IPHONE
- (nullable UIImage *)image;
#endif
@ -46,6 +44,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)deleteAttachments;
+ (NSString *)attachmentsFolder;
+ (NSUInteger)numberOfItemsInAttachmentsFolder;
- (CGSize)imageSizeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
- (CGSize)imageSizeWithoutTransaction;

View File

@ -41,9 +41,9 @@ NS_ASSUME_NONNULL_BEGIN
// state, but this constructor is used only for new outgoing
// attachments which haven't been uploaded yet.
_isUploaded = NO;
_creationTimestamp = [NSDate new];
[self ensureFilePath];
// This instance hasn't been persisted yet.
[self ensureFilePathAndPersist:NO];
return self;
}
@ -63,9 +63,9 @@ NS_ASSUME_NONNULL_BEGIN
// attachments which don't need to be uploaded.
_isUploaded = YES;
self.attachmentType = pointer.attachmentType;
_creationTimestamp = [NSDate new];
[self ensureFilePath];
// This instance hasn't been persisted yet.
[self ensureFilePathAndPersist:NO];
return self;
}
@ -77,13 +77,9 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
// OWS105AttachmentFilePaths will ensure the file path is saved if necessary.
[self ensureFilePath];
// OWS105AttachmentFilePaths will ensure the creation timestamp is saved if necessary.
if (!_creationTimestamp) {
_creationTimestamp = [NSDate new];
}
// This instance has been persisted, we need to
// update it in the database.
[self ensureFilePathAndPersist:YES];
return self;
}
@ -101,7 +97,7 @@ NS_ASSUME_NONNULL_BEGIN
}
}
- (void)ensureFilePath
- (void)ensureFilePathAndPersist:(BOOL)shouldPersist
{
if (self.localRelativeFilePath) {
return;
@ -131,6 +127,18 @@ NS_ASSUME_NONNULL_BEGIN
self.localRelativeFilePath = localRelativeFilePath;
OWSAssert(self.filePath);
if (shouldPersist) {
// It's not ideal to do this asynchronously, but we can create a new transaction
// within initWithCoder: which will be called from within a transaction.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
OWSAssert(transaction);
[self saveWithTransaction:transaction];
}];
});
}
}
#pragma mark - File Management
@ -162,31 +170,33 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSString *)attachmentsFolder
{
static NSString *attachmentsFolder = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *documentsPath =
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
attachmentsFolder = [documentsPath stringByAppendingFormat:@"/Attachments"];
NSString *documentsPath =
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *attachmentFolder = [documentsPath stringByAppendingFormat:@"/Attachments"];
BOOL isDirectory;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:attachmentsFolder isDirectory:&isDirectory];
if (exists) {
OWSAssert(isDirectory);
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:attachmentFolder
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (error) {
DDLogError(@"Failed to create attachments directory: %@", error);
}
DDLogInfo(@"Attachments directory already exists");
} else {
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:attachmentsFolder
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (error) {
DDLogError(@"Failed to create attachments directory: %@", error);
}
}
});
return attachmentsFolder;
return attachmentFolder;
}
+ (NSUInteger)numberOfItemsInAttachmentsFolder
{
NSError *error;
NSUInteger count =
[[[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self attachmentsFolder] error:&error] count];
if (error) {
DDLogError(@"Unable to count attachments in attachments folder. Error: %@", error);
}
return count;
}
- (nullable NSString *)filePath
@ -281,28 +291,10 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)deleteAttachments
{
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *fileURL = [NSURL fileURLWithPath:self.attachmentsFolder];
NSArray<NSURL *> *contents =
[fileManager contentsOfDirectoryAtURL:fileURL includingPropertiesForKeys:nil options:0 error:&error];
[[NSFileManager defaultManager] removeItemAtPath:[self attachmentsFolder] error:&error];
if (error) {
OWSFail(@"failed to get contents of attachments folder: %@ with error: %@", self.attachmentsFolder, error);
return;
DDLogError(@"Failed to delete attachment folder with error: %@", error.debugDescription);
}
for (NSURL *url in contents) {
NSError *deletionError;
[fileManager removeItemAtURL:url error:&deletionError];
if (deletionError) {
OWSFail(@"failed to remove item at path: %@ with error: %@", url, deletionError);
// continue to try to delete remaining items.
}
}
return;
}
- (CGSize)calculateImageSize
@ -374,7 +366,7 @@ NS_ASSUME_NONNULL_BEGIN
if (transaction) {
updateDataStore(transaction);
} else {
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
updateDataStore(transaction);
}];
}
@ -445,7 +437,7 @@ NS_ASSUME_NONNULL_BEGIN
if (transaction) {
updateDataStore(transaction);
} else {
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
updateDataStore(transaction);
}];
}

View File

@ -1,6 +1,4 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSOutgoingSentMessageTranscript.h"
#import "OWSSignalServiceProtos.pb.h"
@ -39,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
- (OWSSignalServiceProtosSyncMessageBuilder *)syncMessageBuilder
- (OWSSignalServiceProtosSyncMessage *)buildSyncMessage
{
OWSSignalServiceProtosSyncMessageBuilder *syncMessageBuilder = [OWSSignalServiceProtosSyncMessageBuilder new];
@ -51,7 +49,7 @@ NS_ASSUME_NONNULL_BEGIN
[syncMessageBuilder setSentBuilder:sentBuilder];
return syncMessageBuilder;
return [syncMessageBuilder build];
}
@end

View File

@ -1,10 +1,7 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSOutgoingSyncMessage.h"
#import "OWSSignalServiceProtos.pb.h"
#import "Cryptography.h"
NS_ASSUME_NONNULL_BEGIN
@ -22,22 +19,18 @@ NS_ASSUME_NONNULL_BEGIN
return NO;
}
// This method should not be overridden, since we want to add random padding to *every* sync message
- (OWSSignalServiceProtosSyncMessage *)buildSyncMessage
- (BOOL)isLegacyMessage
{
OWSSignalServiceProtosSyncMessageBuilder *builder = [self syncMessageBuilder];
// Add a random 1-512 bytes to obscure sync message type
size_t paddingBytesLength = arc4random_uniform(512) + 1;
builder.padding = [Cryptography generateRandomBytes:paddingBytesLength];
return [builder build];
return NO;
}
- (OWSSignalServiceProtosSyncMessageBuilder *)syncMessageBuilder
- (OWSSignalServiceProtosSyncMessage *)buildSyncMessage
{
OWSFail(@"Abstract method should be overridden in subclass.");
return [OWSSignalServiceProtosSyncMessageBuilder new];
NSAssert(NO, @"buildSyncMessage must be overridden in subclass");
// e.g.
OWSSignalServiceProtosSyncMessageBuilder *syncMessageBuilder = [OWSSignalServiceProtosSyncMessageBuilder new];
return [syncMessageBuilder build];
}
- (NSData *)buildPlainTextData

View File

@ -1,6 +1,4 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSOutgoingSyncMessage.h"
@ -8,13 +6,10 @@ NS_ASSUME_NONNULL_BEGIN
@class YapDatabaseReadWriteTransaction;
@protocol ContactsManagerProtocol;
@class OWSIdentityManager;
@interface OWSSyncContactsMessage : OWSOutgoingSyncMessage
- (instancetype)initWithContactsManager:(id<ContactsManagerProtocol>)contactsManager
identityManager:(OWSIdentityManager *)identityManager;
- (instancetype)initWithContactsManager:(id<ContactsManagerProtocol>)contactsManager;
- (NSData *)buildPlainTextAttachmentData;
@end

View File

@ -8,7 +8,6 @@
#import "NSDate+millisecondTimeStamp.h"
#import "OWSContactsOutputStream.h"
#import "OWSSignalServiceProtos.pb.h"
#import "OWSIdentityManager.h"
#import "SignalAccount.h"
#import "TSAttachment.h"
#import "TSAttachmentStream.h"
@ -18,14 +17,12 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSSyncContactsMessage ()
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
@property (nonatomic, readonly) OWSIdentityManager *identityManager;
@end
@implementation OWSSyncContactsMessage
- (instancetype)initWithContactsManager:(id<ContactsManagerProtocol>)contactsManager
identityManager:(OWSIdentityManager *)identityManager
{
self = [super initWithTimestamp:[NSDate ows_millisecondTimeStamp]];
if (!self) {
@ -33,12 +30,11 @@ NS_ASSUME_NONNULL_BEGIN
}
_contactsManager = contactsManager;
_identityManager = identityManager;
return self;
}
- (OWSSignalServiceProtosSyncMessageBuilder *)syncMessageBuilder
- (OWSSignalServiceProtosSyncMessage *)buildSyncMessage
{
if (self.attachmentIds.count != 1) {
DDLogError(@"expected sync contact message to have exactly one attachment, but found %lu",
@ -57,7 +53,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSSignalServiceProtosSyncMessageBuilder *syncMessageBuilder = [OWSSignalServiceProtosSyncMessageBuilder new];
[syncMessageBuilder setContactsBuilder:contactsBuilder];
return syncMessageBuilder;
return [syncMessageBuilder build];
}
- (NSData *)buildPlainTextAttachmentData
@ -70,9 +66,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSContactsOutputStream *contactsOutputStream = [OWSContactsOutputStream streamWithOutputStream:dataOutputStream];
for (SignalAccount *signalAccount in self.contactsManager.signalAccounts) {
OWSRecipientIdentity *recipientIdentity = [self.identityManager recipientIdentityForRecipientId:signalAccount.recipientId];
[contactsOutputStream writeSignalAccount:signalAccount recipientIdentity:recipientIdentity];
[contactsOutputStream writeSignalAccount:signalAccount];
}
[contactsOutputStream flush];

View File

@ -19,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN
return [super initWithTimestamp:[NSDate ows_millisecondTimeStamp]];
}
- (OWSSignalServiceProtosSyncMessageBuilder *)syncMessageBuilder
- (OWSSignalServiceProtosSyncMessage *)buildSyncMessage
{
if (self.attachmentIds.count != 1) {
@ -37,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSSignalServiceProtosSyncMessageBuilder *syncMessageBuilder = [OWSSignalServiceProtosSyncMessageBuilder new];
[syncMessageBuilder setGroupsBuilder:groupsBuilder];
return syncMessageBuilder;
return [syncMessageBuilder build];
}
- (NSData *)buildPlainTextAttachmentData

View File

@ -1,26 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSRecipientIdentity.h"
#import "TSInfoMessage.h"
NS_ASSUME_NONNULL_BEGIN
@class TSThread;
@interface OWSVerificationStateChangeMessage : TSInfoMessage
@property (nonatomic, readonly) NSString *recipientId;
@property (nonatomic, readonly) OWSVerificationState verificationState;
@property (nonatomic, readonly) BOOL isLocalChange;
- (instancetype)initWithTimestamp:(uint64_t)timestamp
thread:(TSThread *)thread
recipientId:(NSString *)recipientId
verificationState:(OWSVerificationState)verificationState
isLocalChange:(BOOL)isLocalChange;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,34 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSVerificationStateChangeMessage.h"
#import "OWSDisappearingMessagesConfiguration.h"
NS_ASSUME_NONNULL_BEGIN
@implementation OWSVerificationStateChangeMessage
- (instancetype)initWithTimestamp:(uint64_t)timestamp
thread:(TSThread *)thread
recipientId:(NSString *)recipientId
verificationState:(OWSVerificationState)verificationState
isLocalChange:(BOOL)isLocalChange
{
OWSAssert(recipientId.length > 0);
self = [super initWithTimestamp:timestamp inThread:thread messageType:TSInfoMessageVerificationStateChange];
if (!self) {
return self;
}
_recipientId = recipientId;
_verificationState = verificationState;
_isLocalChange = isLocalChange;
return self;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -12,16 +12,14 @@ NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(int32_t, TSErrorMessageType) {
TSErrorMessageNoSession,
TSErrorMessageWrongTrustedIdentityKey, // DEPRECATED: We no longer create TSErrorMessageWrongTrustedIdentityKey, but
// persisted legacy messages could exist indefinitly.
TSErrorMessageWrongTrustedIdentityKey,
TSErrorMessageInvalidKeyException,
TSErrorMessageMissingKeyId, // unused
TSErrorMessageInvalidMessage,
TSErrorMessageDuplicateMessage, // unused
TSErrorMessageDuplicateMessage,
TSErrorMessageInvalidVersion,
TSErrorMessageNonBlockingIdentityChange,
TSErrorMessageUnknownContactBlockOffer,
TSErrorMessageGroupCreationFailed,
};
- (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
@ -59,6 +57,8 @@ typedef NS_ENUM(int32_t, TSErrorMessageType) {
@property (nonatomic, readonly) TSErrorMessageType errorType;
@property (nullable, nonatomic, readonly) NSString *recipientId;
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
@end
NS_ASSUME_NONNULL_END

View File

@ -115,9 +115,6 @@ NSUInteger TSErrorMessageSchemaVersion = 1;
case TSErrorMessageUnknownContactBlockOffer:
return NSLocalizedString(@"UNKNOWN_CONTACT_BLOCK_OFFER",
@"Message shown in conversation view that offers to block an unknown user.");
case TSErrorMessageGroupCreationFailed:
return NSLocalizedString(@"GROUP_CREATION_FAILED",
@"Message shown in conversation view that indicates there were issues with group creation.");
default:
return NSLocalizedString(@"ERROR_MESSAGE_UNKNOWN_ERROR", @"");
break;
@ -170,22 +167,19 @@ NSUInteger TSErrorMessageSchemaVersion = 1;
return NO;
}
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration
- (void)markAsReadLocally
{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self markAsReadLocallyWithTransaction:transaction];
}];
}
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
if (_read) {
return;
}
DDLogDebug(@"%@ marking as read uniqueId: %@ which has timestamp: %llu", self.tag, self.uniqueId, self.timestamp);
DDLogInfo(@"%@ marking as read uniqueId: %@ which has timestamp: %llu", self.tag, self.uniqueId, self.timestamp);
_read = YES;
[self saveWithTransaction:transaction];
[self touchThreadWithTransaction:transaction];
// Ignore sendReadReceipt and updateExpiration; they don't apply to error messages.
}
#pragma mark - Logging

View File

@ -108,6 +108,20 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification;
// This will be 0 for messages created before we were tracking sourceDeviceId
@property (nonatomic, readonly) UInt32 sourceDeviceId;
/*
* Marks a message as having been read on this device (as opposed to responding to a remote read receipt).
*
*/
- (void)markAsReadLocally;
// TODO possible to remove?
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
/**
* Similar to markAsReadWithTransaction, but doesn't send out read receipts.
* Used for *responding* to a remote read receipt.
*/
- (void)markAsReadFromReadReceipt;
@end
NS_ASSUME_NONNULL_END

View File

@ -3,8 +3,6 @@
//
#import "TSIncomingMessage.h"
#import "OWSDisappearingMessagesConfiguration.h"
#import "OWSDisappearingMessagesJob.h"
#import "TSContactThread.h"
#import "TSDatabaseSecondaryIndexes.h"
#import "TSGroupThread.h"
@ -78,7 +76,7 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM
+ (nullable instancetype)findMessageWithAuthorId:(NSString *)authorId timestamp:(uint64_t)timestamp
{
__block TSIncomingMessage *foundMessage;
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
// In theory we could build a new secondaryIndex for (authorId,timestamp), but in practice there should
// be *very* few (millisecond) timestamps with multiple authors.
[TSDatabaseSecondaryIndexes
@ -117,33 +115,37 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM
return YES;
}
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration
- (void)markAsReadFromReadReceipt
{
OWSAssert(transaction);
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self markAsReadWithoutNotificationWithTransaction:transaction];
}];
}
if (_read) {
return;
}
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
[self markAsReadWithoutNotificationWithTransaction:transaction];
[[NSNotificationCenter defaultCenter] postNotificationName:TSIncomingMessageWasReadOnThisDeviceNotification
object:self];
}
DDLogDebug(@"%@ marking as read uniqueId: %@ which has timestamp: %llu", self.tag, self.uniqueId, self.timestamp);
- (void)markAsReadLocally
{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self markAsReadWithoutNotificationWithTransaction:transaction];
}];
// Notification must happen outside of the transaction, else we'll likely crash when the notification receiver
// tries to do anything with the DB.
[[NSNotificationCenter defaultCenter] postNotificationName:TSIncomingMessageWasReadOnThisDeviceNotification
object:self];
}
- (void)markAsReadWithoutNotificationWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
DDLogInfo(@"%@ marking as read uniqueId: %@ which has timestamp: %llu", self.tag, self.uniqueId, self.timestamp);
_read = YES;
[self saveWithTransaction:transaction];
[self touchThreadWithTransaction:transaction];
if (updateExpiration) {
[OWSDisappearingMessagesJob setExpirationForMessage:self];
}
if (sendReadReceipt) {
// Notification must happen outside of the transaction, else we'll likely crash when the notification receiver
// tries to do anything with the DB.
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:TSIncomingMessageWasReadOnThisDeviceNotification
object:self];
});
}
}
#pragma mark - Logging

View File

@ -12,13 +12,11 @@ NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, TSInfoMessageType) {
TSInfoMessageTypeSessionDidEnd,
TSInfoMessageUserNotRegistered,
// TSInfoMessageTypeUnsupportedMessage appears to be obsolete.
TSInfoMessageTypeUnsupportedMessage,
TSInfoMessageTypeGroupUpdate,
TSInfoMessageTypeGroupQuit,
TSInfoMessageTypeDisappearingMessagesUpdate,
TSInfoMessageAddToContactsOffer,
TSInfoMessageVerificationStateChange,
};
+ (instancetype)userNotRegisteredMessageInThread:(TSThread *)thread;
@ -44,6 +42,8 @@ typedef NS_ENUM(NSInteger, TSInfoMessageType) {
expiresInSeconds:(uint32_t)expiresInSeconds
expireStartedAt:(uint64_t)expireStartedAt NS_UNAVAILABLE;
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
@end
NS_ASSUME_NONNULL_END

View File

@ -92,9 +92,6 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
case TSInfoMessageAddToContactsOffer:
return NSLocalizedString(@"ADD_TO_CONTACTS_OFFER",
@"Message shown in conversation view that offers to add an unknown user to your phone's contacts.");
case TSInfoMessageVerificationStateChange:
return NSLocalizedString(@"VERIFICATION_STATE_CHANGE_GENERIC",
@"Generic message indicating that verification state changed for a given user.");
default:
break;
}
@ -109,22 +106,19 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
return NO;
}
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration
- (void)markAsReadLocally
{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self markAsReadLocallyWithTransaction:transaction];
}];
}
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
if (_read) {
return;
}
DDLogDebug(@"%@ marking as read uniqueId: %@ which has timestamp: %llu", self.tag, self.uniqueId, self.timestamp);
DDLogInfo(@"%@ marking as read uniqueId: %@ which has timestamp: %llu", self.tag, self.uniqueId, self.timestamp);
_read = YES;
[self saveWithTransaction:transaction];
[self touchThreadWithTransaction:transaction];
// Ignore sendReadReceipt and updateExpiration; they don't apply to info messages.
}
#pragma mark - Logging

View File

@ -97,6 +97,13 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
// If set, this group message should only be sent to a single recipient.
@property (atomic, readonly) NSString *singleGroupRecipient;
/**
* Whether the message should be serialized as a modern aka Content, or the old style legacy message.
* Sync and Call messsages must be sent as Content, but other old style DataMessage payloads should be
* sent as legacy message until we're confident no significant number of legacy clients exist in the wild.
*/
@property (nonatomic, readonly) BOOL isLegacyMessage;
@property (nonatomic, readonly) BOOL isVoiceMessage;
/**
@ -157,8 +164,6 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
// This isn't a perfect arrangement, but in practice this will prevent
// data loss and will resolve all known issues.
- (void)updateWithMessageState:(TSOutgoingMessageState)messageState;
- (void)updateWithMessageState:(TSOutgoingMessageState)messageState
transaction:(YapDatabaseReadWriteTransaction *)transaction;
- (void)updateWithSendingError:(NSError *)error;
- (void)updateWithHasSyncedTranscript:(BOOL)hasSyncedTranscript;
- (void)updateWithCustomMessage:(NSString *)customMessage transaction:(YapDatabaseReadWriteTransaction *)transaction;

View File

@ -245,7 +245,7 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
{
OWSAssert(error);
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setMessageState:TSOutgoingMessageStateUnsent];
@ -256,25 +256,17 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
- (void)updateWithMessageState:(TSOutgoingMessageState)messageState
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self updateWithMessageState:messageState transaction:transaction];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setMessageState:messageState];
}];
}];
}
- (void)updateWithMessageState:(TSOutgoingMessageState)messageState
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setMessageState:messageState];
}];
}
- (void)updateWithHasSyncedTranscript:(BOOL)hasSyncedTranscript
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setHasSyncedTranscript:hasSyncedTranscript];
@ -295,7 +287,7 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
- (void)updateWithCustomMessage:(NSString *)customMessage
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self updateWithCustomMessage:customMessage transaction:transaction];
}];
}
@ -312,14 +304,14 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
- (void)updateWithWasDelivered
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self updateWithWasDeliveredWithTransaction:transaction];
}];
}
- (void)updateWithWasSentAndDelivered
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setMessageState:TSOutgoingMessageStateSentToService];
@ -397,7 +389,7 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
- (void)updateWithSentRecipient:(NSString *)contactId
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self updateWithSentRecipient:contactId transaction:transaction];
}];
}
@ -455,12 +447,16 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
return [[self dataMessageBuilder] build];
}
// For legacy message this is a serialized DataMessage.
// For modern messages, e.g. Sync and Call messages, this is a serialized Contact
- (NSData *)buildPlainTextData
{
OWSSignalServiceProtosContentBuilder *contentBuilder = [OWSSignalServiceProtosContentBuilder new];
contentBuilder.dataMessage = [self buildDataMessage];
return [[self buildDataMessage] data];
}
return [[contentBuilder build] data];
- (BOOL)isLegacyMessage
{
return YES;
}
- (BOOL)shouldSyncTranscript

View File

@ -4,11 +4,11 @@
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
#import "OWSFingerprint.h"
#import "OWSIdentityManager.h"
#import "TSContactThread.h"
#import "TSDatabaseView.h"
#import "TSErrorMessage_privateConstructor.h"
#import "TSMessagesManager.h"
#import "TSStorageManager+IdentityKeyStore.h"
#import "TSStorageManager.h"
#import <AxolotlKit/NSData+keyVersionByte.h>
#import <AxolotlKit/PreKeyWhisperMessage.h>
@ -81,7 +81,10 @@ NS_ASSUME_NONNULL_BEGIN
// Saving a new identity mutates the session store so it must happen on the sessionStoreQueue
dispatch_async([OWSDispatch sessionStoreQueue], ^{
[[OWSIdentityManager sharedManager] saveRemoteIdentity:newKey recipientId:self.envelope.source];
[[TSStorageManager sharedManager] saveRemoteIdentity:newKey
recipientId:self.envelope.source
approvedForBlockingUse:YES
approvedForNonBlockingUse:YES];
dispatch_async(dispatch_get_main_queue(), ^{
// Decrypt this and any old messages for the newly accepted key

View File

@ -15,6 +15,11 @@ extern NSString *TSInvalidRecipientKey;
@interface TSInvalidIdentityKeySendingErrorMessage : TSInvalidIdentityKeyErrorMessage
+ (instancetype)untrustedKeyWithOutgoingMessage:(TSOutgoingMessage *)outgoingMessage
inThread:(TSThread *)thread
forRecipient:(NSString *)recipientId
preKeyBundle:(PreKeyBundle *)preKeyBundle;
@property (nonatomic, readonly) NSString *messageId;
@end

View File

@ -4,12 +4,12 @@
#import "TSInvalidIdentityKeySendingErrorMessage.h"
#import "OWSFingerprint.h"
#import "OWSIdentityManager.h"
#import "PreKeyBundle+jsonDict.h"
#import "SignalRecipient.h"
#import "TSContactThread.h"
#import "TSErrorMessage_privateConstructor.h"
#import "TSOutgoingMessage.h"
#import "TSStorageManager+IdentityKeyStore.h"
#import <AxolotlKit/NSData+keyVersionByte.h>
NS_ASSUME_NONNULL_BEGIN
@ -43,15 +43,26 @@ NSString *TSInvalidRecipientKey = @"TSInvalidRecipientKey";
return self;
}
+ (instancetype)untrustedKeyWithOutgoingMessage:(TSOutgoingMessage *)outgoingMessage
inThread:(TSThread *)thread
forRecipient:(NSString *)recipientId
preKeyBundle:(PreKeyBundle *)preKeyBundle
{
TSInvalidIdentityKeySendingErrorMessage *message = [[self alloc] initWithOutgoingMessage:outgoingMessage
inThread:thread
forRecipient:recipientId
preKeyBundle:preKeyBundle];
return message;
}
- (void)acceptNewIdentityKey
{
// Shouldn't really get here, since we're no longer creating blocking SN changes.
// But there may still be some old unaccepted SN errors in the wild that need to be accepted.
OWSFail(@"accepting new identity key is deprecated.");
// Saving a new identity mutates the session store so it must happen on the sessionStoreQueue
dispatch_async([OWSDispatch sessionStoreQueue], ^{
[[OWSIdentityManager sharedManager] saveRemoteIdentity:self.newIdentityKey recipientId:self.recipientId];
[[TSStorageManager sharedManager] saveRemoteIdentity:self.newIdentityKey
recipientId:self.recipientId
approvedForBlockingUse:YES
approvedForNonBlockingUse:YES];
});
}

View File

@ -11,7 +11,6 @@
NS_ASSUME_NONNULL_BEGIN
NSString *const kNSNotificationName_BlockedPhoneNumbersDidChange = @"kNSNotificationName_BlockedPhoneNumbersDidChange";
NSString *const kOWSBlockingManager_BlockedPhoneNumbersCollection = @"kOWSBlockingManager_BlockedPhoneNumbersCollection";
// This key is used to persist the current "blocked phone numbers" state.
NSString *const kOWSBlockingManager_BlockedPhoneNumbersKey = @"kOWSBlockingManager_BlockedPhoneNumbersKey";
@ -26,7 +25,7 @@ NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockin
// We don't store the phone numbers as instances of PhoneNumber to avoid
// consistency issues between clients, but these should all be valid e164
// phone numbers.
@property (atomic, readonly) NSMutableSet<NSString *> *blockedPhoneNumberSet;
@property (nonatomic, readonly) NSMutableSet<NSString *> *blockedPhoneNumberSet;
@end
@ -75,19 +74,6 @@ NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockin
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)observeNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
- (void)addBlockedPhoneNumber:(NSString *)phoneNumber
{
OWSAssert(phoneNumber.length > 0);
@ -96,7 +82,7 @@ NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockin
@synchronized(self)
{
[self ensureLazyInitialization];
[self lazyLoadBlockedPhoneNumbersIfNecessary];
if ([_blockedPhoneNumberSet containsObject:phoneNumber]) {
// Ignore redundant changes.
@ -117,7 +103,7 @@ NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockin
@synchronized(self)
{
[self ensureLazyInitialization];
[self lazyLoadBlockedPhoneNumbersIfNecessary];
if (![_blockedPhoneNumberSet containsObject:phoneNumber]) {
// Ignore redundant changes.
@ -138,7 +124,7 @@ NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockin
@synchronized(self)
{
[self ensureLazyInitialization];
[self lazyLoadBlockedPhoneNumbersIfNecessary];
NSSet *newSet = [NSSet setWithArray:blockedPhoneNumbers];
if ([_blockedPhoneNumberSet isEqualToSet:newSet]) {
@ -155,7 +141,7 @@ NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockin
{
@synchronized(self)
{
[self ensureLazyInitialization];
[self lazyLoadBlockedPhoneNumbersIfNecessary];
return [_blockedPhoneNumberSet.allObjects sortedArrayUsingSelector:@selector(compare:)];
}
@ -203,7 +189,7 @@ NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockin
}
// This method should only be called from within a synchronized block.
- (void)ensureLazyInitialization
- (void)lazyLoadBlockedPhoneNumbersIfNecessary
{
if (_blockedPhoneNumberSet) {
// _blockedPhoneNumberSet has already been loaded, abort.
@ -215,15 +201,6 @@ NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockin
inCollection:kOWSBlockingManager_BlockedPhoneNumbersCollection];
_blockedPhoneNumberSet = [[NSMutableSet alloc] initWithArray:(blockedPhoneNumbers ?: [NSArray new])];
[self syncBlockedPhoneNumbersIfNecessary];
[self observeNotifications];
}
// This method should only be called from within a synchronized block.
- (void)syncBlockedPhoneNumbersIfNecessary
{
OWSAssert(_blockedPhoneNumberSet);
// If we haven't yet successfully synced the current "blocked phone numbers" changes,
// try again to sync now.
NSArray<NSString *> *syncedBlockedPhoneNumbers =
@ -233,7 +210,7 @@ NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockin
if (![_blockedPhoneNumberSet isEqualToSet:syncedBlockedPhoneNumberSet]) {
DDLogInfo(@"%@ retrying sync of blocked phone numbers", self.tag);
dispatch_async(dispatch_get_main_queue(), ^{
[self sendBlockedPhoneNumbersMessage:self.blockedPhoneNumbers];
[self sendBlockedPhoneNumbersMessage:blockedPhoneNumbers];
});
}
}
@ -254,6 +231,8 @@ NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockin
}
failure:^(NSError *error) {
DDLogError(@"%@ Failed to send blocked phone numbers sync message with error: %@", self.tag, error);
// TODO: We might want to retry more often than just app launch.
}];
}
@ -267,18 +246,6 @@ NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockin
inCollection:kOWSBlockingManager_BlockedPhoneNumbersCollection];
}
#pragma mark - Notifications
- (void)applicationDidBecomeActive:(NSNotification *)notification
{
OWSAssert([NSThread isMainThread]);
@synchronized(self)
{
[self syncBlockedPhoneNumbersIfNecessary];
}
}
#pragma mark - Logging
+ (NSString *)tag

View File

@ -1,38 +1,38 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Created by Michael Kirk on 9/23/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@class TSStorageManager;
@class TSMessage;
@class TSThread;
@class YapDatabaseReadTransaction;
@interface OWSDisappearingMessagesFinder : NSObject
- (void)enumerateExpiredMessagesWithBlock:(void (^_Nonnull)(TSMessage *message))block
transaction:(YapDatabaseReadTransaction *)transaction;
- (void)enumerateUnstartedExpiringMessagesInThread:(TSThread *)thread
block:(void (^_Nonnull)(TSMessage *message))block
transaction:(YapDatabaseReadTransaction *)transaction;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager NS_DESIGNATED_INITIALIZER;
+ (instancetype)defaultInstance;
- (void)enumerateExpiredMessagesWithBlock:(void (^_Nonnull)(TSMessage *message))block;
- (void)enumerateUnstartedExpiringMessagesInThread:(TSThread *)thread block:(void (^_Nonnull)(TSMessage *message))block;
/**
* @return
* uint64_t millisecond timestamp wrapped in a number. Retrieve with `unsignedLongLongvalue`.
* or nil if there are no upcoming expired messages
*/
- (nullable NSNumber *)nextExpirationTimestampWithTransaction:(YapDatabaseReadTransaction *_Nonnull)transaction;
- (nullable NSNumber *)nextExpirationTimestamp;
/**
* Database extensions required for class to work.
*/
+ (void)asyncRegisterDatabaseExtensions:(TSStorageManager *)storageManager;
- (void)asyncRegisterDatabaseExtensions;
/**
* Only use the sync version for testing, generally we'll want to register extensions async
*/
+ (void)blockingRegisterDatabaseExtensions:(TSStorageManager *)storageManager;
- (void)blockingRegisterDatabaseExtensions;
@end

View File

@ -1,6 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Created by Michael Kirk on 9/23/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSDisappearingMessagesFinder.h"
#import "NSDate+millisecondTimeStamp.h"
@ -18,13 +17,40 @@ static NSString *const OWSDisappearingMessageFinderThreadIdColumn = @"thread_id"
static NSString *const OWSDisappearingMessageFinderExpiresAtColumn = @"expires_at";
static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_messages_on_expires_at_and_thread_id_v2";
@interface OWSDisappearingMessagesFinder ()
@property (nonatomic, readonly) TSStorageManager *storageManager;
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@end
@implementation OWSDisappearingMessagesFinder
- (NSArray<NSString *> *)fetchUnstartedExpiringMessageIdsInThread:(TSThread *)thread
transaction:(YapDatabaseReadTransaction *_Nonnull)transaction
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
{
OWSAssert(transaction);
self = [super init];
if (!self) {
return self;
}
_storageManager = storageManager;
_dbConnection = [storageManager newDatabaseConnection];
return self;
}
+ (instancetype)defaultInstance
{
static OWSDisappearingMessagesFinder *defaultInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
defaultInstance = [[self alloc] initWithStorageManager:[TSStorageManager sharedManager]];
});
return defaultInstance;
}
- (NSArray<NSString *> *)fetchUnstartedExpiringMessageIdsInThread:(TSThread *)thread
{
NSMutableArray<NSString *> *messageIds = [NSMutableArray new];
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ = 0 AND %@ = \"%@\"",
OWSDisappearingMessageFinderExpiresAtColumn,
@ -32,19 +58,19 @@ static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_mess
thread.uniqueId];
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
enumerateKeysMatchingQuery:query
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
[messageIds addObject:key];
}];
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
enumerateKeysMatchingQuery:query
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
[messageIds addObject:key];
}];
}];
return [messageIds copy];
}
- (NSArray<NSString *> *)fetchExpiredMessageIdsWithTransaction:(YapDatabaseReadTransaction *_Nonnull)transaction
- (NSArray<NSString *> *)fetchExpiredMessageIds
{
OWSAssert(transaction);
NSMutableArray<NSString *> *messageIds = [NSMutableArray new];
uint64_t now = [NSDate ows_millisecondTimeStamp];
@ -54,31 +80,33 @@ static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_mess
OWSDisappearingMessageFinderExpiresAtColumn,
now];
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
enumerateKeysMatchingQuery:query
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
[messageIds addObject:key];
}];
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
enumerateKeysMatchingQuery:query
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
[messageIds addObject:key];
}];
}];
return [messageIds copy];
}
- (nullable NSNumber *)nextExpirationTimestampWithTransaction:(YapDatabaseReadTransaction *)transaction
- (nullable NSNumber *)nextExpirationTimestamp
{
OWSAssert(transaction);
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ > 0 ORDER BY %@ ASC",
OWSDisappearingMessageFinderExpiresAtColumn,
OWSDisappearingMessageFinderExpiresAtColumn];
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
__block TSMessage *firstMessage;
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
enumerateKeysAndObjectsMatchingQuery:query
usingBlock:^void(NSString *collection, NSString *key, id object, BOOL *stop) {
firstMessage = (TSMessage *)object;
*stop = YES;
}];
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
enumerateKeysAndObjectsMatchingQuery:query
usingBlock:^void(NSString *collection, NSString *key, id object, BOOL *stop) {
firstMessage = (TSMessage *)object;
*stop = YES;
}];
}];
if (firstMessage && firstMessage.expiresAt > 0) {
return [NSNumber numberWithUnsignedLongLong:firstMessage.expiresAt];
@ -87,15 +115,10 @@ static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_mess
return nil;
}
- (void)enumerateUnstartedExpiringMessagesInThread:(TSThread *)thread
block:(void (^_Nonnull)(TSMessage *message))block
transaction:(YapDatabaseReadTransaction *)transaction
- (void)enumerateUnstartedExpiringMessagesInThread:(TSThread *)thread block:(void (^_Nonnull)(TSMessage *message))block
{
OWSAssert(transaction);
for (NSString *expiringMessageId in
[self fetchUnstartedExpiringMessageIdsInThread:thread transaction:transaction]) {
TSMessage *_Nullable message = [TSMessage fetchObjectWithUniqueID:expiringMessageId transaction:transaction];
for (NSString *expiringMessageId in [self fetchUnstartedExpiringMessageIdsInThread:thread]) {
TSMessage *_Nullable message = [TSMessage fetchObjectWithUniqueID:expiringMessageId];
if ([message isKindOfClass:[TSMessage class]]) {
block(message);
} else {
@ -109,30 +132,23 @@ static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_mess
* We don't want to instantiate potentially many messages at once.
*/
- (NSArray<TSMessage *> *)fetchUnstartedExpiringMessagesInThread:(TSThread *)thread
transaction:(YapDatabaseReadTransaction *)transaction
{
OWSAssert(transaction);
NSMutableArray<TSMessage *> *messages = [NSMutableArray new];
[self enumerateUnstartedExpiringMessagesInThread:thread
block:^(TSMessage *message) {
block:^(TSMessage *_Nonnull message) {
[messages addObject:message];
}
transaction:transaction];
}];
return [messages copy];
}
- (void)enumerateExpiredMessagesWithBlock:(void (^_Nonnull)(TSMessage *message))block
transaction:(YapDatabaseReadTransaction *)transaction
{
OWSAssert(transaction);
// Since we can't directly mutate the enumerated expired messages, we store only their ids in hopes of saving a
// little memory and then enumerate the (larger) TSMessage objects one at a time.
for (NSString *expiredMessageId in [self fetchExpiredMessageIdsWithTransaction:transaction]) {
TSMessage *_Nullable message = [TSMessage fetchObjectWithUniqueID:expiredMessageId transaction:transaction];
for (NSString *expiredMessageId in [self fetchExpiredMessageIds]) {
TSMessage *_Nullable message = [TSMessage fetchObjectWithUniqueID:expiredMessageId];
if ([message isKindOfClass:[TSMessage class]]) {
block(message);
} else {
@ -145,22 +161,19 @@ static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_mess
* Don't use this in production. Useful for testing.
* We don't want to instantiate potentially many messages at once.
*/
- (NSArray<TSMessage *> *)fetchExpiredMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction
- (NSArray<TSMessage *> *)fetchExpiredMessages
{
OWSAssert(transaction);
NSMutableArray<TSMessage *> *messages = [NSMutableArray new];
[self enumerateExpiredMessagesWithBlock:^(TSMessage *message) {
[self enumerateExpiredMessagesWithBlock:^(TSMessage *_Nonnull message) {
[messages addObject:message];
}
transaction:transaction];
}];
return [messages copy];
}
#pragma mark - YapDatabaseExtension
+ (YapDatabaseSecondaryIndex *)indexDatabaseExtension
- (YapDatabaseSecondaryIndex *)indexDatabaseExtension
{
YapDatabaseSecondaryIndexSetup *setup = [YapDatabaseSecondaryIndexSetup new];
[setup addColumn:OWSDisappearingMessageFinderExpiresAtColumn withType:YapDatabaseSecondaryIndexTypeInteger];
@ -189,23 +202,23 @@ static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_mess
}
// Useful for tests, don't use in app startup path because it's slow.
+ (void)blockingRegisterDatabaseExtensions:(TSStorageManager *)storageManager
- (void)blockingRegisterDatabaseExtensions
{
[storageManager.database registerExtension:[self indexDatabaseExtension]
withName:OWSDisappearingMessageFinderExpiresAtIndex];
[self.storageManager.database registerExtension:[self indexDatabaseExtension]
withName:OWSDisappearingMessageFinderExpiresAtIndex];
}
+ (void)asyncRegisterDatabaseExtensions:(TSStorageManager *)storageManager
- (void)asyncRegisterDatabaseExtensions
{
[storageManager.database asyncRegisterExtension:[self indexDatabaseExtension]
withName:OWSDisappearingMessageFinderExpiresAtIndex
completionBlock:^(BOOL ready) {
if (ready) {
DDLogDebug(@"%@ completed registering extension async.", self.tag);
} else {
DDLogError(@"%@ failed registering extension async.", self.tag);
}
}];
[self.storageManager.database asyncRegisterExtension:[self indexDatabaseExtension]
withName:OWSDisappearingMessageFinderExpiresAtIndex
completionBlock:^(BOOL ready) {
if (ready) {
DDLogDebug(@"%@ completed registering extension async.", self.tag);
} else {
DDLogError(@"%@ failed registering extension async.", self.tag);
}
}];
}
#pragma mark - Logging

View File

@ -17,10 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSDisappearingMessagesJob ()
@property (nonatomic, readonly) TSStorageManager *storageManager;
@property (nonatomic, readonly) YapDatabaseConnection *databaseConnection;
// This property should only be accessed on the serialQueue.
@property (nonatomic, readonly) OWSDisappearingMessagesFinder *disappearingMessagesFinder;
// These three properties should only be accessed on the main thread.
@ -51,9 +48,7 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
_storageManager = storageManager;
_databaseConnection = storageManager.newDatabaseConnection;
_disappearingMessagesFinder = [OWSDisappearingMessagesFinder new];
_disappearingMessagesFinder = [[OWSDisappearingMessagesFinder alloc] initWithStorageManager:storageManager];
OWSSingletonAssert();
@ -84,32 +79,26 @@ NS_ASSUME_NONNULL_BEGIN
return queue;
}
// This method should only be called on the serialQueue.
- (void)run
{
uint64_t now = [NSDate ows_millisecondTimeStamp];
__block uint expirationCount = 0;
[self.databaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self.disappearingMessagesFinder enumerateExpiredMessagesWithBlock:^(TSMessage *message) {
// sanity check
if (message.expiresAt > now) {
DDLogError(
@"%@ Refusing to remove message which doesn't expire until: %lld", self.tag, message.expiresAt);
return;
}
DDLogDebug(@"%@ Removing message which expired at: %lld", self.tag, message.expiresAt);
[message removeWithTransaction:transaction];
expirationCount++;
[self.disappearingMessagesFinder enumerateExpiredMessagesWithBlock:^(TSMessage *message) {
// sanity check
if (message.expiresAt > now) {
DDLogError(@"%@ Refusing to remove message which doesn't expire until: %lld", self.tag, message.expiresAt);
return;
}
transaction:transaction];
DDLogDebug(@"%@ Removing message which expired at: %lld", self.tag, message.expiresAt);
[message remove];
expirationCount++;
}];
DDLogDebug(@"%@ Removed %u expired messages", self.tag, expirationCount);
}
// This method should only be called on the serialQueue.
- (void)runLoop
{
DDLogVerbose(@"%@ Run", self.tag);
@ -117,11 +106,7 @@ NS_ASSUME_NONNULL_BEGIN
[self run];
uint64_t now = [NSDate ows_millisecondTimeStamp];
__block NSNumber *nextExpirationTimestampNumber;
[self.databaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
nextExpirationTimestampNumber =
[self.disappearingMessagesFinder nextExpirationTimestampWithTransaction:transaction];
}];
NSNumber *nextExpirationTimestampNumber = [self.disappearingMessagesFinder nextExpirationTimestamp];
if (!nextExpirationTimestampNumber) {
// In theory we could kill the loop here. It should resume when the next expiring message is saved,
// But this is a safeguard for any race conditions that exist while running the job as a new message is saved.
@ -164,20 +149,8 @@ NS_ASSUME_NONNULL_BEGIN
});
}
// This method should only be called on the serialQueue.
- (void)setExpirationForMessage:(TSMessage *)message expirationStartedAt:(uint64_t)expirationStartedAt
{
[self.databaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self setExpirationForMessage:message expirationStartedAt:expirationStartedAt transaction:transaction];
}];
}
- (void)setExpirationForMessage:(TSMessage *)message
expirationStartedAt:(uint64_t)expirationStartedAt
transaction:(YapDatabaseReadWriteTransaction *_Nonnull)transaction
{
OWSAssert(transaction);
if (!message.isExpiringMessage) {
return;
}
@ -188,7 +161,7 @@ NS_ASSUME_NONNULL_BEGIN
// Don't clobber if multiple actions simultaneously triggered expiration.
if (message.expireStartedAt == 0 || message.expireStartedAt > expirationStartedAt) {
message.expireStartedAt = expirationStartedAt;
[message saveWithTransaction:transaction];
[message save];
}
// Necessary that the async expiration run happens *after* the message is saved with expiration configuration.
@ -202,26 +175,19 @@ NS_ASSUME_NONNULL_BEGIN
});
}
// This method should only be called on the serialQueue.
- (void)setExpirationsForThread:(TSThread *)thread
{
uint64_t now = [NSDate ows_millisecondTimeStamp];
[self.databaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self.disappearingMessagesFinder
enumerateUnstartedExpiringMessagesInThread:thread
block:^(TSMessage *_Nonnull message) {
DDLogWarn(
@"%@ Starting expiring message which should have already "
@"been started.",
self.tag);
// specify "now" in case D.M. have since been disabled, but we have
// existing unstarted expiring messages that still need to expire.
[self setExpirationForMessage:message
expirationStartedAt:now
transaction:transaction];
}
transaction:transaction];
}];
[self.disappearingMessagesFinder
enumerateUnstartedExpiringMessagesInThread:thread
block:^(TSMessage *_Nonnull message) {
DDLogWarn(@"%@ Starting expiring message which should have already "
@"been started.",
self.tag);
// specify "now" in case D.M. have since been disabled, but we have
// existing unstarted expiring messages that still need to expire.
[self setExpirationForMessage:message expirationStartedAt:now];
}];
}
+ (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message

View File

@ -36,36 +36,33 @@ static NSString *const OWSFailedAttachmentDownloadsJobAttachmentStateIndex = @"i
return self;
}
- (NSArray<NSString *> *)fetchAttemptingOutAttachmentIdsWithTransaction:
(YapDatabaseReadWriteTransaction *_Nonnull)transaction
- (NSArray<NSString *> *)fetchAttemptingOutAttachmentIds:(YapDatabaseConnection *)dbConnection
{
OWSAssert(transaction);
NSMutableArray<NSString *> *attachmentIds = [NSMutableArray new];
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ != %d",
OWSFailedAttachmentDownloadsJobAttachmentStateColumn,
(int)TSAttachmentPointerStateFailed];
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
[[transaction ext:OWSFailedAttachmentDownloadsJobAttachmentStateIndex]
enumerateKeysMatchingQuery:query
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
[attachmentIds addObject:key];
}];
[dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[[transaction ext:OWSFailedAttachmentDownloadsJobAttachmentStateIndex]
enumerateKeysMatchingQuery:query
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
[attachmentIds addObject:key];
}];
}];
return [attachmentIds copy];
}
- (void)enumerateAttemptingOutAttachmentsWithBlock:(void (^_Nonnull)(TSAttachmentPointer *attachment))block
transaction:(YapDatabaseReadWriteTransaction *_Nonnull)transaction
{
OWSAssert(transaction);
YapDatabaseConnection *dbConnection = [self.storageManager newDatabaseConnection];
// Since we can't directly mutate the enumerated attachments, we store only their ids in hopes
// of saving a little memory and then enumerate the (larger) TSAttachment objects one at a time.
for (NSString *attachmentId in [self fetchAttemptingOutAttachmentIdsWithTransaction:transaction]) {
TSAttachmentPointer *_Nullable attachment =
[TSAttachmentPointer fetchObjectWithUniqueID:attachmentId transaction:transaction];
for (NSString *attachmentId in [self fetchAttemptingOutAttachmentIds:dbConnection]) {
TSAttachmentPointer *_Nullable attachment = [TSAttachmentPointer fetchObjectWithUniqueID:attachmentId];
if ([attachment isKindOfClass:[TSAttachmentPointer class]]) {
block(attachment);
} else {
@ -77,19 +74,15 @@ static NSString *const OWSFailedAttachmentDownloadsJobAttachmentStateIndex = @"i
- (void)run
{
__block uint count = 0;
[[self.storageManager newDatabaseConnection]
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self enumerateAttemptingOutAttachmentsWithBlock:^(TSAttachmentPointer *attachment) {
// sanity check
if (attachment.state != TSAttachmentPointerStateFailed) {
DDLogDebug(@"%@ marking attachment as failed", self.tag);
attachment.state = TSAttachmentPointerStateFailed;
[attachment saveWithTransaction:transaction];
count++;
}
}
transaction:transaction];
}];
[self enumerateAttemptingOutAttachmentsWithBlock:^(TSAttachmentPointer *attachment) {
// sanity check
if (attachment.state != TSAttachmentPointerStateFailed) {
DDLogDebug(@"%@ marking attachment as failed", self.tag);
attachment.state = TSAttachmentPointerStateFailed;
[attachment save];
count++;
}
}];
DDLogDebug(@"%@ Marked %u attachments as unsent", self.tag, count);
}

View File

@ -37,36 +37,33 @@ static NSString *const OWSFailedMessagesJobMessageStateIndex = @"index_outoing_m
return self;
}
- (NSArray<NSString *> *)fetchAttemptingOutMessageIdsWithTransaction:
(YapDatabaseReadWriteTransaction *_Nonnull)transaction
- (NSArray<NSString *> *)fetchAttemptingOutMessageIds:(YapDatabaseConnection *)dbConnection
{
OWSAssert(transaction);
NSMutableArray<NSString *> *messageIds = [NSMutableArray new];
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ == %d",
OWSFailedMessagesJobMessageStateColumn,
(int)TSOutgoingMessageStateAttemptingOut];
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
[[transaction ext:OWSFailedMessagesJobMessageStateIndex]
enumerateKeysMatchingQuery:query
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
[messageIds addObject:key];
}];
[dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[[transaction ext:OWSFailedMessagesJobMessageStateIndex]
enumerateKeysMatchingQuery:query
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
[messageIds addObject:key];
}];
}];
return [messageIds copy];
}
- (void)enumerateAttemptingOutMessagesWithBlock:(void (^_Nonnull)(TSOutgoingMessage *message))block
transaction:(YapDatabaseReadWriteTransaction *_Nonnull)transaction
{
OWSAssert(transaction);
YapDatabaseConnection *dbConnection = [self.storageManager newDatabaseConnection];
// Since we can't directly mutate the enumerated "attempting out" expired messages, we store only their ids in hopes
// of saving a little memory and then enumerate the (larger) TSMessage objects one at a time.
for (NSString *expiredMessageId in [self fetchAttemptingOutMessageIdsWithTransaction:transaction]) {
TSOutgoingMessage *_Nullable message =
[TSOutgoingMessage fetchObjectWithUniqueID:expiredMessageId transaction:transaction];
for (NSString *expiredMessageId in [self fetchAttemptingOutMessageIds:dbConnection]) {
TSOutgoingMessage *_Nullable message = [TSOutgoingMessage fetchObjectWithUniqueID:expiredMessageId];
if ([message isKindOfClass:[TSOutgoingMessage class]]) {
block(message);
} else {
@ -78,26 +75,18 @@ static NSString *const OWSFailedMessagesJobMessageStateIndex = @"index_outoing_m
- (void)run
{
__block uint count = 0;
[self enumerateAttemptingOutMessagesWithBlock:^(TSOutgoingMessage *message) {
// sanity check
OWSAssert(message.messageState == TSOutgoingMessageStateAttemptingOut);
if (message.messageState != TSOutgoingMessageStateAttemptingOut) {
DDLogError(@"%@ Refusing to mark as unsent message with state: %d", self.tag, (int)message.messageState);
return;
}
[[self.storageManager newDatabaseConnection]
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self enumerateAttemptingOutMessagesWithBlock:^(TSOutgoingMessage *message) {
// sanity check
OWSAssert(message.messageState == TSOutgoingMessageStateAttemptingOut);
if (message.messageState != TSOutgoingMessageStateAttemptingOut) {
DDLogError(
@"%@ Refusing to mark as unsent message with state: %d", self.tag, (int)message.messageState);
return;
}
DDLogDebug(@"%@ marking message as unsent: %@", self.tag, message.uniqueId);
[message updateWithMessageState:TSOutgoingMessageStateUnsent transaction:transaction];
OWSAssert(message.messageState == TSOutgoingMessageStateUnsent);
count++;
}
transaction:transaction];
}];
DDLogDebug(@"%@ marking message as unsent", self.tag);
[message updateWithMessageState:TSOutgoingMessageStateUnsent];
count++;
}];
DDLogDebug(@"%@ Marked %u messages as unsent", self.tag, count);
}

View File

@ -1,53 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSRecipientIdentity.h"
#import <AxolotlKit/IdentityKeyStore.h>
NS_ASSUME_NONNULL_BEGIN
extern NSString *const TSStorageManagerTrustedKeysCollection;
// This notification will be fired whenever identities are created
// or their verification state changes.
extern NSString *const kNSNotificationName_IdentityStateDidChange;
// number of bytes in a signal identity key, excluding the key-type byte.
extern const NSUInteger kIdentityKeyLength;
@class OWSRecipientIdentity;
@class OWSSignalServiceProtosVerified;
// This class can be safely accessed and used from any thread.
@interface OWSIdentityManager : NSObject <IdentityKeyStore>
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)sharedManager;
- (void)generateNewIdentityKey;
- (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId;
- (void)setVerificationState:(OWSVerificationState)verificationState
identityKey:(NSData *)identityKey
recipientId:(NSString *)recipientId
isUserInitiatedChange:(BOOL)isUserInitiatedChange;
- (OWSVerificationState)verificationStateForRecipientId:(NSString *)recipientId;
- (nullable OWSRecipientIdentity *)recipientIdentityForRecipientId:(NSString *)recipientId;
/**
* @param recipientId unique stable identifier for the recipient, e.g. e164 phone number
* @returns nil if the recipient does not exist, or is trusted for sending
* else returns the untrusted recipient.
*/
- (nullable OWSRecipientIdentity *)untrustedIdentityForSendingToRecipientId:(NSString *)recipientId;
- (void)processIncomingSyncMessage:(OWSSignalServiceProtosVerified *)verified;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,779 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSIdentityManager.h"
#import "NSDate+millisecondTimeStamp.h"
#import "NotificationsProtocol.h"
#import "OWSError.h"
#import "OWSMessageSender.h"
#import "OWSOutgoingNullMessage.h"
#import "OWSRecipientIdentity.h"
#import "OWSVerificationStateChangeMessage.h"
#import "OWSVerificationStateSyncMessage.h"
#import "TSAccountManager.h"
#import "TSContactThread.h"
#import "TSErrorMessage.h"
#import "TSGroupThread.h"
#import "TSStorageManager+keyingMaterial.h"
#import "TSStorageManager+sessionStore.h"
#import "TSStorageManager.h"
#import "TextSecureKitEnv.h"
#import <25519/Curve25519.h>
#import <AxolotlKit/NSData+keyVersionByte.h>
NS_ASSUME_NONNULL_BEGIN
// Storing our own identity key
NSString *const TSStorageManagerIdentityKeyStoreIdentityKey = @"TSStorageManagerIdentityKeyStoreIdentityKey";
NSString *const TSStorageManagerIdentityKeyStoreCollection = @"TSStorageManagerIdentityKeyStoreCollection";
// Storing recipients identity keys
NSString *const TSStorageManagerTrustedKeysCollection = @"TSStorageManagerTrustedKeysCollection";
NSString *const OWSIdentityManager_QueuedVerificationStateSyncMessages =
@"OWSIdentityManager_QueuedVerificationStateSyncMessages";
// Don't trust an identity for sending to unless they've been around for at least this long
const NSTimeInterval kIdentityKeyStoreNonBlockingSecondsThreshold = 5.0;
// The canonical key includes 32 bytes of identity material plus one byte specifying the key type
const NSUInteger kIdentityKeyLength = 33;
// Cryptographic operations do not use the "type" byte of the identity key, so, for legacy reasons we store just
// the identity material.
// TODO: migrate to storing the full 33 byte representation.
const NSUInteger kStoredIdentityKeyLength = 32;
NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationName_IdentityStateDidChange";
@interface OWSIdentityManager ()
@property (nonatomic, readonly) TSStorageManager *storageManager;
@property (nonatomic, readonly) OWSMessageSender *messageSender;
@end
#pragma mark -
@implementation OWSIdentityManager
+ (instancetype)sharedManager
{
static OWSIdentityManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] initDefault];
});
return sharedMyManager;
}
- (instancetype)initDefault
{
TSStorageManager *storageManager = [TSStorageManager sharedManager];
OWSMessageSender *messageSender = [TextSecureKitEnv sharedEnv].messageSender;
return [self initWithStorageManager:storageManager messageSender:messageSender];
}
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
messageSender:(OWSMessageSender *)messageSender
{
self = [super init];
if (!self) {
return self;
}
OWSAssert(storageManager);
OWSAssert(messageSender);
_storageManager = storageManager;
_messageSender = messageSender;
OWSSingletonAssert();
[self observeNotifications];
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)observeNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
- (void)generateNewIdentityKey
{
[self.storageManager setObject:[Curve25519 generateKeyPair]
forKey:TSStorageManagerIdentityKeyStoreIdentityKey
inCollection:TSStorageManagerIdentityKeyStoreCollection];
}
- (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId
{
@synchronized(self)
{
return [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId].identityKey;
}
}
- (nullable ECKeyPair *)identityKeyPair
{
return [self.storageManager keyPairForKey:TSStorageManagerIdentityKeyStoreIdentityKey
inCollection:TSStorageManagerIdentityKeyStoreCollection];
}
- (int)localRegistrationId
{
return (int)[TSAccountManager getOrGenerateRegistrationId];
}
- (BOOL)saveRemoteIdentity:(NSData *)identityKey recipientId:(NSString *)recipientId
{
OWSAssert(identityKey.length == kStoredIdentityKeyLength);
OWSAssert(recipientId.length > 0);
@synchronized(self)
{
// Deprecated. We actually no longer use the TSStorageManagerTrustedKeysCollection for trust
// decisions, but it's desirable to try to keep it up to date with our trusted identitys
// while we're switching between versions, e.g. so we don't get into a state where we have a
// session for an identity not in our key store.
[self.storageManager setObject:identityKey
forKey:recipientId
inCollection:TSStorageManagerTrustedKeysCollection];
OWSRecipientIdentity *existingIdentity = [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
if (existingIdentity == nil) {
DDLogInfo(@"%@ saving first use identity for recipient: %@", self.tag, recipientId);
[[[OWSRecipientIdentity alloc] initWithRecipientId:recipientId
identityKey:identityKey
isFirstKnownKey:YES
createdAt:[NSDate new]
verificationState:OWSVerificationStateDefault] save];
// Cancel any pending verification state sync messages for this recipient.
[self clearSyncMessageForRecipientId:recipientId];
[self fireIdentityStateChangeNotification];
return NO;
}
if (![existingIdentity.identityKey isEqual:identityKey]) {
OWSVerificationState verificationState;
switch (existingIdentity.verificationState) {
case OWSVerificationStateDefault:
verificationState = OWSVerificationStateDefault;
break;
case OWSVerificationStateVerified:
case OWSVerificationStateNoLongerVerified:
verificationState = OWSVerificationStateNoLongerVerified;
break;
}
DDLogInfo(@"%@ replacing identity for existing recipient: %@ (%@ -> %@)",
self.tag,
recipientId,
OWSVerificationStateToString(existingIdentity.verificationState),
OWSVerificationStateToString(verificationState));
[self createIdentityChangeInfoMessageForRecipientId:recipientId];
[[[OWSRecipientIdentity alloc] initWithRecipientId:recipientId
identityKey:identityKey
isFirstKnownKey:NO
createdAt:[NSDate new]
verificationState:verificationState] save];
dispatch_async([OWSDispatch sessionStoreQueue], ^{
[self.storageManager archiveAllSessionsForContact:recipientId];
});
// Cancel any pending verification state sync messages for this recipient.
[self clearSyncMessageForRecipientId:recipientId];
[self fireIdentityStateChangeNotification];
return YES;
}
return NO;
}
}
- (void)setVerificationState:(OWSVerificationState)verificationState
identityKey:(NSData *)identityKey
recipientId:(NSString *)recipientId
isUserInitiatedChange:(BOOL)isUserInitiatedChange
{
OWSAssert(identityKey.length == kStoredIdentityKeyLength);
OWSAssert(recipientId.length > 0);
@synchronized(self)
{
// Ensure a remote identity exists for this key. We may be learning about
// it for the first time.
[self saveRemoteIdentity:identityKey recipientId:recipientId];
OWSRecipientIdentity *recipientIdentity = [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
if (recipientIdentity == nil) {
OWSFail(@"Missing expected identity: %@", recipientId);
return;
}
if (recipientIdentity.verificationState == verificationState) {
return;
}
DDLogInfo(@"%@ setVerificationState: %@ (%@ -> %@)",
self.tag,
recipientId,
OWSVerificationStateToString(recipientIdentity.verificationState),
OWSVerificationStateToString(verificationState));
[recipientIdentity updateWithVerificationState:verificationState];
if (isUserInitiatedChange) {
[self saveChangeMessagesForRecipientId:recipientId verificationState:verificationState isLocalChange:YES];
[self enqueueSyncMessageForVerificationStateForRecipientId:recipientId];
} else {
// Cancel any pending verification state sync messages for this recipient.
[self clearSyncMessageForRecipientId:recipientId];
}
}
[self fireIdentityStateChangeNotification];
}
- (OWSVerificationState)verificationStateForRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
@synchronized(self)
{
OWSRecipientIdentity *_Nullable currentIdentity = [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
if (!currentIdentity) {
// We might not know the identity for this recipient yet.
return OWSVerificationStateDefault;
}
return currentIdentity.verificationState;
}
}
- (nullable OWSRecipientIdentity *)recipientIdentityForRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
@synchronized(self)
{
return [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
}
}
- (nullable OWSRecipientIdentity *)untrustedIdentityForSendingToRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
@synchronized(self)
{
OWSRecipientIdentity *_Nullable recipientIdentity = [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
if (recipientIdentity == nil) {
// trust on first use
return nil;
}
BOOL isTrusted = [self isTrustedIdentityKey:recipientIdentity.identityKey
recipientId:recipientId
direction:TSMessageDirectionOutgoing];
if (isTrusted) {
return nil;
} else {
return recipientIdentity;
}
}
}
- (void)fireIdentityStateChangeNotification
{
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:kNSNotificationName_IdentityStateDidChange
object:nil
userInfo:nil];
});
}
- (BOOL)isTrustedIdentityKey:(NSData *)identityKey
recipientId:(NSString *)recipientId
direction:(TSMessageDirection)direction
{
OWSAssert(identityKey.length == kStoredIdentityKeyLength);
OWSAssert(recipientId.length > 0);
OWSAssert(direction != TSMessageDirectionUnknown);
@synchronized(self)
{
if ([[self.storageManager localNumber] isEqualToString:recipientId]) {
if ([[self identityKeyPair].publicKey isEqualToData:identityKey]) {
return YES;
} else {
DDLogError(@"%@ Wrong identity: %@ for local key: %@, recipientId: %@",
self.tag,
identityKey,
[self identityKeyPair].publicKey,
recipientId);
OWSAssert(NO);
return NO;
}
}
switch (direction) {
case TSMessageDirectionIncoming: {
return YES;
}
case TSMessageDirectionOutgoing: {
OWSRecipientIdentity *existingIdentity = [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
return [self isTrustedKey:identityKey forSendingToIdentity:existingIdentity];
}
default: {
DDLogError(@"%@ unexpected message direction: %ld", self.tag, (long)direction);
OWSAssert(NO);
return NO;
}
}
}
}
- (BOOL)isTrustedKey:(NSData *)identityKey forSendingToIdentity:(nullable OWSRecipientIdentity *)recipientIdentity
{
OWSAssert(identityKey.length == kStoredIdentityKeyLength);
@synchronized(self)
{
if (recipientIdentity == nil) {
return YES;
}
OWSAssert(recipientIdentity.identityKey.length == kStoredIdentityKeyLength);
if (![recipientIdentity.identityKey isEqualToData:identityKey]) {
DDLogWarn(@"%@ key mismatch for recipient: %@", self.tag, recipientIdentity.recipientId);
return NO;
}
if ([recipientIdentity isFirstKnownKey]) {
return YES;
}
switch (recipientIdentity.verificationState) {
case OWSVerificationStateDefault: {
BOOL isNew = (fabs([recipientIdentity.createdAt timeIntervalSinceNow])
< kIdentityKeyStoreNonBlockingSecondsThreshold);
if (isNew) {
DDLogWarn(@"%@ not trusting new identity for recipient: %@", self.tag, recipientIdentity.recipientId);
return NO;
} else {
return YES;
}
}
case OWSVerificationStateVerified:
return YES;
case OWSVerificationStateNoLongerVerified:
DDLogWarn(@"%@ not trusting no longer verified identity for recipient: %@", self.tag, recipientIdentity.recipientId);
return NO;
}
}
}
- (void)createIdentityChangeInfoMessageForRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
NSMutableArray<TSMessage *> *messages = [NSMutableArray new];
TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:recipientId];
OWSAssert(contactThread != nil);
TSErrorMessage *errorMessage =
[TSErrorMessage nonblockingIdentityChangeInThread:contactThread recipientId:recipientId];
[messages addObject:errorMessage];
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:contactThread];
for (TSGroupThread *groupThread in [TSGroupThread groupThreadsWithRecipientId:recipientId]) {
[messages addObject:[TSErrorMessage nonblockingIdentityChangeInThread:groupThread recipientId:recipientId]];
}
[self.storageManager.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (TSMessage *message in messages) {
[message saveWithTransaction:transaction];
}
}];
}
- (void)enqueueSyncMessageForVerificationStateForRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(self)
{
[self.storageManager setObject:recipientId
forKey:recipientId
inCollection:OWSIdentityManager_QueuedVerificationStateSyncMessages];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self tryToSyncQueuedVerificationStates];
});
});
}
- (void)tryToSyncQueuedVerificationStates
{
OWSAssert([NSThread isMainThread]);
if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
// Only try to sync if the app is active to avoid interfering with startup.
//
// applicationDidBecomeActive: will try to sync again when the app becomes active.
return;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(self)
{
NSMutableArray<NSString *> *recipientIds = [NSMutableArray new];
[self.storageManager.dbReadWriteConnection readWriteWithBlock:^(
YapDatabaseReadWriteTransaction *transaction) {
[transaction enumerateKeysAndObjectsInCollection:OWSIdentityManager_QueuedVerificationStateSyncMessages
usingBlock:^(NSString *_Nonnull recipientId,
id _Nonnull object,
BOOL *_Nonnull stop) {
[recipientIds addObject:recipientId];
}];
}];
NSMutableArray<OWSVerificationStateSyncMessage *> *messages = [NSMutableArray new];
for (NSString *recipientId in recipientIds) {
OWSRecipientIdentity *recipientIdentity = [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
if (!recipientIdentity) {
OWSFail(@"Could not load recipient identity for recipientId: %@", recipientId);
continue;
}
if (recipientIdentity.recipientId.length < 1) {
OWSFail(@"Invalid recipient identity for recipientId: %@", recipientId);
continue;
}
// Prepend key type for transit.
// TODO we should just be storing the key type so we don't have to juggle re-adding it.
NSData *identityKey = [recipientIdentity.identityKey prependKeyType];
if (identityKey.length != kIdentityKeyLength) {
OWSFail(@"Invalid recipient identitykey for recipientId: %@ key: %@", recipientId, identityKey);
continue;
}
if (recipientIdentity.verificationState == OWSVerificationStateNoLongerVerified) {
// We don't want to sync "no longer verified" state. Other clients can
// figure this out from the /profile/ endpoint, and this can cause data
// loss as a user's devices overwrite each other's verification.
OWSFail(@"Queue verification state had unexpected value: %@ recipientId: %@",
OWSVerificationStateToString(recipientIdentity.verificationState),
recipientId);
continue;
}
OWSVerificationStateSyncMessage *message = [[OWSVerificationStateSyncMessage alloc]
initWithVerificationState:recipientIdentity.verificationState
identityKey:identityKey
verificationForRecipientId:recipientIdentity.recipientId];
[messages addObject:message];
}
if (messages.count > 0) {
dispatch_async(dispatch_get_main_queue(), ^{
for (OWSVerificationStateSyncMessage *message in messages) {
[self sendSyncVerificationStateMessage:message];
}
});
}
}
});
}
- (void)sendSyncVerificationStateMessage:(OWSVerificationStateSyncMessage *)message
{
OWSAssert(message);
OWSAssert(message.verificationForRecipientId.length > 0);
OWSAssert([NSThread isMainThread]);
TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:message.verificationForRecipientId];
// Send null message to appear as though we're sending a normal message to cover the sync messsage sent
// subsequently
OWSOutgoingNullMessage *nullMessage = [[OWSOutgoingNullMessage alloc] initWithContactThread:contactThread
verificationStateSyncMessage:message];
[self.messageSender sendMessage:nullMessage
success:^{
dispatch_async(dispatch_get_main_queue(), ^{
DDLogInfo(@"%@ Successfully sent verification state NullMessage", self.tag);
[self.messageSender sendMessage:message
success:^{
DDLogInfo(@"%@ Successfully sent verification state sync message", self.tag);
// Record that this verification state was successfully synced.
[self clearSyncMessageForRecipientId:message.verificationForRecipientId];
}
failure:^(NSError *error) {
DDLogError(
@"%@ Failed to send verification state sync message with error: %@", self.tag, error);
}];
});
}
failure:^(NSError *_Nonnull error) {
DDLogError(@"%@ Failed to send verification state NullMessage with error: %@", self.tag, error);
if (error.code == OWSErrorCodeNoSuchSignalRecipient) {
DDLogInfo(@"%@ Removing retries for syncing verification state, since user is no longer registered: %@",
self.tag,
message.verificationForRecipientId);
// Otherwise this will fail forever.
[self clearSyncMessageForRecipientId:message.verificationForRecipientId];
}
}];
}
- (void)clearSyncMessageForRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(self)
{
[self.storageManager removeObjectForKey:recipientId
inCollection:OWSIdentityManager_QueuedVerificationStateSyncMessages];
}
});
}
- (void)processIncomingSyncMessage:(OWSSignalServiceProtosVerified *)verified
{
NSString *recipientId = verified.destination;
if (recipientId.length < 1) {
OWSFail(@"Verification state sync message missing recipientId.");
return;
}
NSData *rawIdentityKey = verified.identityKey;
if (rawIdentityKey.length != kIdentityKeyLength) {
OWSFail(@"Verification state sync message for recipient: %@ with malformed identityKey: %@",
recipientId,
rawIdentityKey);
return;
}
NSData *identityKey = [rawIdentityKey removeKeyType];
switch (verified.state) {
case OWSSignalServiceProtosVerifiedStateDefault:
[self tryToApplyVerificationStateFromSyncMessage:OWSVerificationStateDefault
recipientId:recipientId
identityKey:identityKey
overwriteOnConflict:NO];
break;
case OWSSignalServiceProtosVerifiedStateVerified:
[self tryToApplyVerificationStateFromSyncMessage:OWSVerificationStateVerified
recipientId:recipientId
identityKey:identityKey
overwriteOnConflict:YES];
break;
case OWSSignalServiceProtosVerifiedStateUnverified:
OWSFail(@"Verification state sync message for recipientId: %@ has unexpected value: %@.",
recipientId,
OWSVerificationStateToString(OWSVerificationStateNoLongerVerified));
return;
}
[self fireIdentityStateChangeNotification];
}
- (void)tryToApplyVerificationStateFromSyncMessage:(OWSVerificationState)verificationState
recipientId:(NSString *)recipientId
identityKey:(NSData *)identityKey
overwriteOnConflict:(BOOL)overwriteOnConflict
{
if (recipientId.length < 1) {
OWSFail(@"Verification state sync message missing recipientId.");
return;
}
if (identityKey.length != kStoredIdentityKeyLength) {
OWSFail(@"Verification state sync message missing identityKey: %@", recipientId);
return;
}
@synchronized(self)
{
OWSRecipientIdentity *_Nullable recipientIdentity = [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
if (!recipientIdentity) {
// There's no existing recipient identity for this recipient.
// We should probably create one.
if (verificationState == OWSVerificationStateDefault) {
// There's no point in creating a new recipient identity just to
// set its verification state to default.
return;
}
// Ensure a remote identity exists for this key. We may be learning about
// it for the first time.
[self saveRemoteIdentity:identityKey recipientId:recipientId];
recipientIdentity = [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
if (recipientIdentity == nil) {
OWSFail(@"Missing expected identity: %@", recipientId);
return;
}
if (![recipientIdentity.recipientId isEqualToString:recipientId]) {
OWSFail(@"recipientIdentity has unexpected recipientId: %@", recipientId);
return;
}
if (![recipientIdentity.identityKey isEqualToData:identityKey]) {
OWSFail(@"recipientIdentity has unexpected identityKey: %@", recipientId);
return;
}
if (recipientIdentity.verificationState == verificationState) {
return;
}
DDLogInfo(@"%@ setVerificationState: %@ (%@ -> %@)",
self.tag,
recipientId,
OWSVerificationStateToString(recipientIdentity.verificationState),
OWSVerificationStateToString(verificationState));
[recipientIdentity updateWithVerificationState:verificationState];
// No need to call [saveChangeMessagesForRecipientId:..] since this is
// a new recipient.
} else {
// There's an existing recipient identity for this recipient.
// We should update it.
if (![recipientIdentity.recipientId isEqualToString:recipientId]) {
OWSFail(@"recipientIdentity has unexpected recipientId: %@", recipientId);
return;
}
if (![recipientIdentity.identityKey isEqualToData:identityKey]) {
// The conflict case where we receive a verification sync message
// whose identity key disagrees with the local identity key for
// this recipient.
if (!overwriteOnConflict) {
DDLogWarn(@"recipientIdentity has non-matching identityKey: %@", recipientId);
return;
}
DDLogWarn(@"recipientIdentity has non-matching identityKey; overwriting: %@", recipientId);
[self saveRemoteIdentity:identityKey recipientId:recipientId];
recipientIdentity = [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
if (recipientIdentity == nil) {
OWSFail(@"Missing expected identity: %@", recipientId);
return;
}
if (![recipientIdentity.recipientId isEqualToString:recipientId]) {
OWSFail(@"recipientIdentity has unexpected recipientId: %@", recipientId);
return;
}
if (![recipientIdentity.identityKey isEqualToData:identityKey]) {
OWSFail(@"recipientIdentity has unexpected identityKey: %@", recipientId);
return;
}
}
if (recipientIdentity.verificationState == verificationState) {
return;
}
[recipientIdentity updateWithVerificationState:verificationState];
[self saveChangeMessagesForRecipientId:recipientId verificationState:verificationState isLocalChange:NO];
}
}
}
// We only want to create change messages in response to user activity,
// on any of their devices.
- (void)saveChangeMessagesForRecipientId:(NSString *)recipientId
verificationState:(OWSVerificationState)verificationState
isLocalChange:(BOOL)isLocalChange
{
OWSAssert(recipientId.length > 0);
NSMutableArray<TSMessage *> *messages = [NSMutableArray new];
TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:recipientId];
OWSAssert(contactThread);
[messages addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
thread:contactThread
recipientId:recipientId
verificationState:verificationState
isLocalChange:isLocalChange]];
for (TSGroupThread *groupThread in [TSGroupThread groupThreadsWithRecipientId:recipientId]) {
[messages
addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
thread:groupThread
recipientId:recipientId
verificationState:verificationState
isLocalChange:isLocalChange]];
}
[self.storageManager.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (TSMessage *message in messages) {
[message saveWithTransaction:transaction];
}
}];
}
#pragma mark - Notifications
- (void)applicationDidBecomeActive:(NSNotification *)notification
{
OWSAssert([NSThread isMainThread]);
// We want to defer this so that we never call this method until
// [UIApplicationDelegate applicationDidBecomeActive:] is complete.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)1.f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self tryToSyncQueuedVerificationStates];
});
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -4,6 +4,8 @@
#import "OWSIncomingMessageReadObserver.h"
#import "NSDate+millisecondTimeStamp.h"
#import "OWSDisappearingMessagesConfiguration.h"
#import "OWSDisappearingMessagesJob.h"
#import "OWSSendReadReceiptsJob.h"
#import "TSIncomingMessage.h"
@ -11,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSIncomingMessageReadObserver ()
@property (nonatomic) BOOL isObserving;
@property BOOL isObserving;
@property (nonatomic, readonly) OWSSendReadReceiptsJob *sendReadReceiptsJob;
@end
@ -39,8 +41,6 @@ NS_ASSUME_NONNULL_BEGIN
- (void)startObserving
{
OWSAssert([NSThread isMainThread]);
if (self.isObserving) {
return;
}
@ -60,6 +60,7 @@ NS_ASSUME_NONNULL_BEGIN
}
TSIncomingMessage *message = (TSIncomingMessage *)notification.object;
[OWSDisappearingMessagesJob setExpirationForMessage:message];
[self.sendReadReceiptsJob runWith:message];
}

View File

@ -0,0 +1,20 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSMessageServiceParams.h"
NS_ASSUME_NONNULL_BEGIN
/**
* Contstructs the per-device-message parameters used when submitting a message to
* the Signal Web Service. Using a legacy parameter format. Cannot be used for Sync messages.
*/
@interface OWSLegacyMessageServiceParams : OWSMessageServiceParams
- (instancetype)initWithType:(TSWhisperMessageType)type
recipientId:(NSString *)destination
device:(int)deviceId
body:(NSData *)body
registrationId:(int)registrationId;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,33 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSLegacyMessageServiceParams.h"
NS_ASSUME_NONNULL_BEGIN
@implementation OWSLegacyMessageServiceParams
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
NSMutableDictionary *keys = [[super JSONKeyPathsByPropertyKey] mutableCopy];
[keys setObject:@"body" forKey:@"content"];
return [keys copy];
}
- (instancetype)initWithType:(TSWhisperMessageType)type
recipientId:(NSString *)destination
device:(int)deviceId
body:(NSData *)body
registrationId:(int)registrationId
{
self = [super initWithType:type recipientId:destination device:deviceId content:body registrationId:registrationId];
if (!self) {
return self;
}
return self;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -86,6 +86,14 @@ NS_SWIFT_NAME(MessageSender)
success:(void (^)())successHandler
failure:(void (^)(NSError *error))failureHandler;
/**
* Resend a message to a select recipient in a thread when previous sending failed due to key error.
* e.g. If a key change prevents one recipient from receiving the message, we don't want to resend to the entire group.
*/
- (void)resendMessageFromKeyError:(TSInvalidIdentityKeySendingErrorMessage *)errorMessage
success:(void (^)())successHandler
failure:(void (^)(NSError *error))failureHandler;
- (void)handleMessageSentRemotely:(TSOutgoingMessage *)message sentAt:(uint64_t)sentAt;
/**

View File

@ -4,13 +4,12 @@
#import "OWSMessageSender.h"
#import "ContactsUpdater.h"
#import "NSData+keyVersionByte.h"
#import "NSData+messagePadding.h"
#import "OWSBlockingManager.h"
#import "OWSDevice.h"
#import "OWSDisappearingMessagesJob.h"
#import "OWSError.h"
#import "OWSIdentityManager.h"
#import "OWSLegacyMessageServiceParams.h"
#import "OWSMessageServiceParams.h"
#import "OWSOutgoingSentMessageTranscript.h"
#import "OWSOutgoingSyncMessage.h"
@ -27,6 +26,7 @@
#import "TSNetworkManager.h"
#import "TSOutgoingMessage.h"
#import "TSPreKeyManager.h"
#import "TSStorageManager+IdentityKeyStore.h"
#import "TSStorageManager+PreKeyStore.h"
#import "TSStorageManager+SignedPreKeyStore.h"
#import "TSStorageManager+keyingMaterial.h"
@ -192,8 +192,8 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4;
[message updateWithMessageState:TSOutgoingMessageStateSentToService];
DDLogDebug(@"%@ succeeded.", strongSelf.tag);
aSuccessHandler();
[strongSelf markAsComplete];
};
@ -208,7 +208,6 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4;
DDLogDebug(@"%@ failed with error: %@", strongSelf.tag, error);
aFailureHandler(error);
[strongSelf markAsComplete];
};
@ -280,13 +279,10 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4;
- (void)tryWithRemainingRetries:(NSUInteger)remainingRetries
{
// Use this flag to ensure a given operation only succeeds or fails once.
__block BOOL onceFlag = NO;
RetryableFailureHandler retryableFailureHandler = ^(NSError *_Nonnull error) {
DDLogInfo(@"%@ Sending failed. Remaining retries: %lu", self.tag, (unsigned long)remainingRetries);
DDLogDebug(@"%@ remainingRetries: %lu", self.tag, (unsigned long)remainingRetries);
OWSAssert(!onceFlag);
onceFlag = YES;
RetryableFailureHandler retryableFailureHandler = ^(NSError *_Nonnull error) {
DDLogInfo(@"%@ Sending failed.", self.tag);
if (![error isRetryable] || [error isFatal]) {
DDLogInfo(@"%@ Skipping retry due to terminal error: %@", self.tag, error);
@ -303,29 +299,14 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4;
}
};
[self.messageSender attemptToSendMessage:self.message
success:^{
OWSAssert(!onceFlag);
onceFlag = YES;
self.successHandler();
}
failure:retryableFailureHandler];
[self.messageSender attemptToSendMessage:self.message success:self.successHandler failure:retryableFailureHandler];
}
- (void)markAsComplete
{
[self willChangeValueForKey:OWSSendMessageOperationKeyIsExecuting];
[self willChangeValueForKey:OWSSendMessageOperationKeyIsFinished];
// Ensure we call the success or failure handler exactly once.
@synchronized(self)
{
OWSAssert(self.operationState != OWSSendMessageOperationStateFinished);
self.operationState = OWSSendMessageOperationStateFinished;
}
self.operationState = OWSSendMessageOperationStateFinished;
[self didChangeValueForKey:OWSSendMessageOperationKeyIsExecuting];
[self didChangeValueForKey:OWSSendMessageOperationKeyIsFinished];
@ -447,6 +428,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
success:(void (^)())successHandler
failure:(RetryableFailureHandler)failureHandler
{
DDLogDebug(@"%@ sending message: %@", self.tag, message.debugDescription);
[self ensureAnyAttachmentsUploaded:message
success:^() {
[self deliverMessage:message
@ -467,6 +450,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
failure:(RetryableFailureHandler)failureHandler
{
if (!message.hasAttachments) {
DDLogDebug(@"%@ No attachments for message: %@", self.tag, message);
return successHandler();
}
@ -556,6 +540,34 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
});
}
- (void)resendMessageFromKeyError:(TSInvalidIdentityKeySendingErrorMessage *)errorMessage
success:(void (^)())successHandler
failure:(void (^)(NSError *error))failureHandler
{
AssertIsOnMainThread();
OWSAssert(errorMessage);
NSString *failedMessageId = errorMessage.messageId;
// Here we remove the existing error message because sending a new message will either
// 1.) succeed and create a new successful message in the thread or...
// 2.) fail and create a new identical error message in the thread.
[errorMessage remove];
// The failedMessageId might be nil for transient, unsaved outgoing messages.
// See [TSOutgoingMessage saveWithTransaction:] for details of which messages
// we do not save.
if (!failedMessageId) {
return;
}
TSOutgoingMessage *message = [TSOutgoingMessage fetchObjectWithUniqueID:failedMessageId];
OWSAssert(message);
return [self sendMessage:message success:successHandler failure:failureHandler];
}
- (NSArray<SignalRecipient *> *)getRecipients:(NSArray<NSString *> *)identifiers error:(NSError **)error
{
NSMutableArray<SignalRecipient *> *recipients = [NSMutableArray new];
@ -778,7 +790,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
// retryable errors.
if ([error isFatal]) {
failureHandler(error);
return;
}
if ([error isRetryable] && !firstRetryableError) {
@ -874,55 +885,20 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
} @catch (NSException *exception) {
deviceMessages = @[];
if ([exception.name isEqualToString:UntrustedIdentityKeyException]) {
// This *can* happen under normal usage, but it should happen relatively rarely.
// We expect it to happen whenever Bob reinstalls, and Alice messages Bob before
// she can pull down his latest identity.
// If it's happening a lot, we should rethink our profile fetching strategy.
OWSAnalyticsInfo(@"Message send failed due to untrusted key.");
NSString *localizedErrorDescriptionFormat
= NSLocalizedString(@"FAILED_SENDING_BECAUSE_UNTRUSTED_IDENTITY_KEY",
@"action sheet header when re-sending message which failed because of untrusted identity keys");
NSString *localizedErrorDescription =
[NSString stringWithFormat:localizedErrorDescriptionFormat,
[self.contactsManager displayNameForPhoneIdentifier:recipient.recipientId]];
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeUntrustedIdentityKey, localizedErrorDescription);
[[TSInvalidIdentityKeySendingErrorMessage
untrustedKeyWithOutgoingMessage:message
inThread:thread
forRecipient:exception.userInfo[TSInvalidRecipientKey]
preKeyBundle:exception.userInfo[TSInvalidPreKeyBundleKey]] save];
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeUntrustedIdentityKey,
NSLocalizedString(@"FAILED_SENDING_BECAUSE_UNTRUSTED_IDENTITY_KEY",
@"action sheet header when re-sending message which failed because of untrusted identity keys"));
// Key will continue to be unaccepted, so no need to retry. It'll only cause us to hit the Pre-Key request
// rate limit
[error setIsRetryable:NO];
// Avoid the "Too many failures with this contact" error rate limiting.
[error setIsFatal:YES];
PreKeyBundle *newKeyBundle = exception.userInfo[TSInvalidPreKeyBundleKey];
if (![newKeyBundle isKindOfClass:[PreKeyBundle class]]) {
OWSFail(@"%@ unexpected TSInvalidPreKeyBundleKey: %@", self.tag, newKeyBundle);
failureHandler(error);
return;
}
NSData *newIdentityKeyWithVersion = newKeyBundle.identityKey;
if (![newIdentityKeyWithVersion isKindOfClass:[NSData class]]) {
OWSFail(@"%@ unexpected TSInvalidRecipientKey: %@", self.tag, newIdentityKeyWithVersion);
failureHandler(error);
return;
}
// TODO migrate to storing the full 33 byte representation of the identity key.
if (newIdentityKeyWithVersion.length != kIdentityKeyLength) {
OWSFail(@"%@ unexpected key length: %lu", self.tag, (unsigned long)newIdentityKeyWithVersion.length);
failureHandler(error);
return;
}
NSData *newIdentityKey = [newIdentityKeyWithVersion removeKeyType];
[[OWSIdentityManager sharedManager] saveRemoteIdentity:newIdentityKey recipientId:recipient.recipientId];
failureHandler(error);
return;
return failureHandler(error);
}
if ([exception.name isEqualToString:OWSMessageSenderRateLimitedException]) {
@ -1161,9 +1137,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
inThread:(TSThread *)thread
{
NSMutableArray *messagesArray = [NSMutableArray arrayWithCapacity:recipient.devices.count];
NSData *plainText = [message buildPlainTextData];
DDLogDebug(@"%@ built message: %@ plainTextData.length: %lu", self.tag, [message class], plainText.length);
for (NSNumber *deviceNumber in recipient.devices) {
@try {
@ -1176,7 +1150,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
messageDict = [self encryptedMessageWithPlaintext:plainText
toRecipient:recipient.uniqueId
deviceId:deviceNumber
keyingStorage:[TSStorageManager sharedManager]];
keyingStorage:[TSStorageManager sharedManager]
legacy:message.isLegacyMessage];
} @catch (NSException *exception) {
encryptionException = exception;
}
@ -1209,6 +1184,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
toRecipient:(NSString *)identifier
deviceId:(NSNumber *)deviceNumber
keyingStorage:(TSStorageManager *)storage
legacy:(BOOL)isLegacymessage
{
if (![storage containsSession:identifier deviceId:[deviceNumber intValue]]) {
__block dispatch_semaphore_t sema = dispatch_semaphore_create(0);
@ -1249,7 +1225,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
SessionBuilder *builder = [[SessionBuilder alloc] initWithSessionStore:storage
preKeyStore:storage
signedPreKeyStore:storage
identityKeyStore:[OWSIdentityManager sharedManager]
identityKeyStore:storage
recipientId:identifier
deviceId:[deviceNumber intValue]];
@try {
@ -1272,7 +1248,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storage
preKeyStore:storage
signedPreKeyStore:storage
identityKeyStore:[OWSIdentityManager sharedManager]
identityKeyStore:storage
recipientId:identifier
deviceId:[deviceNumber intValue]];
@ -1282,11 +1258,21 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
NSData *serializedMessage = encryptedMessage.serialized;
TSWhisperMessageType messageType = [self messageTypeForCipherMessage:encryptedMessage];
OWSMessageServiceParams *messageParams = [[OWSMessageServiceParams alloc] initWithType:messageType
recipientId:identifier
device:[deviceNumber intValue]
content:serializedMessage
registrationId:cipher.remoteRegistrationId];
OWSMessageServiceParams *messageParams;
// DEPRECATED - Remove after all clients have been upgraded.
if (isLegacymessage) {
messageParams = [[OWSLegacyMessageServiceParams alloc] initWithType:messageType
recipientId:identifier
device:[deviceNumber intValue]
body:serializedMessage
registrationId:cipher.remoteRegistrationId];
} else {
messageParams = [[OWSMessageServiceParams alloc] initWithType:messageType
recipientId:identifier
device:[deviceNumber intValue]
content:serializedMessage
registrationId:cipher.remoteRegistrationId];
}
NSError *error;
NSDictionary *jsonDict = [MTLJSONAdapter JSONDictionaryFromModel:messageParams error:&error];
@ -1327,7 +1313,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
}
// Called when the server indicates that the devices no longer exist - e.g. when the remote recipient has reinstalled.
- (void)handleStaleDevicesWithResponse:(NSData *)responseData
recipientId:(NSString *)identifier
completion:(void (^)())completionHandler

View File

@ -1,6 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Created by Michael Kirk on 12/1/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSOutgoingCallMessage.h"
#import "NSDate+millisecondTimeStamp.h"
@ -110,6 +109,10 @@ NS_ASSUME_NONNULL_BEGIN
return NO;
}
- (BOOL)isLegacyMessage
{
return NO;
}
//
///**
// * override thread accessor in superclass, since this model is never saved.

View File

@ -1,19 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSOutgoingMessage.h"
NS_ASSUME_NONNULL_BEGIN
@class OWSVerificationStateSyncMessage;
@class TSContactThread;
@interface OWSOutgoingNullMessage : TSOutgoingMessage
- (instancetype)initWithContactThread:(TSContactThread *)contactThread
verificationStateSyncMessage:(OWSVerificationStateSyncMessage *)verificationStateSyncMessage;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,76 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSOutgoingNullMessage.h"
#import "OWSSignalServiceProtos.pb.h"
#import "Cryptography.h"
#import "OWSVerificationStateSyncMessage.h"
#import "NSDate+millisecondTimeStamp.h"
#import "TSContactThread.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSOutgoingNullMessage ()
@property (nonatomic, readonly) OWSVerificationStateSyncMessage *verificationStateSyncMessage;
@end
@implementation OWSOutgoingNullMessage
- (instancetype)initWithContactThread:(TSContactThread *)contactThread
verificationStateSyncMessage:(OWSVerificationStateSyncMessage *)verificationStateSyncMessage
{
self = [super initWithTimestamp:[NSDate ows_millisecondTimeStamp]
inThread:contactThread];
if (!self) {
return self;
}
_verificationStateSyncMessage = verificationStateSyncMessage;
return self;
}
#pragma mark - override TSOutgoingMessage
- (NSData *)buildPlainTextData
{
OWSSignalServiceProtosContentBuilder *contentBuilder = [OWSSignalServiceProtosContentBuilder new];
OWSSignalServiceProtosNullMessageBuilder *nullMessageBuilder = [OWSSignalServiceProtosNullMessageBuilder new];
NSUInteger contentLength = self.verificationStateSyncMessage.unpaddedVerifiedLength;
OWSAssert(self.verificationStateSyncMessage.paddingBytesLength > 0);
// We add the same amount of padding in the VerificationStateSync message and it's coresponding NullMessage so that
// the sync message is indistinguishable from an outgoing Sent transcript corresponding to the NullMessage. We pad
// the NullMessage so as to obscure it's content. The sync message (like all sync messages) will be *additionally*
// padded by the superclass while being sent. The end result is we send a NullMessage of a non-distinct size, and a
// verification sync which is ~1-512 bytes larger then that.
contentLength += self.verificationStateSyncMessage.paddingBytesLength;
OWSAssert(contentLength > 0)
nullMessageBuilder.padding = [Cryptography generateRandomBytes:contentLength];
contentBuilder.nullMessage = [nullMessageBuilder build];
return [contentBuilder build].data;
}
- (BOOL)shouldSyncTranscript
{
return NO;
}
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
// No-op as we don't want to actually display this as an outgoing message in our thread.
return;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -20,10 +20,10 @@
- (BOOL)shouldAffectUnreadCounts;
/**
* Used for *responding* to a remote read receipt or in response to user activity.
* Call when the user viewed the message/call on this device. "locally" as opposed to being notified via a read receipt
* sync message of a remote read.
*/
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration;
- (void)markAsReadLocally;
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
@end

View File

@ -36,8 +36,6 @@
@class OWSSignalServiceProtosGroupDetailsAvatar;
@class OWSSignalServiceProtosGroupDetailsAvatarBuilder;
@class OWSSignalServiceProtosGroupDetailsBuilder;
@class OWSSignalServiceProtosNullMessage;
@class OWSSignalServiceProtosNullMessageBuilder;
@class OWSSignalServiceProtosSyncMessage;
@class OWSSignalServiceProtosSyncMessageBlocked;
@class OWSSignalServiceProtosSyncMessageBlockedBuilder;
@ -52,8 +50,6 @@
@class OWSSignalServiceProtosSyncMessageRequestBuilder;
@class OWSSignalServiceProtosSyncMessageSent;
@class OWSSignalServiceProtosSyncMessageSentBuilder;
@class OWSSignalServiceProtosVerified;
@class OWSSignalServiceProtosVerifiedBuilder;
@class ObjectiveCFileOptions;
@class ObjectiveCFileOptionsBuilder;
@class PBDescriptorProto;
@ -111,15 +107,6 @@ typedef NS_ENUM(SInt32, OWSSignalServiceProtosEnvelopeType) {
BOOL OWSSignalServiceProtosEnvelopeTypeIsValidValue(OWSSignalServiceProtosEnvelopeType value);
NSString *NSStringFromOWSSignalServiceProtosEnvelopeType(OWSSignalServiceProtosEnvelopeType value);
typedef NS_ENUM(SInt32, OWSSignalServiceProtosVerifiedState) {
OWSSignalServiceProtosVerifiedStateDefault = 0,
OWSSignalServiceProtosVerifiedStateVerified = 1,
OWSSignalServiceProtosVerifiedStateUnverified = 2,
};
BOOL OWSSignalServiceProtosVerifiedStateIsValidValue(OWSSignalServiceProtosVerifiedState value);
NSString *NSStringFromOWSSignalServiceProtosVerifiedState(OWSSignalServiceProtosVerifiedState value);
typedef NS_ENUM(SInt32, OWSSignalServiceProtosDataMessageFlags) {
OWSSignalServiceProtosDataMessageFlagsEndSession = 1,
OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate = 2,
@ -139,7 +126,7 @@ BOOL OWSSignalServiceProtosSyncMessageRequestTypeIsValidValue(OWSSignalServicePr
NSString *NSStringFromOWSSignalServiceProtosSyncMessageRequestType(OWSSignalServiceProtosSyncMessageRequestType value);
typedef NS_ENUM(SInt32, OWSSignalServiceProtosAttachmentPointerFlags) {
OWSSignalServiceProtosAttachmentPointerFlagsVoiceMessage = 1,
OWSSignalServiceProtosAttachmentPointerFlagsVoiceMessage = 1,
};
BOOL OWSSignalServiceProtosAttachmentPointerFlagsIsValidValue(OWSSignalServiceProtosAttachmentPointerFlags value);
@ -276,26 +263,21 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
#define Content_dataMessage @"dataMessage"
#define Content_syncMessage @"syncMessage"
#define Content_callMessage @"callMessage"
#define Content_nullMessage @"nullMessage"
@interface OWSSignalServiceProtosContent : PBGeneratedMessage<GeneratedMessageProtocol> {
@private
BOOL hasDataMessage_:1;
BOOL hasSyncMessage_:1;
BOOL hasCallMessage_:1;
BOOL hasNullMessage_:1;
OWSSignalServiceProtosDataMessage* dataMessage;
OWSSignalServiceProtosSyncMessage* syncMessage;
OWSSignalServiceProtosCallMessage* callMessage;
OWSSignalServiceProtosNullMessage* nullMessage;
}
- (BOOL) hasDataMessage;
- (BOOL) hasSyncMessage;
- (BOOL) hasCallMessage;
- (BOOL) hasNullMessage;
@property (readonly, strong) OWSSignalServiceProtosDataMessage* dataMessage;
@property (readonly, strong) OWSSignalServiceProtosSyncMessage* syncMessage;
@property (readonly, strong) OWSSignalServiceProtosCallMessage* callMessage;
@property (readonly, strong) OWSSignalServiceProtosNullMessage* nullMessage;
+ (instancetype) defaultInstance;
- (instancetype) defaultInstance;
@ -352,143 +334,6 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
- (OWSSignalServiceProtosContentBuilder*) setCallMessageBuilder:(OWSSignalServiceProtosCallMessageBuilder*) builderForValue;
- (OWSSignalServiceProtosContentBuilder*) mergeCallMessage:(OWSSignalServiceProtosCallMessage*) value;
- (OWSSignalServiceProtosContentBuilder*) clearCallMessage;
- (BOOL) hasNullMessage;
- (OWSSignalServiceProtosNullMessage*) nullMessage;
- (OWSSignalServiceProtosContentBuilder*) setNullMessage:(OWSSignalServiceProtosNullMessage*) value;
- (OWSSignalServiceProtosContentBuilder*) setNullMessageBuilder:(OWSSignalServiceProtosNullMessageBuilder*) builderForValue;
- (OWSSignalServiceProtosContentBuilder*) mergeNullMessage:(OWSSignalServiceProtosNullMessage*) value;
- (OWSSignalServiceProtosContentBuilder*) clearNullMessage;
@end
#define NullMessage_padding @"padding"
@interface OWSSignalServiceProtosNullMessage : PBGeneratedMessage<GeneratedMessageProtocol> {
@private
BOOL hasPadding_:1;
NSData* padding;
}
- (BOOL) hasPadding;
@property (readonly, strong) NSData* padding;
+ (instancetype) defaultInstance;
- (instancetype) defaultInstance;
- (BOOL) isInitialized;
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output;
- (OWSSignalServiceProtosNullMessageBuilder*) builder;
+ (OWSSignalServiceProtosNullMessageBuilder*) builder;
+ (OWSSignalServiceProtosNullMessageBuilder*) builderWithPrototype:(OWSSignalServiceProtosNullMessage*) prototype;
- (OWSSignalServiceProtosNullMessageBuilder*) toBuilder;
+ (OWSSignalServiceProtosNullMessage*) parseFromData:(NSData*) data;
+ (OWSSignalServiceProtosNullMessage*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
+ (OWSSignalServiceProtosNullMessage*) parseFromInputStream:(NSInputStream*) input;
+ (OWSSignalServiceProtosNullMessage*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
+ (OWSSignalServiceProtosNullMessage*) parseFromCodedInputStream:(PBCodedInputStream*) input;
+ (OWSSignalServiceProtosNullMessage*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
@end
@interface OWSSignalServiceProtosNullMessageBuilder : PBGeneratedMessageBuilder {
@private
OWSSignalServiceProtosNullMessage* resultNullMessage;
}
- (OWSSignalServiceProtosNullMessage*) defaultInstance;
- (OWSSignalServiceProtosNullMessageBuilder*) clear;
- (OWSSignalServiceProtosNullMessageBuilder*) clone;
- (OWSSignalServiceProtosNullMessage*) build;
- (OWSSignalServiceProtosNullMessage*) buildPartial;
- (OWSSignalServiceProtosNullMessageBuilder*) mergeFrom:(OWSSignalServiceProtosNullMessage*) other;
- (OWSSignalServiceProtosNullMessageBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input;
- (OWSSignalServiceProtosNullMessageBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
- (BOOL) hasPadding;
- (NSData*) padding;
- (OWSSignalServiceProtosNullMessageBuilder*) setPadding:(NSData*) value;
- (OWSSignalServiceProtosNullMessageBuilder*) clearPadding;
@end
#define Verified_destination @"destination"
#define Verified_identityKey @"identityKey"
#define Verified_state @"state"
#define Verified_nullMessage @"nullMessage"
@interface OWSSignalServiceProtosVerified : PBGeneratedMessage<GeneratedMessageProtocol> {
@private
BOOL hasDestination_:1;
BOOL hasIdentityKey_:1;
BOOL hasNullMessage_:1;
BOOL hasState_:1;
NSString* destination;
NSData* identityKey;
NSData* nullMessage;
OWSSignalServiceProtosVerifiedState state;
}
- (BOOL) hasDestination;
- (BOOL) hasIdentityKey;
- (BOOL) hasState;
- (BOOL) hasNullMessage;
@property (readonly, strong) NSString* destination;
@property (readonly, strong) NSData* identityKey;
@property (readonly) OWSSignalServiceProtosVerifiedState state;
@property (readonly, strong) NSData* nullMessage;
+ (instancetype) defaultInstance;
- (instancetype) defaultInstance;
- (BOOL) isInitialized;
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output;
- (OWSSignalServiceProtosVerifiedBuilder*) builder;
+ (OWSSignalServiceProtosVerifiedBuilder*) builder;
+ (OWSSignalServiceProtosVerifiedBuilder*) builderWithPrototype:(OWSSignalServiceProtosVerified*) prototype;
- (OWSSignalServiceProtosVerifiedBuilder*) toBuilder;
+ (OWSSignalServiceProtosVerified*) parseFromData:(NSData*) data;
+ (OWSSignalServiceProtosVerified*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
+ (OWSSignalServiceProtosVerified*) parseFromInputStream:(NSInputStream*) input;
+ (OWSSignalServiceProtosVerified*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
+ (OWSSignalServiceProtosVerified*) parseFromCodedInputStream:(PBCodedInputStream*) input;
+ (OWSSignalServiceProtosVerified*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
@end
@interface OWSSignalServiceProtosVerifiedBuilder : PBGeneratedMessageBuilder {
@private
OWSSignalServiceProtosVerified* resultVerified;
}
- (OWSSignalServiceProtosVerified*) defaultInstance;
- (OWSSignalServiceProtosVerifiedBuilder*) clear;
- (OWSSignalServiceProtosVerifiedBuilder*) clone;
- (OWSSignalServiceProtosVerified*) build;
- (OWSSignalServiceProtosVerified*) buildPartial;
- (OWSSignalServiceProtosVerifiedBuilder*) mergeFrom:(OWSSignalServiceProtosVerified*) other;
- (OWSSignalServiceProtosVerifiedBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input;
- (OWSSignalServiceProtosVerifiedBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
- (BOOL) hasDestination;
- (NSString*) destination;
- (OWSSignalServiceProtosVerifiedBuilder*) setDestination:(NSString*) value;
- (OWSSignalServiceProtosVerifiedBuilder*) clearDestination;
- (BOOL) hasIdentityKey;
- (NSData*) identityKey;
- (OWSSignalServiceProtosVerifiedBuilder*) setIdentityKey:(NSData*) value;
- (OWSSignalServiceProtosVerifiedBuilder*) clearIdentityKey;
- (BOOL) hasState;
- (OWSSignalServiceProtosVerifiedState) state;
- (OWSSignalServiceProtosVerifiedBuilder*) setState:(OWSSignalServiceProtosVerifiedState) value;
- (OWSSignalServiceProtosVerifiedBuilder*) clearState;
- (BOOL) hasNullMessage;
- (NSData*) nullMessage;
- (OWSSignalServiceProtosVerifiedBuilder*) setNullMessage:(NSData*) value;
- (OWSSignalServiceProtosVerifiedBuilder*) clearNullMessage;
@end
#define CallMessage_offer @"offer"
@ -987,8 +832,6 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
#define SyncMessage_request @"request"
#define SyncMessage_read @"read"
#define SyncMessage_blocked @"blocked"
#define SyncMessage_verified @"verified"
#define SyncMessage_padding @"padding"
@interface OWSSignalServiceProtosSyncMessage : PBGeneratedMessage<GeneratedMessageProtocol> {
@private
BOOL hasSent_:1;
@ -996,15 +839,11 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
BOOL hasGroups_:1;
BOOL hasRequest_:1;
BOOL hasBlocked_:1;
BOOL hasVerified_:1;
BOOL hasPadding_:1;
OWSSignalServiceProtosSyncMessageSent* sent;
OWSSignalServiceProtosSyncMessageContacts* contacts;
OWSSignalServiceProtosSyncMessageGroups* groups;
OWSSignalServiceProtosSyncMessageRequest* request;
OWSSignalServiceProtosSyncMessageBlocked* blocked;
OWSSignalServiceProtosVerified* verified;
NSData* padding;
NSMutableArray * readArray;
}
- (BOOL) hasSent;
@ -1012,16 +851,12 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
- (BOOL) hasGroups;
- (BOOL) hasRequest;
- (BOOL) hasBlocked;
- (BOOL) hasVerified;
- (BOOL) hasPadding;
@property (readonly, strong) OWSSignalServiceProtosSyncMessageSent* sent;
@property (readonly, strong) OWSSignalServiceProtosSyncMessageContacts* contacts;
@property (readonly, strong) OWSSignalServiceProtosSyncMessageGroups* groups;
@property (readonly, strong) OWSSignalServiceProtosSyncMessageRequest* request;
@property (readonly, strong) NSArray<OWSSignalServiceProtosSyncMessageRead*> * read;
@property (readonly, strong) OWSSignalServiceProtosSyncMessageBlocked* blocked;
@property (readonly, strong) OWSSignalServiceProtosVerified* verified;
@property (readonly, strong) NSData* padding;
- (OWSSignalServiceProtosSyncMessageRead*)readAtIndex:(NSUInteger)index;
+ (instancetype) defaultInstance;
@ -1455,18 +1290,6 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
- (OWSSignalServiceProtosSyncMessageBuilder*) setBlockedBuilder:(OWSSignalServiceProtosSyncMessageBlockedBuilder*) builderForValue;
- (OWSSignalServiceProtosSyncMessageBuilder*) mergeBlocked:(OWSSignalServiceProtosSyncMessageBlocked*) value;
- (OWSSignalServiceProtosSyncMessageBuilder*) clearBlocked;
- (BOOL) hasVerified;
- (OWSSignalServiceProtosVerified*) verified;
- (OWSSignalServiceProtosSyncMessageBuilder*) setVerified:(OWSSignalServiceProtosVerified*) value;
- (OWSSignalServiceProtosSyncMessageBuilder*) setVerifiedBuilder:(OWSSignalServiceProtosVerifiedBuilder*) builderForValue;
- (OWSSignalServiceProtosSyncMessageBuilder*) mergeVerified:(OWSSignalServiceProtosVerified*) value;
- (OWSSignalServiceProtosSyncMessageBuilder*) clearVerified;
- (BOOL) hasPadding;
- (NSData*) padding;
- (OWSSignalServiceProtosSyncMessageBuilder*) setPadding:(NSData*) value;
- (OWSSignalServiceProtosSyncMessageBuilder*) clearPadding;
@end
#define AttachmentPointer_id @"id"
@ -1486,7 +1309,7 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
BOOL hasThumbnail_:1;
BOOL hasDigest_:1;
BOOL hasSize_:1;
BOOL hasFlags_:1;
BOOL hasFlags_ : 1;
UInt64 id;
NSString* contentType;
NSString* fileName;
@ -1503,7 +1326,7 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
- (BOOL) hasThumbnail;
- (BOOL) hasDigest;
- (BOOL) hasFileName;
- (BOOL) hasFlags;
- (BOOL)hasFlags;
@property (readonly) UInt64 id;
@property (readonly, strong) NSString* contentType;
@property (readonly, strong) NSData* key;
@ -1583,10 +1406,10 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
- (OWSSignalServiceProtosAttachmentPointerBuilder*) setFileName:(NSString*) value;
- (OWSSignalServiceProtosAttachmentPointerBuilder*) clearFileName;
- (BOOL) hasFlags;
- (UInt32) flags;
- (OWSSignalServiceProtosAttachmentPointerBuilder*) setFlags:(UInt32) value;
- (OWSSignalServiceProtosAttachmentPointerBuilder*) clearFlags;
- (BOOL)hasFlags;
- (UInt32)flags;
- (OWSSignalServiceProtosAttachmentPointerBuilder *)setFlags:(UInt32)value;
- (OWSSignalServiceProtosAttachmentPointerBuilder *)clearFlags;
@end
#define GroupContext_id @"id"
@ -1685,30 +1508,25 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
#define ContactDetails_name @"name"
#define ContactDetails_avatar @"avatar"
#define ContactDetails_color @"color"
#define ContactDetails_verified @"verified"
@interface OWSSignalServiceProtosContactDetails : PBGeneratedMessage<GeneratedMessageProtocol> {
@private
BOOL hasNumber_:1;
BOOL hasName_:1;
BOOL hasColor_:1;
BOOL hasAvatar_:1;
BOOL hasVerified_:1;
NSString* number;
NSString* name;
NSString* color;
OWSSignalServiceProtosContactDetailsAvatar* avatar;
OWSSignalServiceProtosVerified* verified;
}
- (BOOL) hasNumber;
- (BOOL) hasName;
- (BOOL) hasAvatar;
- (BOOL) hasColor;
- (BOOL) hasVerified;
@property (readonly, strong) NSString* number;
@property (readonly, strong) NSString* name;
@property (readonly, strong) OWSSignalServiceProtosContactDetailsAvatar* avatar;
@property (readonly, strong) NSString* color;
@property (readonly, strong) OWSSignalServiceProtosVerified* verified;
+ (instancetype) defaultInstance;
- (instancetype) defaultInstance;
@ -1826,13 +1644,6 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
- (NSString*) color;
- (OWSSignalServiceProtosContactDetailsBuilder*) setColor:(NSString*) value;
- (OWSSignalServiceProtosContactDetailsBuilder*) clearColor;
- (BOOL) hasVerified;
- (OWSSignalServiceProtosVerified*) verified;
- (OWSSignalServiceProtosContactDetailsBuilder*) setVerified:(OWSSignalServiceProtosVerified*) value;
- (OWSSignalServiceProtosContactDetailsBuilder*) setVerifiedBuilder:(OWSSignalServiceProtosVerifiedBuilder*) builderForValue;
- (OWSSignalServiceProtosContactDetailsBuilder*) mergeVerified:(OWSSignalServiceProtosVerified*) value;
- (OWSSignalServiceProtosContactDetailsBuilder*) clearVerified;
@end
#define GroupDetails_id @"id"

File diff suppressed because it is too large Load Diff

View File

@ -16,8 +16,6 @@ typedef enum {
// These call types are used until the call connects.
RPRecentCallTypeOutgoingIncomplete,
RPRecentCallTypeIncomingIncomplete,
RPRecentCallTypeMissedBecauseOfChangedIdentity,
RPRecentCallTypeIncomingDeclined
} RPRecentCallType;
@interface TSCall : TSInteraction <OWSReadTracking>

View File

@ -36,7 +36,7 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
_callSchemaVersion = TSCallCurrentSchemaVersion;
_callType = callType;
if (_callType == RPRecentCallTypeMissed || _callType == RPRecentCallTypeMissedBecauseOfChangedIdentity) {
if (_callType == RPRecentCallTypeMissed) {
_read = NO;
} else {
_read = YES;
@ -74,11 +74,6 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
return NSLocalizedString(@"OUTGOING_INCOMPLETE_CALL", @"");
case RPRecentCallTypeIncomingIncomplete:
return NSLocalizedString(@"INCOMING_INCOMPLETE_CALL", @"");
case RPRecentCallTypeMissedBecauseOfChangedIdentity:
return NSLocalizedString(@"INFO_MESSAGE_MISSED_CALL_DUE_TO_CHANGED_IDENITY", @"info message text shown in conversation view");
case RPRecentCallTypeIncomingDeclined:
return NSLocalizedString(@"INCOMING_DECLINED_CALL",
@"info message recorded in conversation history when local user declined a call");
}
}
@ -89,22 +84,22 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
return YES;
}
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
DDLogInfo(@"%@ marking as read uniqueId: %@ which has timestamp: %llu", self.tag, self.uniqueId, self.timestamp);
if (_read) {
return;
}
DDLogDebug(@"%@ marking as read uniqueId: %@ which has timestamp: %llu", self.tag, self.uniqueId, self.timestamp);
_read = YES;
[self saveWithTransaction:transaction];
[self touchThreadWithTransaction:transaction];
// Ignore sendReadReceipt and updateExpiration; they don't apply to calls.
// redraw any thread-related unread count UI.
[self touchThreadWithTransaction:transaction];
}
- (void)markAsReadLocally
{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self markAsReadLocallyWithTransaction:transaction];
}];
}
#pragma mark - Methods
@ -119,7 +114,7 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
_callType = callType;
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self saveWithTransaction:transaction];
// redraw any thread-related unread count UI.

View File

@ -50,7 +50,6 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) OWSMessageSender *messageSender;
@property (nonatomic, readonly) OWSIncomingMessageFinder *incomingMessageFinder;
@property (nonatomic, readonly) OWSBlockingManager *blockingManager;
@property (nonatomic, readonly) OWSIdentityManager *identityManager;
@end
@ -74,16 +73,13 @@ NS_ASSUME_NONNULL_BEGIN
id<ContactsManagerProtocol> contactsManager = [TextSecureKitEnv sharedEnv].contactsManager;
id<OWSCallMessageHandler> callMessageHandler = [TextSecureKitEnv sharedEnv].callMessageHandler;
ContactsUpdater *contactsUpdater = [ContactsUpdater sharedUpdater];
OWSIdentityManager *identityManager = [OWSIdentityManager sharedManager];
OWSMessageSender *messageSender = [TextSecureKitEnv sharedEnv].messageSender;
return [self initWithNetworkManager:networkManager
storageManager:storageManager
callMessageHandler:callMessageHandler
contactsManager:contactsManager
contactsUpdater:contactsUpdater
identityManager:identityManager
messageSender:messageSender];
}
@ -92,7 +88,6 @@ NS_ASSUME_NONNULL_BEGIN
callMessageHandler:(id<OWSCallMessageHandler>)callMessageHandler
contactsManager:(id<ContactsManagerProtocol>)contactsManager
contactsUpdater:(ContactsUpdater *)contactsUpdater
identityManager:(OWSIdentityManager *)identityManager
messageSender:(OWSMessageSender *)messageSender
{
self = [super init];
@ -106,7 +101,6 @@ NS_ASSUME_NONNULL_BEGIN
_callMessageHandler = callMessageHandler;
_contactsManager = contactsManager;
_contactsUpdater = contactsUpdater;
_identityManager = identityManager;
_messageSender = messageSender;
_dbConnection = storageManager.newDatabaseConnection;
@ -115,24 +109,9 @@ NS_ASSUME_NONNULL_BEGIN
OWSSingletonAssert();
[self startObserving];
return self;
}
- (void)startObserving
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(yapDatabaseModified:)
name:YapDatabaseModifiedNotification
object:nil];
}
- (void)yapDatabaseModified:(NSNotification *)notification
{
[self updateApplicationBadgeCount];
}
#pragma mark - Debugging
- (NSString *)descriptionForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
@ -186,8 +165,6 @@ NS_ASSUME_NONNULL_BEGIN
return [NSString stringWithFormat:@"<DataMessage: %@ />", [self descriptionForDataMessage:content.dataMessage]];
} else if (content.hasCallMessage) {
return [NSString stringWithFormat:@"<CallMessage: %@ />", content.callMessage];
} else if (content.hasNullMessage) {
return [NSString stringWithFormat:@"<NullMessage: %@ />", content.nullMessage];
} else {
OWSAssert(NO);
return @"UnknownContent";
@ -225,7 +202,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (NSString *)descriptionForSyncMessage:(OWSSignalServiceProtosSyncMessage *)syncMessage
{
NSMutableString *description = [NSMutableString new];
NSMutableString *description = [[NSMutableString alloc] initWithString:@"CallMessage: "];
if (syncMessage.hasSent) {
[description appendString:@"SentTranscript"];
} else if (syncMessage.hasRequest) {
@ -242,10 +219,6 @@ NS_ASSUME_NONNULL_BEGIN
[description appendString:@"Blocked"];
} else if (syncMessage.read.count > 0) {
[description appendString:@"ReadReceipt"];
} else if (syncMessage.hasVerified) {
NSString *verifiedString =
[NSString stringWithFormat:@"Verification for: %@", syncMessage.verified.destination];
[description appendString:verifiedString];
} else {
// Shouldn't happen
OWSAssert(NO);
@ -290,11 +263,7 @@ NS_ASSUME_NONNULL_BEGIN
DDLogDebug(@"%@ handled secure message.", self.tag);
if (error) {
DDLogError(
@"%@ handling secure message from address: %@.%d failed with error: %@",
self.tag,
envelope.source,
(unsigned int)envelope.sourceDevice,
error);
@"%@ handling secure message failed with error: %@", self.tag, error);
}
completion();
}];
@ -304,14 +273,10 @@ NS_ASSUME_NONNULL_BEGIN
case OWSSignalServiceProtosEnvelopeTypePrekeyBundle: {
[self handlePreKeyBundleAsync:envelope
completion:^(NSError *_Nullable error) {
DDLogDebug(@"%@ handled pre-key whisper message", self.tag);
DDLogDebug(@"%@ handled pre-key bundle", self.tag);
if (error) {
DDLogError(@"%@ handling pre-key whisper message from address: %@.%d failed "
@"with error: %@",
self.tag,
envelope.source,
(unsigned int)envelope.sourceDevice,
error);
DDLogError(
@"%@ handling pre-key bundle failed with error: %@", self.tag, error);
}
completion();
}];
@ -363,6 +328,18 @@ NS_ASSUME_NONNULL_BEGIN
NSString *recipientId = messageEnvelope.source;
int deviceId = messageEnvelope.sourceDevice;
dispatch_async([OWSDispatch sessionStoreQueue], ^{
if (![storageManager containsSession:recipientId deviceId:deviceId]) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSErrorMessage *errorMessage =
[TSErrorMessage missingSessionWithEnvelope:messageEnvelope withTransaction:transaction];
[errorMessage saveWithTransaction:transaction];
[self notififyForErrorMessage:errorMessage withEnvelope:messageEnvelope];
}];
DDLogError(@"Skipping message envelope for unknown session.");
completion(nil);
return;
}
// DEPRECATED - Remove after all clients have been upgraded.
NSData *encryptedData
= messageEnvelope.hasContent ? messageEnvelope.content : messageEnvelope.legacyMessage;
@ -387,7 +364,7 @@ NS_ASSUME_NONNULL_BEGIN
SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storageManager
preKeyStore:storageManager
signedPreKeyStore:storageManager
identityKeyStore:self.identityManager
identityKeyStore:storageManager
recipientId:recipientId
deviceId:deviceId];
@ -439,7 +416,7 @@ NS_ASSUME_NONNULL_BEGIN
SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storageManager
preKeyStore:storageManager
signedPreKeyStore:storageManager
identityKeyStore:self.identityManager
identityKeyStore:storageManager
recipientId:recipientId
deviceId:deviceId];
@ -473,7 +450,7 @@ NS_ASSUME_NONNULL_BEGIN
sourceId:envelope.source
sourceDeviceId:envelope.sourceDevice];
if (duplicateEnvelope) {
DDLogInfo(@"%@ Ignoring previously received envelope from %@.%d with timestamp: %llu", self.tag, envelope.source, (unsigned int)envelope.sourceDevice, envelope.timestamp);
DDLogInfo(@"%@ Ignoring previously received envelope with timestamp: %llu", self.tag, envelope.timestamp);
return;
}
@ -486,8 +463,6 @@ NS_ASSUME_NONNULL_BEGIN
[self handleIncomingEnvelope:envelope withDataMessage:content.dataMessage];
} else if (content.hasCallMessage) {
[self handleIncomingEnvelope:envelope withCallMessage:content.callMessage];
} else if (content.hasNullMessage) {
DDLogInfo(@"%@ Received null message.", self.tag);
} else {
DDLogWarn(@"%@ Ignoring envelope. Content with no known payload", self.tag);
}
@ -525,7 +500,7 @@ NS_ASSUME_NONNULL_BEGIN
NSString *recipientId = incomingEnvelope.source;
__block TSThread *thread;
[[TSStorageManager sharedManager].dbReadWriteConnection
[[TSStorageManager sharedManager].dbConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
thread = [TSContactThread getOrCreateThreadWithContactId:recipientId transaction:transaction];
}];
@ -634,8 +609,6 @@ NS_ASSUME_NONNULL_BEGIN
return;
}
DDLogDebug(@"%@ incoming attachment message: %@", self.tag, createdMessage.debugDescription);
[attachmentsProcessor fetchAttachmentsForMessage:createdMessage
success:^(TSAttachmentStream *attachmentStream) {
DDLogDebug(
@ -674,8 +647,7 @@ NS_ASSUME_NONNULL_BEGIN
} else if (syncMessage.hasRequest) {
if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeContacts) {
OWSSyncContactsMessage *syncContactsMessage =
[[OWSSyncContactsMessage alloc] initWithContactsManager:self.contactsManager
identityManager:self.identityManager];
[[OWSSyncContactsMessage alloc] initWithContactsManager:self.contactsManager];
[self.messageSender sendTemporaryAttachmentData:[syncContactsMessage buildPlainTextAttachmentData]
contentType:OWSMimeTypeApplicationOctetStream
@ -686,6 +658,7 @@ NS_ASSUME_NONNULL_BEGIN
failure:^(NSError *error) {
DDLogError(@"%@ Failed to send Contacts response syncMessage with error: %@", self.tag, error);
}];
} else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeGroups) {
OWSSyncGroupsMessage *syncGroupsMessage = [[OWSSyncGroupsMessage alloc] init];
@ -711,9 +684,6 @@ NS_ASSUME_NONNULL_BEGIN
[[OWSReadReceiptsProcessor alloc] initWithReadReceiptProtos:syncMessage.read
storageManager:self.storageManager];
[readReceiptsProcessor process];
} else if (syncMessage.hasVerified) {
DDLogInfo(@"%@ Received verification state for %@", self.tag, syncMessage.verified.destination);
[self.identityManager processIncomingSyncMessage:syncMessage.verified];
} else {
DDLogWarn(@"%@ Ignoring unsupported sync message.", self.tag);
}
@ -920,7 +890,7 @@ NS_ASSUME_NONNULL_BEGIN
messageBody:body
attachmentIds:attachmentIds
expiresInSeconds:dataMessage.expireTimer];
DDLogDebug(@"%@ incoming group text message: %@", self.tag, incomingMessage.debugDescription);
[incomingMessage saveWithTransaction:transaction];
break;
}
@ -942,22 +912,19 @@ NS_ASSUME_NONNULL_BEGIN
messageBody:body
attachmentIds:attachmentIds
expiresInSeconds:dataMessage.expireTimer];
DDLogDebug(@"%@ incoming 1:1 text message: %@", self.tag, incomingMessage.debugDescription);
[incomingMessage saveWithTransaction:transaction];
thread = cThread;
}
if (thread && incomingMessage) {
[incomingMessage saveWithTransaction:transaction];
// Any messages sent from the current user - from this device or another - should be
// automatically marked as read.
BOOL shouldMarkMessageAsRead = [envelope.source isEqualToString:localNumber];
if (shouldMarkMessageAsRead) {
// Don't send a read receipt for messages sent by ourselves.
[incomingMessage markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:YES];
[incomingMessage markAsReadLocallyWithTransaction:transaction];
}
DDLogDebug(@"%@ shouldMarkMessageAsRead: %d (%@)", self.tag, shouldMarkMessageAsRead, envelope.source);
// Other clients allow attachments to be sent along with body, we want the text displayed as a separate
// message
if ([attachmentIds count] > 0 && body != nil && ![body isEqualToString:@""]) {
@ -970,7 +937,6 @@ NS_ASSUME_NONNULL_BEGIN
messageBody:body
attachmentIds:@[]
expiresInSeconds:dataMessage.expireTimer];
DDLogDebug(@"%@ incoming extra text message: %@", self.tag, incomingMessage.debugDescription);
[textMessage saveWithTransaction:transaction];
}
}
@ -1020,12 +986,8 @@ NS_ASSUME_NONNULL_BEGIN
} else if ([exception.name isEqualToString:InvalidVersionException]) {
errorMessage = [TSErrorMessage invalidVersionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:UntrustedIdentityKeyException]) {
// Should no longer get here, since we now record the new identity for incoming messages.
OWSFail(@"%@ Failed to trust identity on incoming message from: %@.%d",
self.tag,
envelope.source,
envelope.sourceDevice);
return;
errorMessage =
[TSInvalidIdentityKeyReceivingErrorMessage untrustedKeyWithEnvelope:envelope withTransaction:transaction];
} else {
errorMessage = [TSErrorMessage corruptedMessageWithEnvelope:envelope withTransaction:transaction];
}
@ -1083,12 +1045,6 @@ NS_ASSUME_NONNULL_BEGIN
return numberOfItems;
}
- (void)updateApplicationBadgeCount
{
NSUInteger numberOfItems = [self unreadMessagesCount];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:numberOfItems];
}
- (NSUInteger)unreadMessagesInThread:(TSThread *)thread {
__block NSUInteger numberOfItems;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {

View File

@ -30,7 +30,7 @@ extern NSString *const TSNetworkManagerDomain;
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)sharedManager;
+ (id)sharedManager;
- (void)makeRequest:(TSRequest *)request
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success

View File

@ -22,8 +22,7 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
#pragma mark Singleton implementation
+ (instancetype)sharedManager
{
+ (id)sharedManager {
static TSNetworkManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{

View File

@ -160,6 +160,7 @@ NSString *const kNSNotificationName_IsCensorshipCircumventionActiveDidChange =
DDLogInfo(@"%@ using reflector HTTPSessionManager", self.tag);
return self.reflectorHTTPSessionManager;
} else {
DDLogDebug(@"%@ using default HTTPSessionManager", self.tag);
return self.defaultHTTPSessionManager;
}
}

View File

@ -156,6 +156,7 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
if (self.websocket) {
switch ([self.websocket readyState]) {
case SR_OPEN:
DDLogVerbose(@"WebSocket already open on connection request");
self.state = SocketManagerStateOpen;
return;
case SR_CONNECTING:
@ -233,7 +234,7 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
return;
}
DDLogWarn(@"%@ Socket state: %@ -> %@",
DDLogWarn(@"%@ Socket state change: %@ -> %@",
self.tag,
[self stringFromSocketManagerState:_state],
[self stringFromSocketManagerState:state]);

View File

@ -0,0 +1,13 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@protocol TSPreferences <NSObject>
- (BOOL)isSendingIdentityApprovalRequired;
@end
NS_ASSUME_NONNULL_END

View File

@ -5,7 +5,7 @@
#import "OWSFingerprintBuilder.h"
#import "ContactsManagerProtocol.h"
#import "OWSFingerprint.h"
#import "OWSIdentityManager.h"
#import "TSStorageManager+IdentityKeyStore.h"
#import "TSStorageManager+keyingMaterial.h"
#import <25519/Curve25519.h>
@ -36,7 +36,7 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId
{
NSData *_Nullable theirIdentityKey = [[OWSIdentityManager sharedManager] identityKeyForRecipientId:theirSignalId];
NSData *_Nullable theirIdentityKey = [self.storageManager identityKeyForRecipientId:theirSignalId];
if (theirIdentityKey == nil) {
OWSAssert(NO);
@ -51,7 +51,7 @@ NS_ASSUME_NONNULL_BEGIN
NSString *theirName = [self.contactsManager displayNameForPhoneIdentifier:theirSignalId];
NSString *mySignalId = [self.storageManager localNumber];
NSData *myIdentityKey = [[OWSIdentityManager sharedManager] identityKeyPair].publicKey;
NSData *myIdentityKey = [self.storageManager identityKeyPair].publicKey;
return [OWSFingerprint fingerprintWithMyStableId:mySignalId
myIdentityKey:myIdentityKey

View File

@ -2,20 +2,10 @@
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSSignalServiceProtos.pb.h"
#import "TSYapDatabaseObject.h"
#import <SignalServiceKit/TSYapDatabaseObject.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, OWSVerificationState) {
OWSVerificationStateDefault,
OWSVerificationStateVerified,
OWSVerificationStateNoLongerVerified,
};
NSString *OWSVerificationStateToString(OWSVerificationState verificationState);
OWSSignalServiceProtosVerifiedState OWSVerificationStateToProtoState(OWSVerificationState verificationState);
@interface OWSRecipientIdentity : TSYapDatabaseObject
@property (nonatomic, readonly) NSString *recipientId;
@ -23,23 +13,28 @@ OWSSignalServiceProtosVerifiedState OWSVerificationStateToProtoState(OWSVerifica
@property (nonatomic, readonly) NSDate *createdAt;
@property (nonatomic, readonly) BOOL isFirstKnownKey;
#pragma mark - Verification State
#pragma mark - get/set Seen
@property (atomic, readonly) OWSVerificationState verificationState;
@property (atomic, readonly) BOOL wasSeen;
- (void)updateAsSeen;
- (void)updateWithVerificationState:(OWSVerificationState)verificationState;
#pragma mark - get/set Approval
@property (atomic, readonly) BOOL approvedForBlockingUse;
@property (atomic, readonly) BOOL approvedForNonBlockingUse;
- (void)updateWithApprovedForBlockingUse:(BOOL)approvedForBlockingUse
approvedForNonBlockingUse:(BOOL)approvedForNonBlockingUse;
#pragma mark - Initializers
- (instancetype)initWithUniqueId:(NSString *)uniqueId NS_UNAVAILABLE;
- (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithRecipientId:(NSString *)recipientId
identityKey:(NSData *)identityKey
isFirstKnownKey:(BOOL)isFirstKnownKey
createdAt:(NSDate *)createdAt
verificationState:(OWSVerificationState)verificationState NS_DESIGNATED_INITIALIZER;
approvedForBlockingUse:(BOOL)approvedForBlockingUse
approvedForNonBlockingUse:(BOOL)approvedForNonBlockingUse NS_DESIGNATED_INITIALIZER;
#pragma mark - debug

View File

@ -8,33 +8,11 @@
NS_ASSUME_NONNULL_BEGIN
NSString *OWSVerificationStateToString(OWSVerificationState verificationState)
{
switch (verificationState) {
case OWSVerificationStateDefault:
return @"OWSVerificationStateDefault";
case OWSVerificationStateVerified:
return @"OWSVerificationStateVerified";
case OWSVerificationStateNoLongerVerified:
return @"OWSVerificationStateNoLongerVerified";
}
}
OWSSignalServiceProtosVerifiedState OWSVerificationStateToProtoState(OWSVerificationState verificationState)
{
switch (verificationState) {
case OWSVerificationStateDefault:
return OWSSignalServiceProtosVerifiedStateDefault;
case OWSVerificationStateVerified:
return OWSSignalServiceProtosVerifiedStateVerified;
case OWSVerificationStateNoLongerVerified:
return OWSSignalServiceProtosVerifiedStateUnverified;
}
}
@interface OWSRecipientIdentity ()
@property (atomic) OWSVerificationState verificationState;
@property (atomic) BOOL wasSeen;
@property (atomic) BOOL approvedForBlockingUse;
@property (atomic) BOOL approvedForNonBlockingUse;
@end
@ -46,24 +24,12 @@ OWSSignalServiceProtosVerifiedState OWSVerificationStateToProtoState(OWSVerifica
*/
@implementation OWSRecipientIdentity
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
if (![coder decodeObjectForKey:@"verificationState"]) {
_verificationState = OWSVerificationStateDefault;
}
}
return self;
}
- (instancetype)initWithRecipientId:(NSString *)recipientId
identityKey:(NSData *)identityKey
isFirstKnownKey:(BOOL)isFirstKnownKey
createdAt:(NSDate *)createdAt
verificationState:(OWSVerificationState)verificationState
approvedForBlockingUse:(BOOL)approvedForBlockingUse
approvedForNonBlockingUse:(BOOL)approvedForNonBlockingUse
{
self = [super initWithUniqueId:recipientId];
if (!self) {
@ -74,16 +40,27 @@ OWSSignalServiceProtosVerifiedState OWSVerificationStateToProtoState(OWSVerifica
_identityKey = identityKey;
_isFirstKnownKey = isFirstKnownKey;
_createdAt = createdAt;
_verificationState = verificationState;
_approvedForBlockingUse = approvedForBlockingUse;
_approvedForNonBlockingUse = approvedForNonBlockingUse;
_wasSeen = NO;
return self;
}
- (void)updateWithVerificationState:(OWSVerificationState)verificationState
- (void)updateAsSeen
{
[self updateWithChangeBlock:^(OWSRecipientIdentity *_Nonnull obj) {
obj.wasSeen = YES;
}];
}
- (void)updateWithApprovedForBlockingUse:(BOOL)approvedForBlockingUse
approvedForNonBlockingUse:(BOOL)approvedForNonBlockingUse
{
// Ensure changes are persisted without clobbering any work done on another thread or instance.
[self updateWithChangeBlock:^(OWSRecipientIdentity *_Nonnull obj) {
obj.verificationState = verificationState;
obj.approvedForBlockingUse = approvedForBlockingUse;
obj.approvedForNonBlockingUse = approvedForNonBlockingUse;
}];
}
@ -91,7 +68,7 @@ OWSSignalServiceProtosVerifiedState OWSVerificationStateToProtoState(OWSVerifica
{
changeBlock(self);
[[self class].dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[[self class].dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
OWSRecipientIdentity *latest = [[self class] fetchObjectWithUniqueID:self.uniqueId transaction:transaction];
if (latest == nil) {
[self saveWithTransaction:transaction];

View File

@ -0,0 +1,58 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import <AxolotlKit/IdentityKeyStore.h>
#import "TSStorageManager.h"
NS_ASSUME_NONNULL_BEGIN
@class OWSRecipientIdentity;
extern NSString *const TSStorageManagerTrustedKeysCollection;
@interface TSStorageManager (IdentityKeyStore) <IdentityKeyStore>
/**
* Explicitly mark an identity as approved for blocking/nonblocking use
* e.g. in response to a user confirmation action.
*
* @param identityKey key data used to identify the recipient
* @param recipientId unique stable identifier for the recipient, e.g. e164 phone number
* @param approvedForBlockingUse if the user wants explicit confirmation before sending to changed numbers, whether
* that confirmation has occurred.
* @param approvedForNonBlockingUse YES to override the duration during which we consider an SN "too soon" to send.
*
* @returns YES if we are replacing an existing known identity key for recipientId.
* NO if there was no previously stored identity key for the recipient.
*/
- (BOOL)saveRemoteIdentity:(NSData *)identityKey
recipientId:(NSString *)recipientId
approvedForBlockingUse:(BOOL)approvedForBlockingUse
approvedForNonBlockingUse:(BOOL)approvedForNonBlockingUse;
/**
* Check if a recipient identity corresponds to an untrusted identity
*
* @param recipientId unique stable identifier for the recipient, e.g. e164 phone number
* @returns nil if the identity doesn't exist or if it's trusted
* else returns the untrusted identity
*/
- (nullable OWSRecipientIdentity *)unconfirmedIdentityThatShouldBlockSendingForRecipientId:(NSString *)recipientId;
/**
* @param recipientId unique stable identifier for the recipient, e.g. e164 phone number
* @returns nil if the recipient's current id has been seen, or if it's the users first key
* else returns the unseen identity
*
*/
- (nullable OWSRecipientIdentity *)unseenIdentityChangeForRecipientId:(NSString *)recipientId;
- (NSArray<OWSRecipientIdentity *> *)unseenIdentityChangesForRecipientIds:(NSArray<NSString *> *)recipientIds;
- (void)generateNewIdentityKey;
- (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId;
- (void)removeIdentityKeyForRecipient:(NSString *)receipientId;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,325 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "NSDate+millisecondTimeStamp.h"
#import "NotificationsProtocol.h"
#import "OWSRecipientIdentity.h"
#import "TSAccountManager.h"
#import "TSContactThread.h"
#import "TSErrorMessage.h"
#import "TSGroupThread.h"
#import "TSPreferences.h"
#import "TSStorageManager+IdentityKeyStore.h"
#import "TSStorageManager+SessionStore.h"
#import "TextSecureKitEnv.h"
#import <25519/Curve25519.h>
NS_ASSUME_NONNULL_BEGIN
// Storing our own identity key
NSString *const TSStorageManagerIdentityKeyStoreIdentityKey = @"TSStorageManagerIdentityKeyStoreIdentityKey";
NSString *const TSStorageManagerIdentityKeyStoreCollection = @"TSStorageManagerIdentityKeyStoreCollection";
// Storing recipients identity keys
NSString *const TSStorageManagerTrustedKeysCollection = @"TSStorageManagerTrustedKeysCollection";
// Don't trust an identity for sending to unless they've been around for at least this long
const NSTimeInterval kIdentityKeyStoreNonBlockingSecondsThreshold = 5.0;
@implementation TSStorageManager (IdentityKeyStore)
+ (id)sharedIdentityKeyLock
{
static id identityKeyLock;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
identityKeyLock = [NSObject new];
});
return identityKeyLock;
}
- (void)generateNewIdentityKey {
[self setObject:[Curve25519 generateKeyPair]
forKey:TSStorageManagerIdentityKeyStoreIdentityKey
inCollection:TSStorageManagerIdentityKeyStoreCollection];
}
- (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId
{
@synchronized([[self class] sharedIdentityKeyLock])
{
return [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId].identityKey;
}
}
- (nullable ECKeyPair *)identityKeyPair
{
return [self keyPairForKey:TSStorageManagerIdentityKeyStoreIdentityKey
inCollection:TSStorageManagerIdentityKeyStoreCollection];
}
- (int)localRegistrationId {
return (int)[TSAccountManager getOrGenerateRegistrationId];
}
- (BOOL)saveRemoteIdentity:(NSData *)identityKey recipientId:(NSString *)recipientId
{
OWSAssert(identityKey != nil);
OWSAssert(recipientId != nil);
@synchronized([[self class] sharedIdentityKeyLock])
{
// Deprecated. We actually no longer use the TSStorageManagerTrustedKeysCollection for trust
// decisions, but it's desirable to try to keep it up to date with our trusted identitys
// while we're switching between versions, e.g. so we don't get into a state where we have a
// session for an identity not in our key store.
[self setObject:identityKey forKey:recipientId inCollection:TSStorageManagerTrustedKeysCollection];
// If send-blocking is disabled at the time the identity was saved, we want to consider the identity as
// approved for blocking. Otherwise the user will see inexplicable failures when trying to send to this
// identity, if they later enabled send-blocking.
BOOL approvedForBlockingUse = ![TextSecureKitEnv sharedEnv].preferences.isSendingIdentityApprovalRequired;
return [self saveRemoteIdentity:identityKey
recipientId:recipientId
approvedForBlockingUse:approvedForBlockingUse
approvedForNonBlockingUse:NO];
}
}
- (BOOL)saveRemoteIdentity:(NSData *)identityKey
recipientId:(NSString *)recipientId
approvedForBlockingUse:(BOOL)approvedForBlockingUse
approvedForNonBlockingUse:(BOOL)approvedForNonBlockingUse
{
OWSAssert(identityKey != nil);
OWSAssert(recipientId != nil);
NSString const *logTag = @"[IdentityKeyStore]";
@synchronized ([[self class] sharedIdentityKeyLock]) {
OWSRecipientIdentity *existingIdentity = [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
if (existingIdentity == nil) {
DDLogInfo(@"%@ saving first use identity for recipient: %@", logTag, recipientId);
[[[OWSRecipientIdentity alloc] initWithRecipientId:recipientId
identityKey:identityKey
isFirstKnownKey:YES
createdAt:[NSDate new]
approvedForBlockingUse:approvedForBlockingUse
approvedForNonBlockingUse:approvedForNonBlockingUse] save];
return NO;
}
if (![existingIdentity.identityKey isEqual:identityKey]) {
DDLogInfo(@"%@ replacing identity for existing recipient: %@", logTag, recipientId);
[self createIdentityChangeInfoMessageForRecipientId:recipientId];
[[[OWSRecipientIdentity alloc] initWithRecipientId:recipientId
identityKey:identityKey
isFirstKnownKey:NO
createdAt:[NSDate new]
approvedForBlockingUse:approvedForBlockingUse
approvedForNonBlockingUse:approvedForNonBlockingUse] save];
return YES;
}
if ([self isBlockingApprovalRequiredForIdentity:existingIdentity] || [self isNonBlockingApprovalRequiredForIdentity:existingIdentity]) {
[existingIdentity updateWithApprovedForBlockingUse:approvedForBlockingUse
approvedForNonBlockingUse:approvedForNonBlockingUse];
return NO;
}
DDLogDebug(@"%@ no changes for identity saved for recipient: %@", logTag, recipientId);
return NO;
}
}
- (BOOL)isTrustedIdentityKey:(NSData *)identityKey
recipientId:(NSString *)recipientId
direction:(TSMessageDirection)direction
{
OWSAssert(identityKey != nil);
OWSAssert(recipientId != nil);
OWSAssert(direction != TSMessageDirectionUnknown);
@synchronized([[self class] sharedIdentityKeyLock])
{
if ([[[self class] localNumber] isEqualToString:recipientId]) {
if ([[self identityKeyPair].publicKey isEqualToData:identityKey]) {
return YES;
} else {
DDLogError(@"%s Wrong identity: %@ for local key: %@",
__PRETTY_FUNCTION__,
identityKey,
[self identityKeyPair].publicKey);
OWSAssert(NO);
return NO;
}
}
switch (direction) {
case TSMessageDirectionIncoming: {
return YES;
}
case TSMessageDirectionOutgoing: {
OWSRecipientIdentity *existingIdentity = [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
return [self isTrustedKey:identityKey forSendingToIdentity:existingIdentity];
}
default: {
DDLogError(@"%s unexpected message direction: %ld", __PRETTY_FUNCTION__, (long)direction);
OWSAssert(NO);
return NO;
}
}
}
}
- (nullable OWSRecipientIdentity *)unconfirmedIdentityThatShouldBlockSendingForRecipientId:(NSString *)recipientId;
{
OWSAssert(recipientId != nil);
@synchronized([[self class] sharedIdentityKeyLock])
{
OWSRecipientIdentity *currentIdentity = [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
if (currentIdentity == nil) {
// No preexisting key, Trust On First Use
return nil;
}
if ([self isTrustedIdentityKey:currentIdentity.identityKey
recipientId:currentIdentity.recipientId
direction:TSMessageDirectionOutgoing]) {
return nil;
}
// identity not yet trusted for sending
return currentIdentity;
}
}
- (NSArray<OWSRecipientIdentity *> *)unseenIdentityChangesForRecipientIds:(NSArray<NSString *> *)recipientIds
{
NSMutableArray<OWSRecipientIdentity *> *unseenRecipientIdentities = [NSMutableArray new];
@synchronized([[self class] sharedIdentityKeyLock])
{
for (NSString *recipientId in recipientIds) {
OWSRecipientIdentity *identity = [self unseenIdentityChangeForRecipientId:recipientId];
if (identity) {
[unseenRecipientIdentities addObject:identity];
}
}
}
return [unseenRecipientIdentities copy];
}
- (nullable OWSRecipientIdentity *)unseenIdentityChangeForRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId != nil);
@synchronized([[self class] sharedIdentityKeyLock])
{
OWSRecipientIdentity *currentIdentity = [OWSRecipientIdentity fetchObjectWithUniqueID:recipientId];
if (currentIdentity == nil) {
// No preexisting key, Trust On First Use
return nil;
}
if (currentIdentity.isFirstKnownKey) {
return nil;
}
if (currentIdentity.wasSeen) {
return nil;
}
// identity not yet seen
return currentIdentity;
}
}
- (BOOL)isTrustedKey:(NSData *)identityKey forSendingToIdentity:(nullable OWSRecipientIdentity *)recipientIdentity
{
OWSAssert(identityKey != nil);
@synchronized([[self class] sharedIdentityKeyLock])
{
if (recipientIdentity == nil) {
DDLogDebug(@"%s Trusting on first use for recipient: %@", __PRETTY_FUNCTION__, recipientIdentity.recipientId);
return YES;
}
OWSAssert(recipientIdentity.identityKey != nil);
if (![recipientIdentity.identityKey isEqualToData:identityKey]) {
DDLogWarn(@"%s key mismatch for recipient: %@", __PRETTY_FUNCTION__, recipientIdentity.recipientId);
return NO;
}
if ([self isBlockingApprovalRequiredForIdentity:recipientIdentity]) {
DDLogWarn(@"%s not trusting until blocking approval is granted. recipient: %@",
__PRETTY_FUNCTION__,
recipientIdentity.recipientId);
return NO;
}
if ([self isNonBlockingApprovalRequiredForIdentity:recipientIdentity]) {
DDLogWarn(@"%s not trusting until non-blocking approval is granted. recipient: %@",
__PRETTY_FUNCTION__,
recipientIdentity.recipientId);
return NO;
}
return YES;
}
}
- (BOOL)isBlockingApprovalRequiredForIdentity:(OWSRecipientIdentity *)recipientIdentity
{
OWSAssert(recipientIdentity != nil);
OWSAssert([TextSecureKitEnv sharedEnv].preferences != nil);
return !recipientIdentity.isFirstKnownKey &&
[TextSecureKitEnv sharedEnv].preferences.isSendingIdentityApprovalRequired &&
!recipientIdentity.approvedForBlockingUse;
}
- (BOOL)isNonBlockingApprovalRequiredForIdentity:(OWSRecipientIdentity *)recipientIdentity
{
OWSAssert(recipientIdentity != nil);
return !recipientIdentity.isFirstKnownKey &&
[[NSDate new] timeIntervalSinceDate:recipientIdentity.createdAt] < kIdentityKeyStoreNonBlockingSecondsThreshold &&
!recipientIdentity.approvedForNonBlockingUse;
}
- (void)removeIdentityKeyForRecipient:(NSString *)recipientId
{
OWSAssert(recipientId != nil);
[[OWSRecipientIdentity fetchObjectWithUniqueID:recipientId] remove];
}
- (void)createIdentityChangeInfoMessageForRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId != nil);
TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:recipientId];
OWSAssert(contactThread != nil);
TSErrorMessage *errorMessage =
[TSErrorMessage nonblockingIdentityChangeInThread:contactThread recipientId:recipientId];
[errorMessage save];
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:contactThread];
for (TSGroupThread *groupThread in [TSGroupThread groupThreadsWithRecipientId:recipientId]) {
[[TSErrorMessage nonblockingIdentityChangeInThread:groupThread recipientId:recipientId] save];
}
}
@end
NS_ASSUME_NONNULL_END

View File

@ -7,8 +7,6 @@
@interface TSStorageManager (SessionStore) <SessionStore>
- (void)archiveAllSessionsForContact:(NSString *)contactIdentifier;
#pragma mark - debug
- (void)printAllSessions;

View File

@ -3,7 +3,6 @@
//
#import "TSStorageManager+SessionStore.h"
#import <AxolotlKit/SessionRecord.h>
NSString *const TSStorageManagerSessionStoreCollection = @"TSStorageManagerSessionStoreCollection";
NSString *const kSessionStoreDBConnectionKey = @"kSessionStoreDBConnectionKey";
@ -159,34 +158,6 @@ void AssertIsOnSessionStoreQueue()
}];
}
- (void)archiveAllSessionsForContact:(NSString *)contactIdentifier
{
AssertIsOnSessionStoreQueue();
DDLogInfo(@"[TSStorageManager (SessionStore)] archiving all sessions for contact: %@", contactIdentifier);
__block NSDictionary<NSNumber *, SessionRecord *> *sessionRecords;
[self.sessionDBConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
sessionRecords =
[transaction objectForKey:contactIdentifier inCollection:TSStorageManagerSessionStoreCollection];
for (id deviceId in sessionRecords) {
id object = sessionRecords[deviceId];
if (![object isKindOfClass:[SessionRecord class]]) {
OWSFail(@"Unexpected object in session dict: %@", object);
continue;
}
SessionRecord *sessionRecord = (SessionRecord *)object;
[sessionRecord archiveCurrentState];
}
[transaction setObject:sessionRecords
forKey:contactIdentifier
inCollection:TSStorageManagerSessionStoreCollection];
}];
}
#pragma mark - debug
- (void)printAllSessions

View File

@ -2,7 +2,7 @@
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSIdentityManager.h"
#import "TSStorageManager+IdentityKeyStore.h"
#import "TSStorageManager+PreKeyStore.h"
#import "TSStorageManager+SignedPreKeyStore.h"
#import "TSStorageManager+keyFromIntLong.h"
@ -26,11 +26,10 @@ NSString *const TSStorageManagerKeyPrekeyCurrentSignedPrekeyId = @"currentSigned
// Signed prekey ids must be > 0.
int preKeyId = 1 + arc4random_uniform(INT32_MAX - 1);
ECKeyPair *_Nullable identityKeyPair = [[OWSIdentityManager sharedManager] identityKeyPair];
return [[SignedPreKeyRecord alloc]
initWithId:preKeyId
keyPair:keyPair
signature:[Ed25519 sign:keyPair.publicKey.prependKeyType withKeyPair:identityKeyPair]
signature:[Ed25519 sign:keyPair.publicKey.prependKeyType withKeyPair:[self identityKeyPair]]
generatedAt:[NSDate date]];
}
@ -148,7 +147,7 @@ NSString *const TSStorageManagerKeyPrekeyCurrentSignedPrekeyId = @"currentSigned
NSDate *firstPrekeyUpdateFailureDate = [self firstPrekeyUpdateFailureDate];
NSUInteger prekeyUpdateFailureCount = [self prekeyUpdateFailureCount];
[self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
__block int i = 0;
DDLogInfo(@"%@ SignedPreKeys Report:", tag);

View File

@ -1,30 +1,10 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
// Notes:
//
// * On disk, we only bother cleaning up files, not directories.
// * For code simplicity, we don't guarantee that everything is
// cleaned up in a single pass. If an interaction is cleaned up,
// it's attachments might not be cleaned up until the next pass.
// If an attachment is cleaned up, it's file on disk might not
// be cleaned up until the next pass.
@interface OWSOrphanedDataCleaner : NSObject
- (instancetype)init NS_UNAVAILABLE;
+ (void)auditAsync;
// completion, if present, will be invoked on the main thread.
+ (void)auditAndCleanupAsync:(void (^_Nullable)())completion;
+ (NSSet<NSString *> *)filePathsInAttachmentsFolder;
+ (long long)fileSizeOfFilePaths:(NSArray<NSString *> *)filePaths;
/**
* Remove any inaccessible data left behind due to application bugs.
*/
- (void)removeOrphanedData;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,6 +1,4 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2016 Open Whisper Systems. All rights reserved.
#import "OWSOrphanedDataCleaner.h"
#import "TSAttachmentStream.h"
@ -9,256 +7,84 @@
#import "TSStorageManager.h"
#import "TSThread.h"
NS_ASSUME_NONNULL_BEGIN
#ifdef SSK_BUILDING_FOR_TESTS
#define CleanupLogDebug NSLog
#define CleanupLogInfo NSLog
#else
#define CleanupLogDebug DDLogDebug
#define CleanupLogInfo DDLogInfo
#endif
@implementation OWSOrphanedDataCleaner
+ (void)auditAsync
- (void)removeOrphanedData
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[OWSOrphanedDataCleaner auditAndCleanup:NO completion:nil];
});
}
+ (void)auditAndCleanupAsync:(void (^_Nullable)())completion
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[OWSOrphanedDataCleaner auditAndCleanup:YES completion:completion];
});
}
// This method finds and optionally cleans up:
//
// * Orphan messages (with no thread).
// * Orphan attachments (with no message).
// * Orphan attachment files (with no attachment).
// * Missing attachment files (cannot be cleaned up).
// These are attachments which have no file on disk. They should be extremely rare -
// the only cases I have seen are probably due to debugging.
// They can't be cleaned up - we don't want to delete the TSAttachmentStream or
// its corresponding message. Better that the broken message shows up in the
// conversation view.
+ (void)auditAndCleanup:(BOOL)shouldCleanup completion:(void (^_Nullable)())completion
{
NSSet<NSString *> *diskFilePaths = [self filePathsInAttachmentsFolder];
long long totalFileSize = [self fileSizeOfFilePaths:diskFilePaths.allObjects];
NSUInteger fileCount = diskFilePaths.count;
TSStorageManager *storageManager = [TSStorageManager sharedManager];
YapDatabaseConnection *databaseConnection = storageManager.newDatabaseConnection;
__block int attachmentStreamCount = 0;
NSMutableSet<NSString *> *attachmentFilePaths = [NSMutableSet new];
NSMutableSet<NSString *> *attachmentIds = [NSMutableSet new];
[databaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[transaction enumerateKeysAndObjectsInCollection:TSAttachmentStream.collection
usingBlock:^(NSString *key, TSAttachment *attachment, BOOL *stop) {
[attachmentIds addObject:attachment.uniqueId];
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
return;
}
TSAttachmentStream *attachmentStream
= (TSAttachmentStream *)attachment;
attachmentStreamCount++;
NSString *_Nullable filePath = [attachmentStream filePath];
OWSAssert(filePath);
[attachmentFilePaths addObject:filePath];
}];
}];
CleanupLogDebug(@"fileCount: %zd", fileCount);
CleanupLogDebug(@"totalFileSize: %lld", totalFileSize);
CleanupLogDebug(@"attachmentStreams: %d", attachmentStreamCount);
CleanupLogDebug(@"attachmentStreams with file paths: %zd", attachmentFilePaths.count);
NSMutableSet<NSString *> *orphanDiskFilePaths = [diskFilePaths mutableCopy];
[orphanDiskFilePaths minusSet:attachmentFilePaths];
NSMutableSet<NSString *> *missingAttachmentFilePaths = [attachmentFilePaths mutableCopy];
[missingAttachmentFilePaths minusSet:diskFilePaths];
CleanupLogDebug(@"orphan disk file paths: %zd", orphanDiskFilePaths.count);
CleanupLogDebug(@"missing attachment file paths: %zd", missingAttachmentFilePaths.count);
[self printPaths:orphanDiskFilePaths.allObjects label:@"orphan disk file paths"];
[self printPaths:missingAttachmentFilePaths.allObjects label:@"missing attachment file paths"];
NSMutableSet *threadIds = [NSMutableSet new];
[databaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[transaction enumerateKeysInCollection:TSThread.collection
usingBlock:^(NSString *_Nonnull key, BOOL *_Nonnull stop) {
[threadIds addObject:key];
}];
}];
NSMutableSet<NSString *> *orphanInteractionIds = [NSMutableSet new];
NSMutableSet<NSString *> *messageAttachmentIds = [NSMutableSet new];
[databaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[transaction enumerateKeysAndObjectsInCollection:TSMessage.collection
usingBlock:^(NSString *key, TSInteraction *interaction, BOOL *stop) {
if (![threadIds containsObject:interaction.uniqueThreadId]) {
[orphanInteractionIds addObject:interaction.uniqueId];
}
if (![interaction isKindOfClass:[TSMessage class]]) {
return;
}
TSMessage *message = (TSMessage *)interaction;
if (message.attachmentIds.count > 0) {
[messageAttachmentIds addObjectsFromArray:message.attachmentIds];
}
}];
}];
CleanupLogDebug(@"attachmentIds: %zd", attachmentIds.count);
CleanupLogDebug(@"messageAttachmentIds: %zd", messageAttachmentIds.count);
NSMutableSet<NSString *> *orphanAttachmentIds = [attachmentIds mutableCopy];
[orphanAttachmentIds minusSet:messageAttachmentIds];
NSMutableSet<NSString *> *missingAttachmentIds = [messageAttachmentIds mutableCopy];
[missingAttachmentIds minusSet:attachmentIds];
CleanupLogDebug(@"orphan attachmentIds: %zd", orphanAttachmentIds.count);
CleanupLogDebug(@"missing attachmentIds: %zd", missingAttachmentIds.count);
CleanupLogDebug(@"orphan interactions: %zd", orphanInteractionIds.count);
// We need to avoid cleaning up new attachments and files that are still in the process of
// being created/written, so we don't clean up anything recent.
#ifdef SSK_BUILDING_FOR_TESTS
const NSTimeInterval kMinimumOrphanAge = 0.f;
#else
const NSTimeInterval kMinimumOrphanAge = 15 * 60.f;
#endif
if (!shouldCleanup) {
return;
// Remove interactions whose threads have been deleted
for (NSString *interactionId in [self orphanedInteractionIds]) {
DDLogWarn(@"Removing orphaned interaction with id: %@", interactionId);
TSInteraction *interaction = [TSInteraction fetchObjectWithUniqueID:interactionId];
[interaction remove];
}
[databaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
for (NSString *interactionId in orphanInteractionIds) {
TSInteraction *interaction = [TSInteraction fetchObjectWithUniqueID:interactionId transaction:transaction];
if (!interaction) {
// This could just be a race condition, but it should be very unlikely.
OWSFail(@"Could not load interaction: %@", interactionId);
continue;
}
CleanupLogInfo(@"Removing orphan message: %@", interaction.uniqueId);
[interaction removeWithTransaction:transaction];
}
for (NSString *attachmentId in orphanAttachmentIds) {
TSAttachment *attachment = [TSAttachment fetchObjectWithUniqueID:attachmentId transaction:transaction];
if (!attachment) {
// This could just be a race condition, but it should be very unlikely.
OWSFail(@"Could not load attachment: %@", attachmentId);
continue;
}
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
continue;
}
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
// Don't delete attachments which were created in the last N minutes.
if (fabs([attachmentStream.creationTimestamp timeIntervalSinceNow]) < kMinimumOrphanAge) {
CleanupLogInfo(@"Skipping orphan attachment due to age: %f",
fabs([attachmentStream.creationTimestamp timeIntervalSinceNow]));
continue;
}
CleanupLogInfo(@"Removing orphan attachment: %@", attachmentStream.uniqueId);
[attachmentStream removeWithTransaction:transaction];
}
}];
for (NSString *filePath in orphanDiskFilePaths) {
// Remove any lingering attachments
for (NSString *path in [self orphanedFilePaths]) {
DDLogWarn(@"Removing orphaned file attachment at path: %@", path);
NSError *error;
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];
if (!attributes || error) {
OWSFail(@"Could not get attributes of file at: %@", filePath);
continue;
}
// Don't delete files which were created in the last N minutes.
if (fabs([attributes.fileModificationDate timeIntervalSinceNow]) < kMinimumOrphanAge) {
CleanupLogInfo(@"Skipping orphan attachment file due to age: %f",
fabs([attributes.fileModificationDate timeIntervalSinceNow]));
continue;
}
CleanupLogInfo(@"Removing orphan attachment file: %@", filePath);
[[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
[[NSFileManager defaultManager] removeItemAtPath:path error:&error];
if (error) {
OWSFail(@"Could not remove orphan file at: %@", filePath);
DDLogError(@"Unable to remove orphaned file attachment at path:%@", path);
}
}
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
}
+ (void)printPaths:(NSArray<NSString *> *)paths label:(NSString *)label
- (NSArray<NSString *> *)orphanedInteractionIds
{
for (NSString *path in [paths sortedArrayUsingSelector:@selector(compare:)]) {
CleanupLogDebug(@"%@: %@", label, path);
}
NSMutableArray *interactionIds = [NSMutableArray new];
[[TSInteraction dbConnection] readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[TSInteraction enumerateCollectionObjectsWithTransaction:transaction
usingBlock:^(TSInteraction *interaction, BOOL *stop) {
TSThread *thread = [TSThread
fetchObjectWithUniqueID:interaction.uniqueThreadId
transaction:transaction];
if (!thread) {
[interactionIds addObject:interaction.uniqueId];
}
}];
}];
return [interactionIds copy];
}
+ (NSSet<NSString *> *)filePathsInAttachmentsFolder
- (NSArray<NSString *> *)orphanedFilePaths
{
NSString *attachmentsFolder = [TSAttachmentStream attachmentsFolder];
CleanupLogDebug(@"attachmentsFolder: %@", attachmentsFolder);
return [self filePathsInDirectory:attachmentsFolder];
}
+ (NSSet<NSString *> *)filePathsInDirectory:(NSString *)dirPath
{
NSMutableSet *filePaths = [NSMutableSet new];
NSError *error;
NSArray<NSString *> *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dirPath error:&error];
NSMutableArray<NSString *> *filenames =
[[[NSFileManager defaultManager] contentsOfDirectoryAtPath:[TSAttachmentStream attachmentsFolder] error:&error]
mutableCopy];
if (error) {
OWSFail(@"contentsOfDirectoryAtPath error: %@", error);
return [NSSet new];
DDLogError(@"error getting orphanedFilePaths:%@", error);
return @[];
}
for (NSString *fileName in fileNames) {
NSString *filePath = [dirPath stringByAppendingPathComponent:fileName];
BOOL isDirectory;
[[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDirectory];
if (isDirectory) {
[filePaths addObjectsFromArray:[self filePathsInDirectory:filePath].allObjects];
} else {
[filePaths addObject:filePath];
NSMutableDictionary<NSString *, NSString *> *attachmentIdFilenames = [NSMutableDictionary new];
for (NSString *filename in filenames) {
// Remove extension from (e.g.) 1234.png to get the attachmentId "1234"
NSString *attachmentId = [filename stringByDeletingPathExtension];
attachmentIdFilenames[attachmentId] = filename;
}
[TSInteraction enumerateCollectionObjectsUsingBlock:^(TSInteraction *interaction, BOOL *stop) {
if ([interaction isKindOfClass:[TSMessage class]]) {
TSMessage *message = (TSMessage *)interaction;
if ([message hasAttachments]) {
for (NSString *attachmentId in message.attachmentIds) {
[attachmentIdFilenames removeObjectForKey:attachmentId];
}
}
}
}
return filePaths;
}
}];
+ (long long)fileSizeOfFilePath:(NSString *)filePath
{
NSError *error;
NSNumber *fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error][NSFileSize];
if (error) {
OWSFail(@"attributesOfItemAtPath: %@ error: %@", filePath, error);
return 0;
NSArray<NSString *> *filenamesToDelete = [attachmentIdFilenames allValues];
NSMutableArray<NSString *> *absolutePathsToDelete = [NSMutableArray arrayWithCapacity:[filenamesToDelete count]];
for (NSString *filename in filenamesToDelete) {
NSString *absolutePath = [[TSAttachmentStream attachmentsFolder] stringByAppendingFormat:@"/%@", filename];
[absolutePathsToDelete addObject:absolutePath];
}
return fileSize.longLongValue;
}
+ (long long)fileSizeOfFilePaths:(NSArray<NSString *> *)filePaths
{
long long result = 0;
for (NSString *filePath in filePaths) {
result += [self fileSizeOfFilePath:filePath];
}
return result;
return [absolutePathsToDelete copy];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -1,5 +1,9 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// TSDatabaseSecondaryIndexes.m
// Signal
//
// Created by Frederic Jacobs on 26/01/15.
// Copyright (c) 2015 Open Whisper Systems. All rights reserved.
//
#import "TSDatabaseSecondaryIndexes.h"
@ -39,5 +43,4 @@
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
[[transaction ext:@"idx"] enumerateKeysMatchingQuery:query usingBlock:block];
}
@end

View File

@ -5,56 +5,40 @@
#import <Foundation/Foundation.h>
#import <YapDatabase/YapDatabaseViewTransaction.h>
extern NSString *const kNSNotificationName_DatabaseViewRegistrationComplete;
extern NSString *const TSInboxGroup;
extern NSString *const TSArchiveGroup;
extern NSString *const TSUnreadIncomingMessagesGroup;
extern NSString *const TSSecondaryDevicesGroup;
extern NSString *const TSThreadDatabaseViewExtensionName;
extern NSString *const TSMessageDatabaseViewExtensionName;
extern NSString *const TSUnreadDatabaseViewExtensionName;
extern NSString *const TSSecondaryDevicesDatabaseViewExtensionName;
@interface TSDatabaseView : NSObject
- (instancetype)init NS_UNAVAILABLE;
extern NSString *TSInboxGroup;
extern NSString *TSArchiveGroup;
extern NSString *TSUnreadIncomingMessagesGroup;
extern NSString *TSSecondaryDevicesGroup;
+ (BOOL)hasPendingViewRegistrations;
extern NSString *TSThreadDatabaseViewExtensionName;
extern NSString *TSMessageDatabaseViewExtensionName;
extern NSString *TSThreadIncomingMessageDatabaseViewExtensionName;
extern NSString *TSThreadOutgoingMessageDatabaseViewExtensionName;
extern NSString *TSUnreadDatabaseViewExtensionName;
extern NSString *TSUnseenDatabaseViewExtensionName;
extern NSString *TSDynamicMessagesDatabaseViewExtensionName;
extern NSString *TSSafetyNumberChangeDatabaseViewExtensionName;
extern NSString *TSSecondaryDevicesDatabaseViewExtensionName;
+ (void)registerThreadDatabaseView;
+ (void)registerThreadInteractionsDatabaseView;
+ (void)asyncRegisterThreadOutgoingMessagesDatabaseView;
+ (BOOL)registerThreadDatabaseView;
+ (BOOL)registerThreadInteractionsDatabaseView;
+ (BOOL)registerThreadIncomingMessagesDatabaseView;
+ (BOOL)registerThreadOutgoingMessagesDatabaseView;
// Instances of OWSReadTracking for wasRead is NO and shouldAffectUnreadCounts is YES.
//
// Should be used for "unread message counts".
+ (void)registerUnreadDatabaseView;
+ (BOOL)registerUnreadDatabaseView;
// Should be used for "unread indicator".
//
// Instances of OWSReadTracking for wasRead is NO.
+ (void)asyncRegisterUnseenDatabaseView;
+ (void)asyncRegisterThreadSpecialMessagesDatabaseView;
+ (BOOL)registerUnseenDatabaseView;
+ (BOOL)registerDynamicMessagesDatabaseView;
+ (BOOL)registerSafetyNumberChangeDatabaseView;
+ (void)asyncRegisterSecondaryDevicesDatabaseView;
// Returns the "unseen" database view if it is ready;
// otherwise it returns the "unread" database view.
+ (id)unseenDatabaseViewExtension:(YapDatabaseReadTransaction *)transaction;
// NOTE: It is not safe to call this method while hasPendingViewRegistrations is YES.
+ (id)threadOutgoingMessageDatabaseView:(YapDatabaseReadTransaction *)transaction;
// NOTE: It is not safe to call this method while hasPendingViewRegistrations is YES.
+ (id)threadSpecialMessagesDatabaseView:(YapDatabaseReadTransaction *)transaction;
// This method should be called _after_ all async database registrations have been started.
+ (void)asyncRegistrationCompletion;
@end

View File

@ -12,77 +12,34 @@
#import "TSThread.h"
#import <YapDatabase/YapDatabaseView.h>
NSString *const kNSNotificationName_DatabaseViewRegistrationComplete =
@"kNSNotificationName_DatabaseViewRegistrationComplete";
NSString *TSInboxGroup = @"TSInboxGroup";
NSString *TSArchiveGroup = @"TSArchiveGroup";
NSString *const TSInboxGroup = @"TSInboxGroup";
NSString *const TSArchiveGroup = @"TSArchiveGroup";
NSString *TSUnreadIncomingMessagesGroup = @"TSUnreadIncomingMessagesGroup";
NSString *TSSecondaryDevicesGroup = @"TSSecondaryDevicesGroup";
NSString *const TSUnreadIncomingMessagesGroup = @"TSUnreadIncomingMessagesGroup";
NSString *const TSSecondaryDevicesGroup = @"TSSecondaryDevicesGroup";
// YAPDB BUG: when changing from non-persistent to persistent view, we had to rename TSThreadDatabaseViewExtensionName
// -> TSThreadDatabaseViewExtensionName2 to work around https://github.com/yapstudios/YapDatabase/issues/324
NSString *const TSThreadDatabaseViewExtensionName = @"TSThreadDatabaseViewExtensionName2";
NSString *const TSMessageDatabaseViewExtensionName = @"TSMessageDatabaseViewExtensionName";
NSString *const TSThreadOutgoingMessageDatabaseViewExtensionName = @"TSThreadOutgoingMessageDatabaseViewExtensionName";
NSString *const TSUnreadDatabaseViewExtensionName = @"TSUnreadDatabaseViewExtensionName";
NSString *const TSUnseenDatabaseViewExtensionName = @"TSUnseenDatabaseViewExtensionName";
NSString *const TSThreadSpecialMessagesDatabaseViewExtensionName = @"TSThreadSpecialMessagesDatabaseViewExtensionName";
NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevicesDatabaseViewExtensionName";
@interface TSDatabaseView ()
@property (nonatomic) BOOL areAllAsyncRegistrationsComplete;
@end
#pragma mark -
NSString *TSThreadDatabaseViewExtensionName = @"TSThreadDatabaseViewExtensionName";
NSString *TSMessageDatabaseViewExtensionName = @"TSMessageDatabaseViewExtensionName";
NSString *TSThreadIncomingMessageDatabaseViewExtensionName = @"TSThreadOutgoingMessageDatabaseViewExtensionName";
NSString *TSThreadOutgoingMessageDatabaseViewExtensionName = @"TSThreadOutgoingMessageDatabaseViewExtensionName";
NSString *TSUnreadDatabaseViewExtensionName = @"TSUnreadDatabaseViewExtensionName";
NSString *TSUnseenDatabaseViewExtensionName = @"TSUnseenDatabaseViewExtensionName";
NSString *TSDynamicMessagesDatabaseViewExtensionName = @"TSDynamicMessagesDatabaseViewExtensionName";
NSString *TSSafetyNumberChangeDatabaseViewExtensionName = @"TSSafetyNumberChangeDatabaseViewExtensionName";
NSString *TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevicesDatabaseViewExtensionName";
@implementation TSDatabaseView
+ (instancetype)sharedInstance
{
static TSDatabaseView *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] initDefault];
});
return sharedInstance;
}
- (instancetype)initDefault
{
self = [super init];
if (!self) {
return self;
}
OWSSingletonAssert();
return self;
}
+ (BOOL)hasPendingViewRegistrations
{
OWSAssert([NSThread isMainThread]);
return ![TSDatabaseView sharedInstance].areAllAsyncRegistrationsComplete;
}
+ (void)registerMessageDatabaseViewWithName:(NSString *)viewName
+ (BOOL)registerMessageDatabaseViewWithName:(NSString *)viewName
viewGrouping:(YapDatabaseViewGrouping *)viewGrouping
version:(NSString *)version
async:(BOOL)async
{
OWSAssert([NSThread isMainThread]);
OWSAssert(viewName.length > 0);
OWSAssert((viewGrouping));
YapDatabaseView *existingView = [[TSStorageManager sharedManager].database registeredExtension:viewName];
if (existingView) {
return;
return YES;
}
YapDatabaseViewSorting *viewSorting = [self messagesSorting];
@ -95,21 +52,10 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
YapDatabaseView *view =
[[YapDatabaseView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:version options:options];
if (async) {
[[TSStorageManager sharedManager].database
asyncRegisterExtension:view
withName:viewName
completionBlock:^(BOOL ready) {
OWSCAssert(ready);
DDLogInfo(@"%@ asyncRegisterExtension: %@ -> %d", self.tag, viewName, ready);
}];
} else {
[[TSStorageManager sharedManager].database registerExtension:view withName:viewName];
}
return [[TSStorageManager sharedManager].database registerExtension:view withName:viewName];
}
+ (void)registerUnreadDatabaseView
+ (BOOL)registerUnreadDatabaseView
{
YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *(
YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) {
@ -122,13 +68,12 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
return nil;
}];
[self registerMessageDatabaseViewWithName:TSUnreadDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"1"
async:NO];
return [self registerMessageDatabaseViewWithName:TSUnreadDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"1"];
}
+ (void)asyncRegisterUnseenDatabaseView
+ (BOOL)registerUnseenDatabaseView
{
YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *(
YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) {
@ -141,22 +86,36 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
return nil;
}];
[self registerMessageDatabaseViewWithName:TSUnseenDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"1"
async:YES];
return [self registerMessageDatabaseViewWithName:TSUnseenDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"1"];
}
+ (void)asyncRegisterThreadSpecialMessagesDatabaseView
+ (BOOL)registerDynamicMessagesDatabaseView
{
YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *(
YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) {
OWSAssert([object isKindOfClass:[TSInteraction class]]);
if ([object isKindOfClass:[TSInteraction class]]) {
TSInteraction *interaction = (TSInteraction *)object;
if ([interaction isDynamicInteraction]) {
return interaction.uniqueThreadId;
}
} else {
OWSAssert(0);
}
return nil;
}];
TSInteraction *interaction = (TSInteraction *)object;
if ([interaction isDynamicInteraction]) {
return interaction.uniqueThreadId;
} else if ([object isKindOfClass:[TSInvalidIdentityKeyErrorMessage class]]) {
return [self registerMessageDatabaseViewWithName:TSDynamicMessagesDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"3"];
}
+ (BOOL)registerSafetyNumberChangeDatabaseView
{
YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *(
YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) {
if ([object isKindOfClass:[TSInvalidIdentityKeyErrorMessage class]]) {
TSInteraction *interaction = (TSInteraction *)object;
return interaction.uniqueThreadId;
} else if ([object isKindOfClass:[TSErrorMessage class]]) {
@ -168,29 +127,42 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
return nil;
}];
[self registerMessageDatabaseViewWithName:TSThreadSpecialMessagesDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"1"
async:YES];
return [self registerMessageDatabaseViewWithName:TSSafetyNumberChangeDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"2"];
}
+ (void)registerThreadInteractionsDatabaseView
+ (BOOL)registerThreadInteractionsDatabaseView
{
YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *(
YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) {
OWSAssert([object isKindOfClass:[TSInteraction class]]);
TSInteraction *interaction = (TSInteraction *)object;
return interaction.uniqueThreadId;
if ([object isKindOfClass:[TSInteraction class]]) {
return ((TSInteraction *)object).uniqueThreadId;
}
return nil;
}];
[self registerMessageDatabaseViewWithName:TSMessageDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"1"
async:NO];
return [self registerMessageDatabaseViewWithName:TSMessageDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"1"];
}
+ (void)asyncRegisterThreadOutgoingMessagesDatabaseView
+ (BOOL)registerThreadIncomingMessagesDatabaseView
{
YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *(
YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) {
if ([object isKindOfClass:[TSIncomingMessage class]]) {
return ((TSIncomingMessage *)object).uniqueThreadId;
}
return nil;
}];
return [self registerMessageDatabaseViewWithName:TSThreadIncomingMessageDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"1"];
}
+ (BOOL)registerThreadOutgoingMessagesDatabaseView
{
YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *(
YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) {
@ -200,18 +172,16 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
return nil;
}];
[self registerMessageDatabaseViewWithName:TSThreadOutgoingMessageDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"2"
async:YES];
return [self registerMessageDatabaseViewWithName:TSThreadOutgoingMessageDatabaseViewExtensionName
viewGrouping:viewGrouping
version:@"1"];
}
+ (void)registerThreadDatabaseView
{
+ (BOOL)registerThreadDatabaseView {
YapDatabaseView *threadView =
[[TSStorageManager sharedManager].database registeredExtension:TSThreadDatabaseViewExtensionName];
if (threadView) {
return;
return YES;
}
YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping
@ -233,15 +203,16 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
YapDatabaseViewSorting *viewSorting = [self threadSorting];
YapDatabaseViewOptions *options = [[YapDatabaseViewOptions alloc] init];
options.isPersistent = YES;
options.isPersistent = NO;
options.allowedCollections =
[[YapWhitelistBlacklist alloc] initWithWhitelist:[NSSet setWithObject:[TSThread collection]]];
YapDatabaseView *databaseView =
[[YapDatabaseView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:@"1" options:options];
[[TSStorageManager sharedManager].database registerExtension:databaseView
withName:TSThreadDatabaseViewExtensionName];
return [[TSStorageManager sharedManager]
.database registerExtension:databaseView
withName:TSThreadDatabaseViewExtensionName];
}
/**
@ -366,57 +337,6 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
}];
}
+ (id)unseenDatabaseViewExtension:(YapDatabaseReadTransaction *)transaction
{
OWSAssert(transaction);
id result = [transaction ext:TSUnseenDatabaseViewExtensionName];
if (!result) {
result = [transaction ext:TSUnreadDatabaseViewExtensionName];
OWSAssert(result);
}
return result;
}
+ (id)threadOutgoingMessageDatabaseView:(YapDatabaseReadTransaction *)transaction
{
OWSAssert(transaction);
id result = [transaction ext:TSThreadOutgoingMessageDatabaseViewExtensionName];
OWSAssert(result);
return result;
}
+ (id)threadSpecialMessagesDatabaseView:(YapDatabaseReadTransaction *)transaction
{
OWSAssert(transaction);
id result = [transaction ext:TSThreadSpecialMessagesDatabaseViewExtensionName];
OWSAssert(result);
return result;
}
+ (void)asyncRegistrationCompletion
{
OWSAssert([NSThread isMainThread]);
// All async registrations are complete when writes are unblocked.
[[TSStorageManager sharedManager].newDatabaseConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
dispatch_async(dispatch_get_main_queue(), ^{
TSDatabaseView.sharedInstance.areAllAsyncRegistrationsComplete = YES;
[[NSNotificationCenter defaultCenter]
postNotificationName:kNSNotificationName_DatabaseViewRegistrationComplete
object:nil
userInfo:nil];
});
}];
}
#pragma mark - Logging
+ (NSString *)tag

View File

@ -1,5 +1,9 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// TSStorageHeaders.h
// Signal
//
// Created by Frederic Jacobs on 22/08/15.
// Copyright (c) 2015 Open Whisper Systems. All rights reserved.
//
#ifndef Signal_TSStorageHeaders_h
@ -7,7 +11,7 @@
#import "TSStorageManager.h"
#import "OWSIdentityManager.h"
#import "TSStorageManager+IdentityKeyStore.h"
#import "TSStorageManager+PreKeyStore.h"
#import "TSStorageManager+SessionStore.h"
#import "TSStorageManager+SignedPreKeyStore.h"

View File

@ -53,7 +53,7 @@
- (void)storePhoneNumber:(NSString *)phoneNumber
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction setObject:phoneNumber
forKey:TSStorageRegisteredNumberKey
inCollection:TSStorageUserAccountCollection];
@ -61,12 +61,13 @@
}
+ (void)storeServerToken:(NSString *)authToken signalingKey:(NSString *)signalingKey {
TSStorageManager *sharedManager = self.sharedManager;
[sharedManager.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction setObject:authToken forKey:TSStorageServerAuthToken inCollection:TSStorageUserAccountCollection];
[transaction setObject:signalingKey
forKey:TSStorageServerSignalingKey
inCollection:TSStorageUserAccountCollection];
YapDatabaseConnection *dbConn = [[self sharedManager] dbConnection];
[dbConn readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction setObject:authToken forKey:TSStorageServerAuthToken inCollection:TSStorageUserAccountCollection];
[transaction setObject:signalingKey
forKey:TSStorageServerSignalingKey
inCollection:TSStorageUserAccountCollection];
}];
}

View File

@ -27,16 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
+ (BOOL)isDatabasePasswordAccessible;
/**
* The safeBlockingMigrationsBlock block will
* run any outstanding version migrations that are a) blocking and b) safe
* to be run before the environment and storage is completely configured.
*
* Specifically, these migration should not depend on or affect the data
* of any database view.
*/
- (void)setupDatabaseWithSafeBlockingMigrations:(void (^_Nonnull)())safeBlockingMigrationsBlock;
- (void)setupDatabase;
- (void)deleteThreadsAndMessages;
- (void)resetSignalStorage;
@ -61,8 +52,7 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable SignedPreKeyRecord *)signedPreKeyRecordForKey:(NSString *)key inCollection:(NSString *)collection;
- (void)purgeCollection:(NSString *)collection;
@property (nullable, nonatomic, readonly) YapDatabaseConnection *dbReadConnection;
@property (nullable, nonatomic, readonly) YapDatabaseConnection *dbReadWriteConnection;
@property (nullable, nonatomic, readonly) YapDatabaseConnection *dbConnection;
@end

View File

@ -156,8 +156,7 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
if (!_database) {
return NO;
}
_dbReadConnection = self.newDatabaseConnection;
_dbReadWriteConnection = self.newDatabaseConnection;
_dbConnection = self.newDatabaseConnection;
return YES;
}
@ -192,45 +191,30 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
};
}
- (void)setupDatabaseWithSafeBlockingMigrations:(void (^_Nonnull)())safeBlockingMigrationsBlock
- (void)setupDatabase
{
// Synchronously register extensions which are essential for views.
// Register extensions which are essential for rendering threads synchronously
[TSDatabaseView registerThreadDatabaseView];
[TSDatabaseView registerThreadInteractionsDatabaseView];
[TSDatabaseView registerThreadIncomingMessagesDatabaseView];
[TSDatabaseView registerThreadOutgoingMessagesDatabaseView];
[TSDatabaseView registerUnreadDatabaseView];
[TSDatabaseView registerUnseenDatabaseView];
[TSDatabaseView registerDynamicMessagesDatabaseView];
[TSDatabaseView registerSafetyNumberChangeDatabaseView];
[self.database registerExtension:[TSDatabaseSecondaryIndexes registerTimeStampIndex] withName:@"idx"];
// Run the blocking migrations.
//
// These need to run _before_ the async registered database views or
// they will block on them, which (in the upgrade case) can block
// return of appDidFinishLaunching... which in term can cause the
// app to crash on launch.
safeBlockingMigrationsBlock();
// Asynchronously register other extensions.
//
// All sync registrations must be done before all async registrations,
// or the sync registrations will block on the async registrations.
[TSDatabaseView asyncRegisterUnseenDatabaseView];
[TSDatabaseView asyncRegisterThreadOutgoingMessagesDatabaseView];
[TSDatabaseView asyncRegisterThreadSpecialMessagesDatabaseView];
// Register extensions which aren't essential for rendering threads async
[[OWSIncomingMessageFinder new] asyncRegisterExtension];
[TSDatabaseView asyncRegisterSecondaryDevicesDatabaseView];
[OWSReadReceipt asyncRegisterIndexOnSenderIdAndTimestampWithDatabase:self.database];
[OWSDisappearingMessagesFinder asyncRegisterDatabaseExtensions:self];
OWSDisappearingMessagesFinder *finder = [[OWSDisappearingMessagesFinder alloc] initWithStorageManager:self];
[finder asyncRegisterDatabaseExtensions];
OWSFailedMessagesJob *failedMessagesJob = [[OWSFailedMessagesJob alloc] initWithStorageManager:self];
[failedMessagesJob asyncRegisterDatabaseExtensions];
OWSFailedAttachmentDownloadsJob *failedAttachmentDownloadsMessagesJob =
[[OWSFailedAttachmentDownloadsJob alloc] initWithStorageManager:self];
[failedAttachmentDownloadsMessagesJob asyncRegisterDatabaseExtensions];
// NOTE: [TSDatabaseView asyncRegistrationCompletion] ensures that
// kNSNotificationName_DatabaseViewRegistrationComplete is not fired until all
// of the async registrations are complete.
[TSDatabaseView asyncRegistrationCompletion];
}
- (void)protectSignalFiles {
@ -389,19 +373,19 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
#pragma mark - convenience methods
- (void)purgeCollection:(NSString *)collection {
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction removeAllObjectsInCollection:collection];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction removeAllObjectsInCollection:collection];
}];
}
- (void)setObject:(id)object forKey:(NSString *)key inCollection:(NSString *)collection {
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction setObject:object forKey:key inCollection:collection];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction setObject:object forKey:key inCollection:collection];
}];
}
- (void)removeObjectForKey:(NSString *)string inCollection:(NSString *)collection {
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction removeObjectForKey:string inCollection:collection];
}];
}
@ -409,8 +393,8 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
- (id)objectForKey:(NSString *)key inCollection:(NSString *)collection {
__block NSString *object;
[self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
object = [transaction objectForKey:key inCollection:collection];
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
object = [transaction objectForKey:key inCollection:collection];
}];
return object;
@ -420,8 +404,8 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
{
__block NSDictionary *object;
[self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
object = [transaction objectForKey:key inCollection:collection];
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
object = [transaction objectForKey:key inCollection:collection];
}];
return object;
@ -480,7 +464,7 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
- (int)incrementIntForKey:(NSString *)key inCollection:(NSString *)collection
{
__block int value = 0;
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
value = [[transaction objectForKey:key inCollection:collection] intValue];
value++;
[transaction setObject:@(value) forKey:key inCollection:collection];
@ -504,11 +488,11 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
}
- (void)deleteThreadsAndMessages {
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction removeAllObjectsInCollection:[TSThread collection]];
[transaction removeAllObjectsInCollection:[SignalRecipient collection]];
[transaction removeAllObjectsInCollection:[TSInteraction collection]];
[transaction removeAllObjectsInCollection:[TSAttachment collection]];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction removeAllObjectsInCollection:[TSThread collection]];
[transaction removeAllObjectsInCollection:[SignalRecipient collection]];
[transaction removeAllObjectsInCollection:[TSInteraction collection]];
[transaction removeAllObjectsInCollection:[TSAttachment collection]];
}];
[TSAttachmentStream deleteAttachments];
}
@ -530,8 +514,7 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
- (void)resetSignalStorage
{
self.database = nil;
_dbReadConnection = nil;
_dbReadWriteConnection = nil;
_dbConnection = nil;
[self deletePasswordFromKeychain];

View File

@ -58,12 +58,10 @@
usingBlock:(void (^)(id object, BOOL *stop))block;
/**
* @return Shared database connections for reading and writing.
* @return A shared database connection.
*/
- (YapDatabaseConnection *)dbReadConnection;
+ (YapDatabaseConnection *)dbReadConnection;
- (YapDatabaseConnection *)dbReadWriteConnection;
+ (YapDatabaseConnection *)dbReadWriteConnection;
- (YapDatabaseConnection *)dbConnection;
+ (YapDatabaseConnection *)dbConnection;
- (TSStorageManager *)storageManager;
+ (TSStorageManager *)storageManager;

View File

@ -1,6 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Created by Frederic Jacobs on 16/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
#import "TSYapDatabaseObject.h"
#import "TSStorageManager.h"
@ -32,7 +31,7 @@
- (void)save
{
[[self dbReadWriteConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[[self dbConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self saveWithTransaction:transaction];
}];
}
@ -44,7 +43,7 @@
- (void)touch
{
[[self dbReadWriteConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[[self dbConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self touchWithTransaction:transaction];
}];
}
@ -56,19 +55,14 @@
- (void)remove
{
[[self dbReadWriteConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[[self dbConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self removeWithTransaction:transaction];
}];
}
- (YapDatabaseConnection *)dbReadConnection
- (YapDatabaseConnection *)dbConnection
{
return [[self class] dbReadConnection];
}
- (YapDatabaseConnection *)dbReadWriteConnection
{
return [[self class] dbReadWriteConnection];
return [[self class] dbConnection];
}
- (TSStorageManager *)storageManager
@ -78,26 +72,9 @@
#pragma mark Class Methods
+ (YapDatabaseConnection *)dbReadConnection
+ (YapDatabaseConnection *)dbConnection
{
// We use TSYapDatabaseObject's dbReadWriteConnection (not TSStorageManager's
// dbReadConnection) for consistency, since we tend to [TSYapDatabaseObject
// save] and want to write to the same connection we read from. To get true
// consistency, we'd want to update entities by reading & writing from within
// the same transaction, but that'll be a big refactor.
return self.dbReadWriteConnection;
}
+ (YapDatabaseConnection *)dbReadWriteConnection
{
// Use a dedicated connection for model reads & writes.
static YapDatabaseConnection *dbReadWriteConnection = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dbReadWriteConnection = [self storageManager].newDatabaseConnection;
});
return dbReadWriteConnection;
return [self storageManager].dbConnection;
}
+ (TSStorageManager *)storageManager
@ -113,7 +90,7 @@
+ (NSUInteger)numberOfKeysInCollection
{
__block NSUInteger count;
[[self dbReadConnection] readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[[self dbConnection] readWithBlock:^(YapDatabaseReadTransaction *transaction) {
count = [self numberOfKeysInCollectionWithTransaction:transaction];
}];
return count;
@ -135,7 +112,7 @@
+ (void)enumerateCollectionObjectsUsingBlock:(void (^)(id object, BOOL *stop))block
{
[[self dbReadConnection] readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[[self dbConnection] readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[self enumerateCollectionObjectsWithTransaction:transaction usingBlock:block];
}];
}
@ -154,7 +131,7 @@
+ (void)removeAllObjectsInCollection
{
[[self dbReadWriteConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[[self dbConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction removeAllObjectsInCollection:[self collection]];
}];
}
@ -167,7 +144,7 @@
+ (instancetype)fetchObjectWithUniqueID:(NSString *)uniqueID
{
__block id object;
[[self dbReadConnection] readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[[self dbConnection] readWithBlock:^(YapDatabaseReadTransaction *transaction) {
object = [transaction objectForKey:uniqueID inCollection:[self collection]];
}];
return object;

View File

@ -8,13 +8,15 @@ NS_ASSUME_NONNULL_BEGIN
@class OWSMessageSender;
@protocol NotificationsProtocol;
@protocol OWSCallMessageHandler;
@protocol TSPreferences;
@interface TextSecureKitEnv : NSObject
- (instancetype)initWithCallMessageHandler:(id<OWSCallMessageHandler>)callMessageHandler
contactsManager:(id<ContactsManagerProtocol>)contactsManager
messageSender:(OWSMessageSender *)messageSender
notificationsManager:(id<NotificationsProtocol>)notificationsManager NS_DESIGNATED_INITIALIZER;
notificationsManager:(id<NotificationsProtocol>)notificationsManager
preferences:(id<TSPreferences>)preferences NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@ -25,6 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
@property (nonatomic, readonly) OWSMessageSender *messageSender;
@property (nonatomic, readonly) id<NotificationsProtocol> notificationsManager;
@property (nonatomic, readonly) id<TSPreferences> preferences;
@end

View File

@ -10,13 +10,17 @@ static TextSecureKitEnv *TextSecureKitEnvSharedInstance;
@implementation TextSecureKitEnv
@synthesize callMessageHandler = _callMessageHandler, contactsManager = _contactsManager,
messageSender = _messageSender, notificationsManager = _notificationsManager;
@synthesize callMessageHandler = _callMessageHandler,
contactsManager = _contactsManager,
messageSender = _messageSender,
notificationsManager = _notificationsManager,
preferences = _preferences;
- (instancetype)initWithCallMessageHandler:(id<OWSCallMessageHandler>)callMessageHandler
contactsManager:(id<ContactsManagerProtocol>)contactsManager
messageSender:(OWSMessageSender *)messageSender
notificationsManager:(id<NotificationsProtocol>)notificationsManager
preferences:(nonnull id<TSPreferences>)preferences
{
self = [super init];
if (!self) {
@ -27,6 +31,7 @@ static TextSecureKitEnv *TextSecureKitEnvSharedInstance;
_contactsManager = contactsManager;
_messageSender = messageSender;
_notificationsManager = notificationsManager;
_preferences = preferences;
return self;
}
@ -71,6 +76,12 @@ static TextSecureKitEnv *TextSecureKitEnvSharedInstance;
return _notificationsManager;
}
- (id<TSPreferences>)preferences
{
NSAssert(_preferences, @"Trying to access preferences before it's set.");
return _preferences;
}
@end
NS_ASSUME_NONNULL_END

Some files were not shown because too many files have changed in this diff Show More