Compare commits

..

1 Commits

Author SHA1 Message Date
Matthew Chen
b9fa48a7b0 BLURG 2017-07-05 19:51:06 -04:00
153 changed files with 1671 additions and 5922 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']
@ -59,7 +37,7 @@ EOS
s.dependency 'AFNetworking'
s.dependency 'AxolotlKit'
s.dependency 'Mantle'
s.dependency 'YapDatabase/SQLCipher', '~> 2.9.3'
s.dependency 'YapDatabase/SQLCipher'
s.dependency 'SocketRocket'
s.dependency 'libPhoneNumber-iOS'
s.dependency 'SAMKeychain'

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

@ -10,7 +10,6 @@ NS_ASSUME_NONNULL_BEGIN
extern NSString *const TSRegistrationErrorDomain;
extern NSString *const TSRegistrationErrorUserInfoHTTPStatus;
extern NSString *const kNSNotificationName_RegistrationStateDidChange;
extern NSString *const kNSNotificationName_LocalNumberDidChange;
@class TSNetworkManager;
@class TSStorageManager;

View File

@ -18,12 +18,11 @@ NS_ASSUME_NONNULL_BEGIN
NSString *const TSRegistrationErrorDomain = @"TSRegistrationErrorDomain";
NSString *const TSRegistrationErrorUserInfoHTTPStatus = @"TSHTTPStatus";
NSString *const kNSNotificationName_RegistrationStateDidChange = @"kNSNotificationName_RegistrationStateDidChange";
NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName_LocalNumberDidChange";
@interface TSAccountManager ()
@property (nonatomic, nullable) NSString *phoneNumberAwaitingVerification;
@property (nonatomic, readonly) TSStorageManager *storageManager;
@property (nullable, nonatomic, retain) NSString *phoneNumberAwaitingVerification;
@property (nonatomic, strong, readonly) TSStorageManager *storageManager;
@end
@ -59,15 +58,6 @@ NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName
return sharedInstance;
}
- (void)setPhoneNumberAwaitingVerification:(NSString *_Nullable)phoneNumberAwaitingVerification
{
_phoneNumberAwaitingVerification = phoneNumberAwaitingVerification;
[[NSNotificationCenter defaultCenter] postNotificationName:kNSNotificationName_LocalNumberDidChange
object:nil
userInfo:nil];
}
+ (BOOL)isRegistered {
return [TSStorageManager localNumber] ? YES : NO;
}

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

@ -0,0 +1,19 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSYapDatabaseObject.h"
NS_ASSUME_NONNULL_BEGIN
@interface TSPrivacyPreferences : TSYapDatabaseObject
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)sharedInstance;
@property BOOL shouldBlockOnIdentityChange;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,48 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSPrivacyPreferences.h"
NS_ASSUME_NONNULL_BEGIN
NSString *const TSPrivacyPreferencesSingletonKey = @"TSPrivacyPreferences";
@implementation TSPrivacyPreferences
+ (instancetype)sharedInstance
{
static TSPrivacyPreferences *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [self fetchObjectWithUniqueID:TSPrivacyPreferencesSingletonKey];
if (!sharedInstance) {
sharedInstance = [[self alloc] initDefault];
}
});
return sharedInstance;
}
- (instancetype)initDefault
{
return [self initWithShouldBlockOnIdentityChange:YES];
}
- (instancetype)initWithShouldBlockOnIdentityChange:(BOOL)shouldBlockOnIdentityChange
{
self = [super initWithUniqueId:TSPrivacyPreferencesSingletonKey];
if (!self) {
return self;
}
_shouldBlockOnIdentityChange = shouldBlockOnIdentityChange;
OWSSingletonAssert();
return self;
}
@end
NS_ASSUME_NONNULL_END

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;
}
@ -164,24 +165,10 @@ static NSString *const RPDefaultsKeyPhoneNumberCanonical = @"RPDefaultsKeyPhoneN
// (ISO 3166-1 alpha-2).
NSNumber *callingCodeForLocalNumber = [[PhoneNumber phoneNumberFromE164:clientPhoneNumber] getCountryCode];
if (callingCodeForLocalNumber != nil) {
NSString *callingCodePrefix = [NSString stringWithFormat:@"+%@", callingCodeForLocalNumber];
tryParsingWithCountryCode(
[callingCodePrefix stringByAppendingString:sanitizedString], [self defaultRegionCode]);
// Try to determine what the country code is for the local phone number
// and also try parsing the phone number using that country code if it
// differs from the device's region code.
//
// For example, a French person living in Italy might have an
// Italian phone number but use French region/language for their
// phone. They're likely to have both Italian and French contacts.
NSString *localCountryCode =
[PhoneNumberUtil.sharedUtil probableCountryCodeForCallingCode:callingCodePrefix];
if (localCountryCode && ![localCountryCode isEqualToString:[self defaultRegionCode]]) {
tryParsingWithCountryCode(
[callingCodePrefix stringByAppendingString:sanitizedString], localCountryCode);
}
tryParsingWithCountryCode([NSString stringWithFormat:@"+%@%@",
callingCodeForLocalNumber,
sanitizedString],
[self defaultRegionCode]);
}
}

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

@ -36,11 +36,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (nullable NSString *)contactIdentifier;
/**
* @returns recipientId for each recipient in the thread
*/
@property (nonatomic, readonly) NSArray<NSString *> *recipientIdentifiers;
#if TARGET_OS_IOS
/**
@ -72,6 +67,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"
@ -85,12 +84,6 @@ NS_ASSUME_NONNULL_BEGIN
return nil;
}
- (NSArray<NSString *> *)recipientIdentifiers
{
NSAssert(FALSE, @"Should be implemented in subclasses");
return @[];
}
- (nullable UIImage *)image
{
return nil;
@ -131,7 +124,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 +165,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];
}];
@ -190,24 +183,6 @@ NS_ASSUME_NONNULL_BEGIN
return hasUnread;
}
- (NSArray<id<OWSReadTracking>> *)unseenMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction
{
NSMutableArray<id<OWSReadTracking>> *messages = [NSMutableArray new];
[[TSDatabaseView unseenDatabaseViewExtension:transaction]
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);
return;
}
[messages addObject:(id<OWSReadTracking>)object];
}];
return [messages copy];
}
- (NSArray<id<OWSReadTracking> > *)unreadMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction
{
NSMutableArray<id<OWSReadTracking> > *messages = [NSMutableArray new];
@ -225,45 +200,36 @@ NS_ASSUME_NONNULL_BEGIN
return [messages copy];
}
- (NSArray<id<OWSReadTracking> > *)unreadMessages
{
__block NSArray<id<OWSReadTracking> > *messages;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
messages = [self unreadMessagesWithTransaction:transaction];
}];
return messages;
}
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
for (id<OWSReadTracking> message in [self unseenMessagesWithTransaction:transaction]) {
[message markAsReadWithTransaction:transaction sendReadReceipt:YES updateExpiration:YES];
for (id<OWSReadTracking> message in [self unreadMessagesWithTransaction:transaction]) {
[message markAsReadLocallyWithTransaction:transaction];
}
}
// Just to be defensive, we'll also check for unread messages.
OWSAssert([self unseenMessagesWithTransaction:transaction].count < 1);
- (void)markAllAsRead
{
for (id<OWSReadTracking> message in [self unreadMessages]) {
[message markAsReadLocally];
}
}
- (TSInteraction *) lastInteraction {
__block TSInteraction *last;
[TSStorageManager.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[TSStorageManager.sharedManager.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction){
last = [[transaction ext:TSMessageDatabaseViewExtensionName] lastObjectInGroup:self.uniqueId];
}];
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;
}
}];
}];
return last;
return (TSInteraction *)last;
}
- (NSDate *)lastMessageDate {
@ -275,49 +241,16 @@ 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 receiptDateForSorting];
if (![self.class shouldInteractionAppearInInbox:lastMessage]) {
return;
}
NSDate *lastMessageDate = [lastMessage dateForSorting];
if (!_lastMessageDate || [lastMessageDate timeIntervalSinceDate:self.lastMessageDate] > 0) {
_lastMessageDate = lastMessageDate;
@ -397,7 +330,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];
}];
@ -87,18 +80,13 @@ NS_ASSUME_NONNULL_BEGIN
return [[self class] contactIdFromThreadId:self.uniqueId];
}
- (NSArray<NSString *> *)recipientIdentifiers
{
return @[self.contactIdentifier];
}
- (BOOL)isGroupThread {
return false;
}
- (BOOL)hasSafetyNumbers
{
return !![[OWSIdentityManager sharedManager] identityKeyForRecipientId:self.contactIdentifier];
return !![self.storageManager identityKeyForRecipientId:self.contactIdentifier];
}
- (NSString *)name

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 "TSGroupModel.h"
#import "TSThread.h"
@ -23,9 +22,6 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSString *)threadIdFromGroupId:(NSData *)groupId;
// all group threads containing recipient as a member
+ (NSArray<TSGroupThread *> *)groupThreadsWithRecipientId:(NSString *)recipientId;
- (void)updateAvatarWithAttachmentStream:(TSAttachmentStream *)attachmentStream;
@end

View File

@ -1,12 +1,10 @@
//
// 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 "TSGroupThread.h"
#import "NSData+Base64.h"
#import "SignalRecipient.h"
#import "TSAttachmentStream.h"
#import <SignalServiceKit/TSAccountManager.h>
#import <YapDatabase/YapDatabaseConnection.h>
#import <YapDatabase/YapDatabaseTransaction.h>
@ -18,13 +16,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 +29,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 +41,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 +56,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 +68,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,57 +77,14 @@ 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)]];
}
- (NSArray<NSString *> *)recipientIdentifiers
{
NSMutableArray<NSString *> *groupMemberIds = [self.groupModel.groupMemberIds mutableCopy];
if (groupMemberIds == nil) {
return @[];
}
[groupMemberIds removeObject:[TSAccountManager localNumber]];
return [groupMemberIds copy];
}
// Group and Contact threads share a collection, this is a convenient way to enumerate *just* the group threads
+ (void)enumerateGroupThreadsUsingBlock:(void (^)(TSGroupThread *groupThread, BOOL *stop))block
{
[self enumerateCollectionObjectsUsingBlock:^(id obj, BOOL *stop) {
if ([obj isKindOfClass:[TSGroupThread class]]) {
block((TSGroupThread *)obj, stop);
}
}];
}
// @returns all threads to which the recipient is a member.
//
// @note If this becomes a hotspot we can extract into a YapDB View.
// As is, the number of groups should be small (dozens, *maybe* hundreds), and we only enumerate them upon SN changes.
+ (NSArray<TSGroupThread *> *)groupThreadsWithRecipientId:(NSString *)recipientId
{
NSMutableArray<TSGroupThread *> *groupThreads = [NSMutableArray new];
[self enumerateGroupThreadsUsingBlock:^(TSGroupThread *_Nonnull groupThread, BOOL *_Nonnull stop) {
if ([groupThread.groupModel.groupMemberIds containsObject:recipientId]) {
[groupThreads addObject:groupThread];
}
}];
return [groupThreads copy];
}
- (BOOL)isGroupThread
{
return true;

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

@ -92,7 +92,7 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
digest:digest
contentType:attachmentProto.contentType
relay:relay
sourceFilename:attachmentProto.fileName
filename:attachmentProto.fileName
attachmentType:attachmentType];
[attachmentIds addObject:pointer.uniqueId];

View File

@ -31,20 +31,20 @@ typedef NS_ENUM(NSUInteger, TSAttachmentType) {
@property (atomic, readwrite) BOOL isDownloaded;
@property (nonatomic) TSAttachmentType attachmentType;
// Represents the "source" filename sent or received in the protos,
// Represents the "nominal" filename sent or received in the protos,
// not the filename on disk.
@property (nonatomic, readonly, nullable) NSString *sourceFilename;
@property (nonatomic, readonly, nullable) NSString *filename;
// This constructor is used for new instances of TSAttachmentPointer,
// i.e. undownloaded incoming attachments.
- (instancetype)initWithServerId:(UInt64)serverId
encryptionKey:(NSData *)encryptionKey
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename;
filename:(nullable NSString *)filename;
// This constructor is used for new instances of TSAttachmentStream
// that represent new, un-uploaded outgoing attachments.
- (instancetype)initWithContentType:(NSString *)contentType sourceFilename:(nullable NSString *)sourceFilename;
- (instancetype)initWithContentType:(NSString *)contentType filename:(nullable NSString *)filename;
// This constructor is used for new instances of TSAttachmentStream
// that represent downloaded incoming attachments.

View File

@ -22,7 +22,7 @@ NSUInteger const TSAttachmentSchemaVersion = 3;
- (instancetype)initWithServerId:(UInt64)serverId
encryptionKey:(NSData *)encryptionKey
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename
filename:(nullable NSString *)filename
{
self = [super init];
if (!self) {
@ -33,14 +33,14 @@ NSUInteger const TSAttachmentSchemaVersion = 3;
_encryptionKey = encryptionKey;
_contentType = contentType;
_attachmentSchemaVersion = TSAttachmentSchemaVersion;
_sourceFilename = sourceFilename;
_filename = filename;
return self;
}
// This constructor is used for new instances of TSAttachmentStream
// that represent new, un-uploaded outgoing attachments.
- (instancetype)initWithContentType:(NSString *)contentType sourceFilename:(nullable NSString *)sourceFilename
- (instancetype)initWithContentType:(NSString *)contentType filename:(nullable NSString *)filename
{
self = [super init];
if (!self) {
@ -49,7 +49,7 @@ NSUInteger const TSAttachmentSchemaVersion = 3;
_contentType = contentType;
_attachmentSchemaVersion = TSAttachmentSchemaVersion;
_sourceFilename = sourceFilename;
_filename = filename;
return self;
}
@ -67,7 +67,7 @@ NSUInteger const TSAttachmentSchemaVersion = 3;
_serverId = pointer.serverId;
_encryptionKey = pointer.encryptionKey;
_contentType = pointer.contentType;
_sourceFilename = pointer.sourceFilename;
_filename = pointer.filename;
_attachmentSchemaVersion = TSAttachmentSchemaVersion;
return self;
@ -85,12 +85,6 @@ NSUInteger const TSAttachmentSchemaVersion = 3;
_attachmentSchemaVersion = TSAttachmentSchemaVersion;
}
if (!_sourceFilename) {
// renamed _filename to _sourceFilename
_sourceFilename = [coder decodeObjectForKey:@"filename"];
OWSAssert(!_sourceFilename || [_sourceFilename isKindOfClass:[NSString class]]);
}
return self;
}
@ -113,9 +107,9 @@ NSUInteger const TSAttachmentSchemaVersion = 3;
return [NSString stringWithFormat:@"📽 %@", attachmentString];
} else if ([MIMETypeUtil isAudio:self.contentType]) {
// a missing filename is the legacy way to determine if an audio attachment is
// a voice note vs. other arbitrary audio attachments.
if (self.isVoiceMessage || !self.sourceFilename || self.sourceFilename.length == 0) {
// a missing filename is the legacy way to determin if an audio attachment is a voice note vs. other arbitrary
// audio attachments.
if (self.isVoiceMessage || !self.filename || self.filename.length == 0) {
attachmentString = NSLocalizedString(@"ATTACHMENT_TYPE_VOICE_MESSAGE",
@"Short text label for a voice message attachment, used for thread preview and on lockscreen");
return [NSString stringWithFormat:@"🎤 %@", attachmentString];

View File

@ -24,7 +24,7 @@ typedef NS_ENUM(NSUInteger, TSAttachmentPointerState) {
digest:(nullable NSData *)digest
contentType:(NSString *)contentType
relay:(NSString *)relay
sourceFilename:(nullable NSString *)sourceFilename
filename:(nullable NSString *)filename
attachmentType:(TSAttachmentType)attachmentType NS_DESIGNATED_INITIALIZER;
@property (nonatomic, readonly) NSString *relay;

View File

@ -30,10 +30,10 @@ NS_ASSUME_NONNULL_BEGIN
digest:(nullable NSData *)digest
contentType:(NSString *)contentType
relay:(NSString *)relay
sourceFilename:(nullable NSString *)sourceFilename
filename:(nullable NSString *)filename
attachmentType:(TSAttachmentType)attachmentType
{
self = [super initWithServerId:serverId encryptionKey:key contentType:contentType sourceFilename:sourceFilename];
self = [super initWithServerId:serverId encryptionKey:key contentType:contentType filename:filename];
if (!self) {
return self;
}

View File

@ -10,15 +10,12 @@
NS_ASSUME_NONNULL_BEGIN
@class TSAttachmentPointer;
@class YapDatabaseReadWriteTransaction;
@interface TSAttachmentStream : TSAttachment
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithContentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithContentType:(NSString *)contentType filename:(NSString *)filename NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithPointer:(TSAttachmentPointer *)pointer NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
// Though now required, `digest` may be null for pre-existing records or from
// messages received from other clients
@ -27,8 +24,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
@ -37,21 +32,14 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)isImage;
- (BOOL)isVideo;
- (BOOL)isAudio;
- (nullable NSURL *)mediaURL;
- (nullable NSString *)filePath;
- (nullable NSURL *)mediaURL;
- (nullable NSData *)readDataFromFileWithError:(NSError **)error;
- (BOOL)writeData:(NSData *)data error:(NSError **)error;
+ (void)deleteAttachments;
+ (NSString *)attachmentsFolder;
- (CGSize)imageSizeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
- (CGSize)imageSizeWithoutTransaction;
- (CGFloat)audioDurationSecondsWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
- (CGFloat)audioDurationSecondsWithoutTransaction;
+ (NSUInteger)numberOfItemsInAttachmentsFolder;
@end

View File

@ -6,32 +6,15 @@
#import "MIMETypeUtil.h"
#import "TSAttachmentPointer.h"
#import <AVFoundation/AVFoundation.h>
#import <ImageIO/ImageIO.h>
#import <YapDatabase/YapDatabase.h>
#import <YapDatabase/YapDatabaseTransaction.h>
NS_ASSUME_NONNULL_BEGIN
@interface TSAttachmentStream ()
// We only want to generate the file path for this attachment once, so that
// changes in the file path generation logic don't break existing attachments.
@property (nullable, nonatomic) NSString *localRelativeFilePath;
// These properties should only be accessed on the main thread.
@property (nullable, nonatomic) NSNumber *cachedImageWidth;
@property (nullable, nonatomic) NSNumber *cachedImageHeight;
@property (nullable, nonatomic) NSNumber *cachedAudioDurationSeconds;
@end
#pragma mark -
@implementation TSAttachmentStream
- (instancetype)initWithContentType:(NSString *)contentType sourceFilename:(nullable NSString *)sourceFilename
- (instancetype)initWithContentType:(NSString *)contentType filename:(NSString *)filename
{
self = [super initWithContentType:contentType sourceFilename:sourceFilename];
self = [super initWithContentType:contentType filename:filename];
if (!self) {
return self;
}
@ -41,9 +24,6 @@ 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];
return self;
}
@ -63,27 +43,6 @@ NS_ASSUME_NONNULL_BEGIN
// attachments which don't need to be uploaded.
_isUploaded = YES;
self.attachmentType = pointer.attachmentType;
_creationTimestamp = [NSDate new];
[self ensureFilePath];
return self;
}
- (nullable instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (!self) {
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];
}
return self;
}
@ -101,137 +60,82 @@ NS_ASSUME_NONNULL_BEGIN
}
}
- (void)ensureFilePath
#pragma mark - TSYapDatabaseModel overrides
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
if (self.localRelativeFilePath) {
return;
}
NSString *attachmentsFolder = [[self class] attachmentsFolder];
NSString *filePath = [MIMETypeUtil filePathForAttachment:self.uniqueId
ofMIMEType:self.contentType
sourceFilename:self.sourceFilename
inFolder:attachmentsFolder];
if (!filePath) {
DDLogError(@"%@ Could not generate path for attachment.", self.tag);
OWSAssert(0);
return;
}
if (![filePath hasPrefix:attachmentsFolder]) {
DDLogError(@"%@ Attachment paths should all be in the attachments folder.", self.tag);
OWSAssert(0);
return;
}
NSString *localRelativeFilePath = [filePath substringFromIndex:attachmentsFolder.length];
if (localRelativeFilePath.length < 1) {
DDLogError(@"%@ Empty local relative attachment paths.", self.tag);
OWSAssert(0);
return;
}
self.localRelativeFilePath = localRelativeFilePath;
OWSAssert(self.filePath);
[super removeWithTransaction:transaction];
[self removeFile];
}
#pragma mark - File Management
- (nullable NSData *)readDataFromFileWithError:(NSError **)error
{
*error = nil;
NSString *_Nullable filePath = self.filePath;
if (!filePath) {
DDLogError(@"%@ Missing path for attachment.", self.tag);
OWSAssert(0);
return nil;
}
return [NSData dataWithContentsOfFile:filePath options:0 error:error];
return [NSData dataWithContentsOfFile:self.filePath options:0 error:error];
}
- (BOOL)writeData:(NSData *)data error:(NSError **)error
{
*error = nil;
NSString *_Nullable filePath = self.filePath;
if (!filePath) {
DDLogError(@"%@ Missing path for attachment.", self.tag);
OWSAssert(0);
return NO;
}
DDLogInfo(@"%@ Writing attachment to file: %@", self.tag, filePath);
return [data writeToFile:filePath options:0 error:error];
DDLogInfo(@"%@ Created file at %@", self.tag, self.filePath);
return [data writeToFile:self.filePath options:0 error:error];
}
+ (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
{
if (!self.localRelativeFilePath) {
OWSAssert(0);
return nil;
}
return [[[self class] attachmentsFolder] stringByAppendingPathComponent:self.localRelativeFilePath];
return [MIMETypeUtil filePathForAttachment:self.uniqueId
ofMIMEType:self.contentType
filename:self.filename
inFolder:[[self class] attachmentsFolder]];
}
- (nullable NSURL *)mediaURL
{
NSString *_Nullable filePath = self.filePath;
if (!filePath) {
DDLogError(@"%@ Missing path for attachment.", self.tag);
OWSAssert(0);
return nil;
}
return [NSURL fileURLWithPath:filePath];
NSString *filePath = self.filePath;
return filePath ? [NSURL fileURLWithPath:filePath] : nil;
}
- (void)removeFileWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
- (void)removeFile
{
NSString *_Nullable filePath = self.filePath;
if (!filePath) {
DDLogError(@"%@ Missing path for attachment.", self.tag);
OWSAssert(0);
return;
}
NSError *error;
[[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
[[NSFileManager defaultManager] removeItemAtPath:[self filePath] error:&error];
if (error) {
DDLogError(@"%@ remove file errored with: %@", self.tag, error);
}
}
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
[super removeWithTransaction:transaction];
[self removeFileWithTransaction:transaction];
}
- (BOOL)isAnimated {
return [MIMETypeUtil isAnimated:self.contentType];
}
@ -250,26 +154,17 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable UIImage *)image
{
if ([self isVideo]) {
if ([self isVideo] || [self isAudio]) {
return [self videoThumbnail];
} else if ([self isImage] || [self isAnimated]) {
NSURL *_Nullable mediaUrl = [self mediaURL];
if (!mediaUrl) {
return nil;
}
return [UIImage imageWithData:[NSData dataWithContentsOfURL:mediaUrl]];
} else {
return nil;
// [self isAnimated] || [self isImage]
return [UIImage imageWithData:[NSData dataWithContentsOfURL:[self mediaURL]]];
}
}
- (nullable UIImage *)videoThumbnail
{
NSURL *_Nullable mediaUrl = [self mediaURL];
if (!mediaUrl) {
return nil;
}
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:mediaUrl options:nil];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:self.filePath] options:nil];
AVAssetImageGenerator *generate = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generate.appliesPreferredTrackTransform = YES;
NSError *err = NULL;
@ -281,191 +176,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
{
if ([self isVideo]) {
return [self videoThumbnail].size;
} else if ([self isImage] || [self isAnimated]) {
NSURL *_Nullable mediaUrl = [self mediaURL];
if (!mediaUrl) {
return CGSizeZero;
}
// With CGImageSource we avoid loading the whole image into memory.
CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)mediaUrl, NULL);
if (!source) {
OWSAssert(0);
return CGSizeZero;
}
NSDictionary *options = @{
(NSString *)kCGImageSourceShouldCache : @(NO),
};
NSDictionary *properties
= (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(source, 0, (CFDictionaryRef)options);
CGSize imageSize = CGSizeZero;
if (properties) {
NSNumber *width = properties[(NSString *)kCGImagePropertyPixelWidth];
NSNumber *height = properties[(NSString *)kCGImagePropertyPixelHeight];
if (width && height) {
imageSize = CGSizeMake(width.floatValue, height.floatValue);
} else {
OWSAssert(0);
}
}
CFRelease(source);
return imageSize;
} else {
return CGSizeZero;
}
}
- (CGSize)ensureCachedImageSizeWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction
{
OWSAssert([NSThread isMainThread]);
if (self.cachedImageWidth && self.cachedImageHeight) {
return CGSizeMake(self.cachedImageWidth.floatValue, self.cachedImageHeight.floatValue);
}
CGSize imageSize = [self calculateImageSize];
self.cachedImageWidth = @(imageSize.width);
self.cachedImageHeight = @(imageSize.height);
void (^updateDataStore)() = ^(YapDatabaseReadWriteTransaction *transaction) {
OWSAssert(transaction);
NSString *collection = [[self class] collection];
TSAttachmentStream *latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection];
if (latestInstance) {
latestInstance.cachedImageWidth = @(imageSize.width);
latestInstance.cachedImageHeight = @(imageSize.height);
[latestInstance saveWithTransaction:transaction];
} else {
// This message has not yet been saved; do nothing.
OWSAssert(0);
}
};
if (transaction) {
updateDataStore(transaction);
} else {
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
updateDataStore(transaction);
}];
}
return imageSize;
}
- (CGSize)imageSizeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert([NSThread isMainThread]);
OWSAssert(transaction);
return [self ensureCachedImageSizeWithTransaction:transaction];
}
- (CGSize)imageSizeWithoutTransaction
{
OWSAssert([NSThread isMainThread]);
return [self ensureCachedImageSizeWithTransaction:nil];
}
- (CGFloat)calculateAudioDurationSeconds
{
OWSAssert([NSThread isMainThread]);
OWSAssert([self isAudio]);
NSError *error;
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:self.mediaURL error:&error];
if (error && [error.domain isEqualToString:NSOSStatusErrorDomain]
&& (error.code == kAudioFileInvalidFileError || error.code == kAudioFileStreamError_InvalidFile)) {
// Ignore "invalid audio file" errors.
return 0.f;
}
OWSAssert(!error);
if (!error) {
return (CGFloat)[audioPlayer duration];
} else {
return 0;
}
}
- (CGFloat)ensureCachedAudioDurationSecondsWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction
{
OWSAssert([NSThread isMainThread]);
if (self.cachedAudioDurationSeconds) {
return self.cachedAudioDurationSeconds.floatValue;
}
CGFloat audioDurationSeconds = [self calculateAudioDurationSeconds];
self.cachedAudioDurationSeconds = @(audioDurationSeconds);
void (^updateDataStore)() = ^(YapDatabaseReadWriteTransaction *transaction) {
OWSAssert(transaction);
NSString *collection = [[self class] collection];
TSAttachmentStream *latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection];
if (latestInstance) {
latestInstance.cachedAudioDurationSeconds = @(audioDurationSeconds);
[latestInstance saveWithTransaction:transaction];
} else {
// This message has not yet been saved; do nothing.
OWSAssert(0);
}
};
if (transaction) {
updateDataStore(transaction);
} else {
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
updateDataStore(transaction);
}];
}
return audioDurationSeconds;
}
- (CGFloat)audioDurationSecondsWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert([NSThread isMainThread]);
OWSAssert(transaction);
return [self ensureCachedAudioDurationSecondsWithTransaction:transaction];
}
- (CGFloat)audioDurationSecondsWithoutTransaction
{
OWSAssert([NSThread isMainThread]);
return [self ensureCachedAudioDurationSecondsWithTransaction:nil];
}
#pragma mark - Logging

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

@ -2,42 +2,31 @@
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSReadTracking.h"
#import "OWSSignalServiceProtos.pb.h"
#import "TSMessage.h"
NS_ASSUME_NONNULL_BEGIN
@interface TSErrorMessage : TSMessage <OWSReadTracking>
@interface TSErrorMessage : TSMessage
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;
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(TSThread *)thread
failedMessageType:(TSErrorMessageType)errorMessageType NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(TSThread *)thread
failedMessageType:(TSErrorMessageType)errorMessageType
recipientId:(nullable NSString *)recipientId NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(TSThread *)thread
failedMessageType:(TSErrorMessageType)errorMessageType;
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(nullable TSThread *)thread
messageBody:(nullable NSString *)body
messageBody:(NSString *)body
attachmentIds:(NSArray<NSString *> *)attachmentIds
expiresInSeconds:(uint32_t)expiresInSeconds
expireStartedAt:(uint64_t)expireStartedAt NS_UNAVAILABLE;
@ -54,11 +43,6 @@ typedef NS_ENUM(int32_t, TSErrorMessageType) {
+ (instancetype)missingSessionWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
withTransaction:(YapDatabaseReadWriteTransaction *)transaction;
+ (instancetype)nonblockingIdentityChangeInThread:(TSThread *)thread recipientId:(NSString *)recipientId;
@property (nonatomic, readonly) TSErrorMessageType errorType;
@property (nullable, nonatomic, readonly) NSString *recipientId;
@end
NS_ASSUME_NONNULL_END

View File

@ -3,57 +3,22 @@
//
#import "TSErrorMessage.h"
#import "ContactsManagerProtocol.h"
#import "NSDate+millisecondTimeStamp.h"
#import "NotificationsProtocol.h"
#import "TSContactThread.h"
#import "TSErrorMessage_privateConstructor.h"
#import "TSMessagesManager.h"
#import "TextSecureKitEnv.h"
#import <YapDatabase/YapDatabaseConnection.h>
NS_ASSUME_NONNULL_BEGIN
NSUInteger TSErrorMessageSchemaVersion = 1;
@interface TSErrorMessage ()
@property (nonatomic, getter=wasRead) BOOL read;
@property (nonatomic, readonly) NSUInteger errorMessageSchemaVersion;
@end
#pragma mark -
@implementation TSErrorMessage
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (!self) {
return self;
}
if (self.errorMessageSchemaVersion < 1) {
_read = YES;
}
_errorMessageSchemaVersion = TSErrorMessageSchemaVersion;
return self;
return [super initWithCoder:coder];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(TSThread *)thread
failedMessageType:(TSErrorMessageType)errorMessageType
{
return [self initWithTimestamp:timestamp inThread:thread failedMessageType:errorMessageType recipientId:nil];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(TSThread *)thread
failedMessageType:(TSErrorMessageType)errorMessageType
recipientId:(nullable NSString *)recipientId
{
self = [super initWithTimestamp:timestamp
inThread:thread
@ -67,8 +32,14 @@ NSUInteger TSErrorMessageSchemaVersion = 1;
}
_errorType = errorMessageType;
_recipientId = recipientId;
_errorMessageSchemaVersion = TSErrorMessageSchemaVersion;
// TODO: Move this out of model class.
//
// For now, dispatch async to ensure we're not inside a transaction
// and thereby avoid deadlock.
TSErrorMessage *errorMessage = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:thread];
});
return self;
}
@ -97,27 +68,10 @@ NSUInteger TSErrorMessageSchemaVersion = 1;
return NSLocalizedString(@"ERROR_MESSAGE_INVALID_KEY_EXCEPTION", @"");
case TSErrorMessageWrongTrustedIdentityKey:
return NSLocalizedString(@"ERROR_MESSAGE_WRONG_TRUSTED_IDENTITY_KEY", @"");
case TSErrorMessageNonBlockingIdentityChange: {
if (self.recipientId) {
NSString *messageFormat = NSLocalizedString(@"ERROR_MESSAGE_NON_BLOCKING_IDENTITY_CHANGE_FORMAT",
@"Shown when signal users safety numbers changed, embeds the user's {{name or phone number}}");
NSString *recipientDisplayName =
[[TextSecureKitEnv sharedEnv].contactsManager displayNameForPhoneIdentifier:self.recipientId];
return [NSString stringWithFormat:messageFormat, recipientDisplayName];
} else {
// recipientId will be nil for legacy errors
return NSLocalizedString(
@"ERROR_MESSAGE_NON_BLOCKING_IDENTITY_CHANGE", @"Shown when signal users safety numbers changed");
}
break;
}
case TSErrorMessageNonBlockingIdentityChange:
return NSLocalizedString(@"ERROR_MESSAGE_NON_BLOCKING_IDENTITY_CHANGE", @"");
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.");
return NSLocalizedString(@"UNKNOWN_CONTACT_BLOCK_OFFER", nil);
default:
return NSLocalizedString(@"ERROR_MESSAGE_UNKNOWN_ERROR", @"");
break;
@ -155,51 +109,4 @@ NSUInteger TSErrorMessageSchemaVersion = 1;
[[self alloc] initWithEnvelope:envelope withTransaction:transaction failedMessageType:TSErrorMessageNoSession];
}
+ (instancetype)nonblockingIdentityChangeInThread:(TSThread *)thread recipientId:(NSString *)recipientId
{
return [[self alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
inThread:thread
failedMessageType:TSErrorMessageNonBlockingIdentityChange
recipientId:recipientId];
}
#pragma mark - OWSReadTracking
- (BOOL)shouldAffectUnreadCounts
{
return NO;
}
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration
{
OWSAssert(transaction);
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 error messages.
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -107,6 +107,21 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification;
// This will be 0 for messages created before we were tracking sourceDeviceId
@property (nonatomic, readonly) UInt32 sourceDeviceId;
@property (nonatomic, readonly, getter=wasRead) BOOL read;
/*
* 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

View File

@ -3,8 +3,6 @@
//
#import "TSIncomingMessage.h"
#import "OWSDisappearingMessagesConfiguration.h"
#import "OWSDisappearingMessagesJob.h"
#import "TSContactThread.h"
#import "TSDatabaseSecondaryIndexes.h"
#import "TSGroupThread.h"
@ -14,24 +12,11 @@ NS_ASSUME_NONNULL_BEGIN
NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingMessageWasReadOnThisDeviceNotification";
@interface TSIncomingMessage ()
@property (nonatomic, getter=wasRead) BOOL read;
@end
#pragma mark -
@implementation TSIncomingMessage
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (!self) {
return self;
}
return self;
return [super initWithCoder:coder];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
@ -72,13 +57,15 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM
_sourceDeviceId = sourceDeviceId;
_read = NO;
OWSAssert(self.receivedAtDate);
return self;
}
+ (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
@ -110,40 +97,37 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM
return foundMessage;
}
#pragma mark - OWSReadTracking
- (BOOL)shouldAffectUnreadCounts
- (void)markAsReadFromReadReceipt
{
return YES;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self markAsReadWithoutNotificationWithTransaction:transaction];
}];
}
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
[self markAsReadWithoutNotificationWithTransaction:transaction];
[[NSNotificationCenter defaultCenter] postNotificationName:TSIncomingMessageWasReadOnThisDeviceNotification
object:self];
}
if (_read) {
return;
}
- (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];
}
DDLogDebug(@"%@ marking as read uniqueId: %@ which has timestamp: %llu", self.tag, self.uniqueId, self.timestamp);
- (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

@ -2,23 +2,19 @@
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSReadTracking.h"
#import "TSMessage.h"
NS_ASSUME_NONNULL_BEGIN
@interface TSInfoMessage : TSMessage <OWSReadTracking>
@interface TSInfoMessage : TSMessage
typedef NS_ENUM(NSInteger, TSInfoMessageType) {
TSInfoMessageTypeSessionDidEnd,
TSInfoMessageUserNotRegistered,
// TSInfoMessageTypeUnsupportedMessage appears to be obsolete.
TSInfoMessageTypeUnsupportedMessage,
TSInfoMessageTypeGroupUpdate,
TSInfoMessageTypeGroupQuit,
TSInfoMessageTypeDisappearingMessagesUpdate,
TSInfoMessageAddToContactsOffer,
TSInfoMessageVerificationStateChange,
TSInfoMessageTypeDisappearingMessagesUpdate
};
+ (instancetype)userNotRegisteredMessageInThread:(TSThread *)thread;

View File

@ -2,40 +2,16 @@
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSInfoMessage.h"
#import "NSDate+millisecondTimeStamp.h"
#import <YapDatabase/YapDatabaseConnection.h>
#import "TSInfoMessage.h"
NS_ASSUME_NONNULL_BEGIN
NSUInteger TSInfoMessageSchemaVersion = 1;
@interface TSInfoMessage ()
@property (nonatomic, getter=wasRead) BOOL read;
@property (nonatomic, readonly) NSUInteger infoMessageSchemaVersion;
@end
#pragma mark -
@implementation TSInfoMessage
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (!self) {
return self;
}
if (self.infoMessageSchemaVersion < 1) {
_read = YES;
}
_infoMessageSchemaVersion = TSInfoMessageSchemaVersion;
return self;
return [super initWithCoder:coder];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
@ -54,7 +30,6 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
}
_messageType = infoMessage;
_infoMessageSchemaVersion = TSInfoMessageSchemaVersion;
return self;
}
@ -89,12 +64,6 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
return NSLocalizedString(@"GROUP_YOU_LEFT", nil);
case TSInfoMessageTypeGroupUpdate:
return _customMessage != nil ? _customMessage : NSLocalizedString(@"GROUP_UPDATED", nil);
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;
}
@ -102,43 +71,6 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
return @"Unknown Info Message Type";
}
#pragma mark - OWSReadTracking
- (BOOL)shouldAffectUnreadCounts
{
return NO;
}
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration
{
OWSAssert(transaction);
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 info messages.
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) TSThread *thread;
@property (nonatomic, readonly) uint64_t timestamp;
- (NSDate *)date;
- (NSString *)description;
/**
@ -32,17 +33,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)interactionForTimestamp:(uint64_t)timestamp
withTransaction:(YapDatabaseReadWriteTransaction *)transaction;
- (NSDate *)dateForSorting;
- (uint64_t)timestampForSorting;
- (NSComparisonResult)compareForSorting:(TSInteraction *)other;
// "Dynamic" interactions are not messages or static events (like
// info messages, error messages, etc.). They are interactions
// created, updated and deleted by the views.
//
// These include block offers, "add to contact" offers,
// unseen message indicators, etc.
- (BOOL)isDynamicInteraction;
- (nullable NSDate *)receiptDateForSorting;
@end

View File

@ -3,7 +3,6 @@
//
#import "TSInteraction.h"
#import "NSDate+millisecondTimeStamp.h"
#import "TSDatabaseSecondaryIndexes.h"
#import "TSStorageManager+messageIDs.h"
#import "TSThread.h"
@ -53,6 +52,7 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
#pragma mark Thread
- (TSThread *)thread
@ -72,6 +72,11 @@ NS_ASSUME_NONNULL_BEGIN
return self.timestamp;
}
- (NSDate *)date {
uint64_t seconds = self.timestamp / 1000;
return [NSDate dateWithTimeIntervalSince1970:seconds];
}
+ (NSString *)stringFromTimeStamp:(uint64_t)timestamp {
return [[NSNumber numberWithUnsignedLongLong:timestamp] stringValue];
}
@ -83,30 +88,9 @@ NS_ASSUME_NONNULL_BEGIN
return [myNumber unsignedLongLongValue];
}
- (NSDate *)dateForSorting
- (nullable NSDate *)receiptDateForSorting
{
return [NSDate ows_dateWithMillisecondsSince1970:self.timestampForSorting];
}
- (uint64_t)timestampForSorting
{
return self.timestamp;
}
- (NSComparisonResult)compareForSorting:(TSInteraction *)other
{
OWSAssert(other);
uint64_t timestamp1 = self.timestampForSorting;
uint64_t timestamp2 = other.timestampForSorting;
if (timestamp1 > timestamp2) {
return NSOrderedDescending;
} else if (timestamp1 < timestamp2) {
return NSOrderedAscending;
} else {
return NSOrderedSame;
}
return self.date;
}
- (NSString *)description {
@ -125,11 +109,6 @@ NS_ASSUME_NONNULL_BEGIN
[fetchedThread updateWithLastMessage:self transaction:transaction];
}
- (BOOL)isDynamicInteraction
{
return NO;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -21,6 +21,9 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) uint64_t expiresAt;
@property (nonatomic, readonly) BOOL isExpiringMessage;
@property (nonatomic, readonly) BOOL shouldStartExpireTimer;
// _DO NOT_ access this property directly. You almost certainly
// want to use receiptDateForSorting instead.
@property (nonatomic, readonly) NSDate *receivedAtDate;
- (instancetype)initWithTimestamp:(uint64_t)timestamp;

View File

@ -35,13 +35,6 @@ static const NSUInteger OWSMessageSchemaVersion = 3;
*/
@property (nonatomic, readonly) NSUInteger schemaVersion;
// The timestamp property is populated by the envelope,
// which is created by the sender.
//
// We typically want to order messages locally by when
// they were received & decrypted, not by when they were sent.
@property (nonatomic, readonly) uint64_t receivedAtTimestamp;
@end
#pragma mark -
@ -111,7 +104,7 @@ static const NSUInteger OWSMessageSchemaVersion = 3;
_expiresInSeconds = expiresInSeconds;
_expireStartedAt = expireStartedAt;
[self updateExpiresAt];
_receivedAtTimestamp = [NSDate ows_millisecondTimeStamp];
_receivedAtDate = [NSDate date];
return self;
}
@ -140,16 +133,10 @@ static const NSUInteger OWSMessageSchemaVersion = 3;
_attachmentIds = [NSMutableArray new];
}
if (_receivedAtTimestamp == 0) {
// Upgrade from the older "receivedAtDate" and "receivedAt" properties if
// necessary.
NSDate *receivedAtDate = [coder decodeObjectForKey:@"receivedAtDate"];
if (!receivedAtDate) {
receivedAtDate = [coder decodeObjectForKey:@"receivedAt"];
}
if (receivedAtDate) {
_receivedAtTimestamp = [NSDate ows_millisecondsSince1970ForDate:receivedAtDate];
}
if (!_receivedAtDate) {
// TSIncomingMessage.receivedAt has been superceded by TSMessage.receivedAtDate.
NSDate *receivedAt = [coder decodeObjectForKey:@"receivedAt"];
_receivedAtDate = receivedAt;
}
_schemaVersion = OWSMessageSchemaVersion;
@ -237,19 +224,10 @@ static const NSUInteger OWSMessageSchemaVersion = 3;
return self.expiresInSeconds > 0;
}
- (uint64_t)timestampForSorting
- (nullable NSDate *)receiptDateForSorting
{
if ([self shouldUseReceiptDateForSorting] && self.receivedAtTimestamp > 0) {
return self.receivedAtTimestamp;
} else {
OWSAssert(self.timestamp > 0);
return self.timestamp;
}
}
- (BOOL)shouldUseReceiptDateForSorting
{
return YES;
// Prefer receivedAtDate if set, otherwise fallback to date.
return self.receivedAtDate ?: self.date;
}
#pragma mark - Logging

View File

@ -89,7 +89,7 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
@property (atomic, readonly) BOOL hasSyncedTranscript;
@property (atomic, readonly) NSString *customMessage;
@property (atomic, readonly) NSString *mostRecentFailureText;
// A map of attachment id-to-"source" filename.
// A map of attachment id-to-filename.
@property (nonatomic, readonly) NSMutableDictionary<NSString *, NSString *> *attachmentFilenameMap;
@property (atomic, readonly) TSGroupMetaMessage groupMetaMessage;
@ -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;
/**

View File

@ -186,6 +186,8 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
_attachmentFilenameMap = [NSMutableDictionary new];
OWSAssert(self.receivedAtDate);
return self;
}
@ -245,7 +247,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];
@ -254,9 +256,19 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
}];
}
//- (void)updateWithMessageState:(TSOutgoingMessageState)messageState
//{
// [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
// [self applyChangeToSelfAndLatestOutgoingMessage:transaction
// changeBlock:^(TSOutgoingMessage *message) {
// [message setMessageState:messageState];
// }];
// }];
//}
- (void)updateWithMessageState:(TSOutgoingMessageState)messageState
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self updateWithMessageState:messageState transaction:transaction];
}];
}
@ -265,7 +277,7 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setMessageState:messageState];
@ -274,7 +286,7 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
- (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 +307,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 +324,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 +409,7 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
- (void)updateWithSentRecipient:(NSString *)contactId
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self updateWithSentRecipient:contactId transaction:transaction];
}];
}
@ -441,8 +453,8 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
if (!attachmentWasGroupAvatar) {
NSMutableArray *attachments = [NSMutableArray new];
for (NSString *attachmentId in self.attachmentIds) {
NSString *sourceFilename = self.attachmentFilenameMap[attachmentId];
[attachments addObject:[self buildAttachmentProtoForAttachmentId:attachmentId filename:sourceFilename]];
NSString *filename = self.attachmentFilenameMap[attachmentId];
[attachments addObject:[self buildAttachmentProtoForAttachmentId:attachmentId filename:filename]];
}
[builder setAttachmentsArray:attachments];
}
@ -455,12 +467,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

@ -1,5 +1,9 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// TSInvalidIdentityKeyErrorMessage.m
// Signal
//
// Created by Frederic Jacobs on 15/02/15.
// Copyright (c) 2015 Open Whisper Systems. All rights reserved.
//
#import "TSInvalidIdentityKeyErrorMessage.h"

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,7 @@ 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];
dispatch_async(dispatch_get_main_queue(), ^{
// Decrypt this and any old messages for the newly accepted key

View File

@ -1,5 +1,9 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// TSInvalidIdentityKeySendingErrorMessage.h
// Signal
//
// Created by Frederic Jacobs on 15/02/15.
// Copyright (c) 2015 Open Whisper Systems. All rights reserved.
//
#import "TSInvalidIdentityKeyErrorMessage.h"
@ -15,6 +19,12 @@ extern NSString *TSInvalidRecipientKey;
@interface TSInvalidIdentityKeySendingErrorMessage : TSInvalidIdentityKeyErrorMessage
+ (instancetype)untrustedKeyWithOutgoingMessage:(TSOutgoingMessage *)outgoingMessage
inThread:(TSThread *)thread
forRecipient:(NSString *)recipientId
preKeyBundle:(PreKeyBundle *)preKeyBundle;
@property (nonatomic, readonly) NSString *recipientId;
@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
@ -32,26 +32,34 @@ NSString *TSInvalidRecipientKey = @"TSInvalidRecipientKey";
{
self = [super initWithTimestamp:message.timestamp
inThread:thread
failedMessageType:TSErrorMessageWrongTrustedIdentityKey
recipientId:recipientId];
failedMessageType:TSErrorMessageWrongTrustedIdentityKey];
if (self) {
_messageId = message.uniqueId;
_preKeyBundle = preKeyBundle;
_recipientId = recipientId;
}
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];
});
}

View File

@ -1,17 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSInfoMessage.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSAddToContactsOfferMessage : TSInfoMessage
+ (instancetype)addToContactsOfferMessage:(uint64_t)timestamp thread:(TSThread *)thread contactId:(NSString *)contactId;
@property (nonatomic, readonly) NSString *contactId;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,49 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSAddToContactsOfferMessage.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSAddToContactsOfferMessage ()
@property (nonatomic) NSString *contactId;
@end
#pragma mark -
@implementation OWSAddToContactsOfferMessage
+ (instancetype)addToContactsOfferMessage:(uint64_t)timestamp thread:(TSThread *)thread contactId:(NSString *)contactId
{
return [[OWSAddToContactsOfferMessage alloc] initWithTimestamp:timestamp thread:thread contactId:contactId];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread contactId:(NSString *)contactId
{
self = [super initWithTimestamp:timestamp inThread:thread messageType:TSInfoMessageAddToContactsOffer];
if (self) {
_contactId = contactId;
}
return self;
}
- (BOOL)shouldUseReceiptDateForSorting
{
// Use the timestamp, not the "received at" timestamp to sort,
// since we're creating these interactions after the fact and back-dating them.
return NO;
}
- (BOOL)isDynamicInteraction
{
return YES;
}
@end
NS_ASSUME_NONNULL_END

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

@ -71,7 +71,7 @@ NS_SWIFT_NAME(MessageSender)
*/
- (void)sendAttachmentData:(NSData *)attachmentData
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename
filename:(nullable NSString *)filename
inMessage:(TSOutgoingMessage *)outgoingMessage
success:(void (^)())successHandler
failure:(void (^)(NSError *error))failureHandler;
@ -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();
}
@ -509,7 +493,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[self sendAttachmentData:attachmentData
contentType:contentType
sourceFilename:nil
filename:nil
inMessage:message
success:successWithDeleteHandler
failure:failureWithDeleteHandler];
@ -517,7 +501,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
- (void)sendAttachmentData:(NSData *)data
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename
filename:(nullable NSString *)filename
inMessage:(TSOutgoingMessage *)message
success:(void (^)())successHandler
failure:(void (^)(NSError *error))failureHandler
@ -531,7 +515,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
dispatch_async([OWSDispatch attachmentsQueue], ^{
TSAttachmentStream *attachmentStream =
[[TSAttachmentStream alloc] initWithContentType:contentType sourceFilename:sourceFilename];
[[TSAttachmentStream alloc] initWithContentType:contentType filename:filename];
if (message.isVoiceMessage) {
attachmentStream.attachmentType = TSAttachmentTypeVoiceMessage;
}
@ -545,8 +529,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[attachmentStream save];
[message.attachmentIds addObject:attachmentStream.uniqueId];
if (sourceFilename) {
message.attachmentFilenameMap[attachmentStream.uniqueId] = sourceFilename;
if (filename) {
message.attachmentFilenameMap[attachmentStream.uniqueId] = filename;
}
[message save];
@ -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]) {
@ -962,7 +938,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
});
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogInfo(@"%@ sending to recipient: %@, failed with error: %@", self.tag, recipient.uniqueId, error);
DDLogInfo(@"%@ sending to recipient: %@, failed with error: %@",
self.tag,
recipient.uniqueId,
message.debugDescription);
[DDLog flushLog];
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
@ -1161,9 +1140,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 +1153,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 +1187,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 +1228,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 +1251,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 +1261,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 +1316,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

@ -15,15 +15,13 @@
*/
@property (nonatomic, readonly, getter=wasRead) BOOL read;
/**
* 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)markAsReadLocally;
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
@property (nonatomic, readonly) NSString *uniqueThreadId;
- (BOOL)shouldAffectUnreadCounts;
/**
* Used for *responding* to a remote read receipt or in response to user activity.
*/
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration;
@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

@ -34,16 +34,14 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
- (BOOL)shouldUseReceiptDateForSorting
- (nullable NSDate *)receiptDateForSorting
{
// Use the timestamp, not the "received at" timestamp to sort,
// since we're creating these interactions after the fact and back-dating them.
return NO;
}
- (BOOL)isDynamicInteraction
{
return YES;
// Always use date, since we're creating these interactions after the fact
// and back-dating them.
//
// By default [TSMessage receiptDateForSorting] will prefer to use receivedAtDate
// which is not back-dated.
return self.date;
}
@end

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

@ -13,16 +13,14 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
@interface TSCall ()
@property (nonatomic, getter=wasRead) BOOL read;
@property (nonatomic, readonly) NSUInteger callSchemaVersion;
@end
#pragma mark -
@implementation TSCall
@synthesize read = _read;
- (instancetype)initWithTimestamp:(uint64_t)timestamp
withCallNumber:(NSString *)contactNumber
callType:(RPRecentCallType)callType
@ -36,7 +34,7 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
_callSchemaVersion = TSCallCurrentSchemaVersion;
_callType = callType;
if (_callType == RPRecentCallTypeMissed || _callType == RPRecentCallTypeMissedBecauseOfChangedIdentity) {
if (_callType == RPRecentCallTypeMissed) {
_read = NO;
} else {
_read = YES;
@ -74,37 +72,27 @@ 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");
}
}
#pragma mark - OWSReadTracking
- (BOOL)shouldAffectUnreadCounts
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
return YES;
}
DDLogInfo(@"%@ marking as read uniqueId: %@ which has timestamp: %llu", self.tag, self.uniqueId, self.timestamp);
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration
{
OWSAssert(transaction);
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 +107,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

@ -1,13 +1,12 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Created by Frederic Jacobs.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
#import "TSYapDatabaseObject.h"
#import "ContactsManagerProtocol.h"
@interface TSGroupModel : TSYapDatabaseObject
@property (nonatomic, strong) NSMutableArray<NSString *> *groupMemberIds;
@property (nonatomic, strong) NSMutableArray *groupMemberIds;
@property (nonatomic, strong) NSString *groupName;
@property (nonatomic, strong) NSData *groupId;
@ -15,7 +14,7 @@
@property (nonatomic, strong) UIImage *groupImage;
- (instancetype)initWithTitle:(NSString *)title
memberIds:(NSMutableArray<NSString *> *)memberIds
memberIds:(NSMutableArray *)memberIds
image:(UIImage *)image
groupId:(NSData *)groupId;

View File

@ -1,6 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Created by Frederic Jacobs on 13/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
#import "TSGroupModel.h"
#import "FunctionalUtil.h"
@ -9,7 +8,7 @@
#if TARGET_OS_IOS
- (instancetype)initWithTitle:(NSString *)title
memberIds:(NSMutableArray<NSString *> *)memberIds
memberIds:(NSMutableArray *)memberIds
image:(UIImage *)image
groupId:(NSData *)groupId
{

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,17 @@ 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];
}];
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 +363,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 +415,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 +449,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 +462,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 +499,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 +608,6 @@ NS_ASSUME_NONNULL_BEGIN
return;
}
DDLogDebug(@"%@ incoming attachment message: %@", self.tag, createdMessage.debugDescription);
[attachmentsProcessor fetchAttachmentsForMessage:createdMessage
success:^(TSAttachmentStream *attachmentStream) {
DDLogDebug(
@ -674,8 +646,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 +657,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 +683,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);
}
@ -789,7 +758,7 @@ NS_ASSUME_NONNULL_BEGIN
if (gThread.groupModel.groupImage) {
[self.messageSender sendAttachmentData:UIImagePNGRepresentation(gThread.groupModel.groupImage)
contentType:OWSMimeTypeImagePng
sourceFilename:nil
filename:nil
inMessage:message
success:^{
DDLogDebug(@"%@ Successfully sent group update with avatar", self.tag);
@ -920,7 +889,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 +911,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 +936,6 @@ NS_ASSUME_NONNULL_BEGIN
messageBody:body
attachmentIds:@[]
expiresInSeconds:dataMessage.expireTimer];
DDLogDebug(@"%@ incoming extra text message: %@", self.tag, incomingMessage.debugDescription);
[textMessage saveWithTransaction:transaction];
}
}
@ -989,7 +954,11 @@ NS_ASSUME_NONNULL_BEGIN
// Update thread preview in inbox
[thread touch];
// TODO Delay notification by 100ms?
// It's pretty annoying when you're phone keeps buzzing while you're having a conversation on Desktop.
NSString *name = [thread name];
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForIncomingMessage:incomingMessage
from:name
inThread:thread
contactsManager:self.contactsManager];
}
@ -1005,9 +974,9 @@ NS_ASSUME_NONNULL_BEGIN
exception.description,
exception.name,
exception.reason);
__block TSErrorMessage *errorMessage;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSErrorMessage *errorMessage;
if ([exception.name isEqualToString:NoSessionException]) {
errorMessage = [TSErrorMessage missingSessionWithEnvelope:envelope withTransaction:transaction];
} else if ([exception.name isEqualToString:InvalidKeyException]) {
@ -1020,28 +989,14 @@ 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];
}
[errorMessage saveWithTransaction:transaction];
}];
if (errorMessage != nil) {
[self notififyForErrorMessage:errorMessage withEnvelope:envelope];
}
}
- (void)notififyForErrorMessage:(TSErrorMessage *)errorMessage withEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
{
TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source];
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:contactThread];
}
#pragma mark - helpers
@ -1083,12 +1038,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

@ -1,15 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSRequest.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSGetProfileRequest : TSRequest
- (instancetype)initWithRecipientId:(NSString *)recipientId;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,30 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSGetProfileRequest.h"
#import "TSConstants.h"
NS_ASSUME_NONNULL_BEGIN
@implementation OWSGetProfileRequest
- (instancetype)initWithRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
NSString *path = [NSString stringWithFormat:textSecureProfileAPIFormat, recipientId];
self = [super initWithURL:[NSURL URLWithString:path]];
if (!self) {
return self;
}
self.HTTPMethod = @"GET";
self.parameters = nil;
return self;
}
@end
NS_ASSUME_NONNULL_END

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

@ -14,6 +14,7 @@ NSString *const TSNetworkManagerDomain = @"org.whispersystems.signal.networkMana
@interface TSNetworkManager ()
@property (nonatomic, readonly, strong) OWSSignalService *signalService;
typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
@end
@ -22,24 +23,26 @@ 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, ^{
sharedMyManager = [[self alloc] initDefault];
OWSSignalService *signalService = [[OWSSignalService alloc] init];
sharedMyManager = [[self alloc] initWithSignalService:signalService];
});
return sharedMyManager;
}
- (instancetype)initDefault
- (instancetype)initWithSignalService:(OWSSignalService *)signalService
{
self = [super init];
if (!self) {
return self;
}
_signalService = signalService;
OWSSingletonAssert();
return self;
@ -56,7 +59,7 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
void (^failure)(NSURLSessionDataTask *task, NSError *error) =
[TSNetworkManager errorPrettifyingForFailureBlock:failureBlock];
AFHTTPSessionManager *sessionManager = [OWSSignalService sharedInstance].HTTPSessionManager;
AFHTTPSessionManager *sessionManager = self.signalService.HTTPSessionManager;
if ([request isKindOfClass:[TSVerifyCodeRequest class]]) {
// We plant the Authorization parameter ourselves, no need to double add.

View File

@ -1,6 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Created by Michael Kirk on 12/20/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@ -8,9 +7,9 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSCensorshipConfiguration : NSObject
- (NSString *)frontingHost:(NSString *)e164PhoneNumber;
- (NSString *)frontingHost:(NSString *)e164PhonNumber;
- (NSString *)reflectorHost;
- (BOOL)isCensoredPhoneNumber:(NSString *)e164PhoneNumber;
- (BOOL)isCensoredPhoneNumber:(NSString *)e164PhonNumber;
@end

View File

@ -11,20 +11,20 @@ NSString *const OWSCensorshipConfigurationReflectorHost = @"signal-reflector-mee
@implementation OWSCensorshipConfiguration
- (NSString *)frontingHost:(NSString *)e164PhoneNumber
- (NSString *)frontingHost:(NSString *)e164PhonNumber
{
OWSAssert(e164PhoneNumber.length > 0);
OWSAssert(e164PhonNumber.length > 0);
NSString *domain = nil;
for (NSString *countryCode in self.censoredCountryCodes) {
if ([e164PhoneNumber hasPrefix:countryCode]) {
if ([e164PhonNumber hasPrefix:countryCode]) {
domain = self.censoredCountryCodes[countryCode];
}
}
// Fronting should only be auto-activated for countries specified in censoredCountryCodes,
// all of which have a domain specified. However users can also manually enable
// censorship circumvention.
// Fronting should only be used for countries specified in censoredCountryCodes,
// all of which have a domain specified.
OWSAssert(domain);
if (!domain) {
domain = @"google.com";
}
@ -60,10 +60,10 @@ NSString *const OWSCensorshipConfigurationReflectorHost = @"signal-reflector-mee
};
}
- (BOOL)isCensoredPhoneNumber:(NSString *)e164PhoneNumber
- (BOOL)isCensoredPhoneNumber:(NSString *)e164PhonNumber
{
for (NSString *countryCode in self.censoredCountryCodes) {
if ([e164PhoneNumber hasPrefix:countryCode]) {
if ([e164PhonNumber hasPrefix:countryCode]) {
return YES;
}
}

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