mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-19 06:04:31 -04:00
Merge remote-tracking branch 'origin/main' into user_once_cell_for_session
This commit is contained in:
13
.github/workflows/audit.yml
vendored
Normal file
13
.github/workflows/audit.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: Security audit
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
jobs:
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
1
.github/workflows/docs.yml
vendored
1
.github/workflows/docs.yml
vendored
@@ -40,3 +40,4 @@ jobs:
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./target/doc/
|
||||
force_orphan: true
|
||||
|
||||
58
.github/workflows/ffi.yml
vendored
Normal file
58
.github/workflows/ffi.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
name: FFI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Run Apple platform tests
|
||||
runs-on: macos-12
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Install targets
|
||||
run: |
|
||||
rustup target add aarch64-apple-ios-sim
|
||||
rustup target add x86_64-apple-ios
|
||||
|
||||
- name: Load cache
|
||||
uses: Swatinem/rust-cache@v1
|
||||
|
||||
- name: Install Uniffi
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: install
|
||||
args: uniffi_bindgen
|
||||
|
||||
|
||||
- name: Generate .xcframework
|
||||
run: sh bindings/apple/debug_build_xcframework.sh ci
|
||||
|
||||
- name: Run XCTests
|
||||
run: |
|
||||
xcodebuild test \
|
||||
-project bindings/apple/MatrixRustSDK.xcodeproj \
|
||||
-scheme MatrixRustSDK \
|
||||
-sdk iphonesimulator \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 13,OS=15.4'
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,6 +1,10 @@
|
||||
Cargo.lock
|
||||
target
|
||||
generated
|
||||
master.zip
|
||||
emsdk-*
|
||||
.idea/
|
||||
|
||||
## User settings
|
||||
xcuserdata/
|
||||
.vscode/
|
||||
|
||||
513
bindings/apple/MatrixRustSDK.xcodeproj/project.pbxproj
Normal file
513
bindings/apple/MatrixRustSDK.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,513 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 55;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
181AA19B27B52AB40005F102 /* MatrixSDKFFI.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 181AA19A27B52AB40005F102 /* MatrixSDKFFI.xcframework */; };
|
||||
181AA19C27B52AB40005F102 /* MatrixSDKFFI.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 181AA19A27B52AB40005F102 /* MatrixSDKFFI.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
189A89BA27B40BBF0048B0A5 /* sdk.swift in Sources */ = {isa = PBXBuildFile; fileRef = 189A89B927B40BBF0048B0A5 /* sdk.swift */; };
|
||||
18CE89D827B2939900CA89E1 /* MatrixRustSDKApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18CE89D727B2939900CA89E1 /* MatrixRustSDKApp.swift */; };
|
||||
18CE89DA27B2939900CA89E1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18CE89D927B2939900CA89E1 /* ContentView.swift */; };
|
||||
18CE89DC27B2939A00CA89E1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 18CE89DB27B2939A00CA89E1 /* Assets.xcassets */; };
|
||||
18CE89E927B2939A00CA89E1 /* MatrixRustSDKTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18CE89E827B2939A00CA89E1 /* MatrixRustSDKTests.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
18CE89E527B2939A00CA89E1 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 18CE89CC27B2939900CA89E1 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 18CE89D327B2939900CA89E1;
|
||||
remoteInfo = MatrixRustSDK;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
18CE8A1F27B2941600CA89E1 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
181AA19C27B52AB40005F102 /* MatrixSDKFFI.xcframework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
181AA19927B52AA60005F102 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
181AA19A27B52AB40005F102 /* MatrixSDKFFI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MatrixSDKFFI.xcframework; path = ../../generated/MatrixSDKFFI.xcframework; sourceTree = "<group>"; };
|
||||
189A89B927B40BBF0048B0A5 /* sdk.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = sdk.swift; path = ../../../generated/swift/sdk.swift; sourceTree = "<group>"; };
|
||||
189A89C327B417CA0048B0A5 /* MatrixRustSDK-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MatrixRustSDK-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
18CE89D427B2939900CA89E1 /* MatrixRustSDK.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MatrixRustSDK.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
18CE89D727B2939900CA89E1 /* MatrixRustSDKApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixRustSDKApp.swift; sourceTree = "<group>"; };
|
||||
18CE89D927B2939900CA89E1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
18CE89DB27B2939A00CA89E1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
18CE89E427B2939A00CA89E1 /* MatrixRustSDKTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MatrixRustSDKTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
18CE89E827B2939A00CA89E1 /* MatrixRustSDKTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixRustSDKTests.swift; sourceTree = "<group>"; };
|
||||
18CE8A0127B293A900CA89E1 /* MatrixRustSDK.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MatrixRustSDK.entitlements; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
18CE89D127B2939900CA89E1 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
181AA19B27B52AB40005F102 /* MatrixSDKFFI.xcframework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
18CE89E127B2939A00CA89E1 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
189A89AB27B2E16B0048B0A5 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
181AA19A27B52AB40005F102 /* MatrixSDKFFI.xcframework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
189A89B827B40BB10048B0A5 /* Generated */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
189A89B927B40BBF0048B0A5 /* sdk.swift */,
|
||||
);
|
||||
name = Generated;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
18CE89CB27B2939900CA89E1 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
18CE89D627B2939900CA89E1 /* MatrixRustSDK */,
|
||||
18CE89E727B2939A00CA89E1 /* MatrixRustSDKTests */,
|
||||
18CE89D527B2939900CA89E1 /* Products */,
|
||||
189A89AB27B2E16B0048B0A5 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
18CE89D527B2939900CA89E1 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
18CE89D427B2939900CA89E1 /* MatrixRustSDK.app */,
|
||||
18CE89E427B2939A00CA89E1 /* MatrixRustSDKTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
18CE89D627B2939900CA89E1 /* MatrixRustSDK */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
181AA19927B52AA60005F102 /* Info.plist */,
|
||||
189A89B827B40BB10048B0A5 /* Generated */,
|
||||
18CE89D727B2939900CA89E1 /* MatrixRustSDKApp.swift */,
|
||||
18CE89D927B2939900CA89E1 /* ContentView.swift */,
|
||||
18CE8A0127B293A900CA89E1 /* MatrixRustSDK.entitlements */,
|
||||
18CE89DB27B2939A00CA89E1 /* Assets.xcassets */,
|
||||
189A89C327B417CA0048B0A5 /* MatrixRustSDK-Bridging-Header.h */,
|
||||
);
|
||||
path = MatrixRustSDK;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
18CE89E727B2939A00CA89E1 /* MatrixRustSDKTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
18CE89E827B2939A00CA89E1 /* MatrixRustSDKTests.swift */,
|
||||
);
|
||||
path = MatrixRustSDKTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
18CE89D327B2939900CA89E1 /* MatrixRustSDK */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 18CE89F827B2939A00CA89E1 /* Build configuration list for PBXNativeTarget "MatrixRustSDK" */;
|
||||
buildPhases = (
|
||||
18CE89D027B2939900CA89E1 /* Sources */,
|
||||
18CE89D127B2939900CA89E1 /* Frameworks */,
|
||||
18CE89D227B2939900CA89E1 /* Resources */,
|
||||
18CE8A1F27B2941600CA89E1 /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = MatrixRustSDK;
|
||||
productName = MatrixRustSDK;
|
||||
productReference = 18CE89D427B2939900CA89E1 /* MatrixRustSDK.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
18CE89E327B2939A00CA89E1 /* MatrixRustSDKTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 18CE89FB27B2939A00CA89E1 /* Build configuration list for PBXNativeTarget "MatrixRustSDKTests" */;
|
||||
buildPhases = (
|
||||
18CE89E027B2939A00CA89E1 /* Sources */,
|
||||
18CE89E127B2939A00CA89E1 /* Frameworks */,
|
||||
18CE89E227B2939A00CA89E1 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
18CE89E627B2939A00CA89E1 /* PBXTargetDependency */,
|
||||
);
|
||||
name = MatrixRustSDKTests;
|
||||
productName = MatrixRustSDKTests;
|
||||
productReference = 18CE89E427B2939A00CA89E1 /* MatrixRustSDKTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
18CE89CC27B2939900CA89E1 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 1320;
|
||||
LastUpgradeCheck = 1320;
|
||||
TargetAttributes = {
|
||||
18CE89D327B2939900CA89E1 = {
|
||||
CreatedOnToolsVersion = 13.2.1;
|
||||
LastSwiftMigration = 1320;
|
||||
};
|
||||
18CE89E327B2939A00CA89E1 = {
|
||||
CreatedOnToolsVersion = 13.2.1;
|
||||
TestTargetID = 18CE89D327B2939900CA89E1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 18CE89CF27B2939900CA89E1 /* Build configuration list for PBXProject "MatrixRustSDK" */;
|
||||
compatibilityVersion = "Xcode 13.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 18CE89CB27B2939900CA89E1;
|
||||
productRefGroup = 18CE89D527B2939900CA89E1 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
18CE89D327B2939900CA89E1 /* MatrixRustSDK */,
|
||||
18CE89E327B2939A00CA89E1 /* MatrixRustSDKTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
18CE89D227B2939900CA89E1 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
18CE89DC27B2939A00CA89E1 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
18CE89E227B2939A00CA89E1 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
18CE89D027B2939900CA89E1 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
18CE89DA27B2939900CA89E1 /* ContentView.swift in Sources */,
|
||||
189A89BA27B40BBF0048B0A5 /* sdk.swift in Sources */,
|
||||
18CE89D827B2939900CA89E1 /* MatrixRustSDKApp.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
18CE89E027B2939A00CA89E1 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
18CE89E927B2939A00CA89E1 /* MatrixRustSDKTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
18CE89E627B2939A00CA89E1 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 18CE89D327B2939900CA89E1 /* MatrixRustSDK */;
|
||||
targetProxy = 18CE89E527B2939A00CA89E1 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
18CE89F627B2939A00CA89E1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.2;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
18CE89F727B2939A00CA89E1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.2;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
18CE89F927B2939A00CA89E1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = MatrixRustSDK/MatrixRustSDK.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = MatrixRustSDK/Info.plist;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.matrix.MatrixRustSDK;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MatrixRustSDK/MatrixRustSDK-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
18CE89FA27B2939A00CA89E1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = MatrixRustSDK/MatrixRustSDK.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = MatrixRustSDK/Info.plist;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.matrix.MatrixRustSDK;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MatrixRustSDK/MatrixRustSDK-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
18CE89FC27B2939A00CA89E1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.2;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.matrix.MatrixRustSDKTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MatrixRustSDK.app/MatrixRustSDK";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
18CE89FD27B2939A00CA89E1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.2;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.matrix.MatrixRustSDKTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MatrixRustSDK.app/MatrixRustSDK";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
18CE89CF27B2939900CA89E1 /* Build configuration list for PBXProject "MatrixRustSDK" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
18CE89F627B2939A00CA89E1 /* Debug */,
|
||||
18CE89F727B2939A00CA89E1 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
18CE89F827B2939A00CA89E1 /* Build configuration list for PBXNativeTarget "MatrixRustSDK" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
18CE89F927B2939A00CA89E1 /* Debug */,
|
||||
18CE89FA27B2939A00CA89E1 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
18CE89FB27B2939A00CA89E1 /* Build configuration list for PBXNativeTarget "MatrixRustSDKTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
18CE89FC27B2939A00CA89E1 /* Debug */,
|
||||
18CE89FD27B2939A00CA89E1 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 18CE89CC27B2939900CA89E1 /* Project object */;
|
||||
}
|
||||
7
bindings/apple/MatrixRustSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
bindings/apple/MatrixRustSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1320"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "18CE89D327B2939900CA89E1"
|
||||
BuildableName = "MatrixRustSDK.app"
|
||||
BlueprintName = "MatrixRustSDK"
|
||||
ReferencedContainer = "container:MatrixRustSDK.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "18CE89E327B2939A00CA89E1"
|
||||
BuildableName = "MatrixRustSDKTests.xctest"
|
||||
BlueprintName = "MatrixRustSDKTests"
|
||||
ReferencedContainer = "container:MatrixRustSDK.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "18CE89ED27B2939A00CA89E1"
|
||||
BuildableName = "MatrixRustSDKUITests.xctest"
|
||||
BlueprintName = "MatrixRustSDKUITests"
|
||||
ReferencedContainer = "container:MatrixRustSDK.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "18CE89D327B2939900CA89E1"
|
||||
BuildableName = "MatrixRustSDK.app"
|
||||
BlueprintName = "MatrixRustSDK"
|
||||
ReferencedContainer = "container:MatrixRustSDK.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "18CE89D327B2939900CA89E1"
|
||||
BuildableName = "MatrixRustSDK.app"
|
||||
BlueprintName = "MatrixRustSDK"
|
||||
ReferencedContainer = "container:MatrixRustSDK.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
21
bindings/apple/MatrixRustSDK/ContentView.swift
Normal file
21
bindings/apple/MatrixRustSDK/ContentView.swift
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// MatrixRustSDK
|
||||
//
|
||||
// Created by Stefan Ceriu on 08.02.2022.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
Text("Hello, Rust!")
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
5
bindings/apple/MatrixRustSDK/Info.plist
Normal file
5
bindings/apple/MatrixRustSDK/Info.plist
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
||||
@@ -0,0 +1,5 @@
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
#import "sdkFFI.h"
|
||||
10
bindings/apple/MatrixRustSDK/MatrixRustSDK.entitlements
Normal file
10
bindings/apple/MatrixRustSDK/MatrixRustSDK.entitlements
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
17
bindings/apple/MatrixRustSDK/MatrixRustSDKApp.swift
Normal file
17
bindings/apple/MatrixRustSDK/MatrixRustSDKApp.swift
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// MatrixRustSDKApp.swift
|
||||
// MatrixRustSDK
|
||||
//
|
||||
// Created by Stefan Ceriu on 08.02.2022.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct MatrixRustSDKApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
}
|
||||
48
bindings/apple/MatrixRustSDKTests/MatrixRustSDKTests.swift
Normal file
48
bindings/apple/MatrixRustSDKTests/MatrixRustSDKTests.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// MatrixRustSDKTests.swift
|
||||
// MatrixRustSDKTests
|
||||
//
|
||||
// Created by Stefan Ceriu on 08.02.2022.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import MatrixRustSDK
|
||||
|
||||
class MatrixRustSDKTests: XCTestCase {
|
||||
|
||||
static var client: Client!
|
||||
|
||||
override class func setUp() {
|
||||
client = try! guestClient(basePath: basePath, homeserver: "https://matrix.org")
|
||||
}
|
||||
|
||||
func testClientProperties() {
|
||||
XCTAssertTrue(Self.client.isGuest())
|
||||
|
||||
XCTAssertNotNil(try? Self.client.restoreToken())
|
||||
XCTAssertNotNil(try? Self.client.deviceId())
|
||||
XCTAssertNotNil(try? Self.client.displayName())
|
||||
}
|
||||
|
||||
func testReadOnlyFileSystemError() {
|
||||
do {
|
||||
let _ = try loginNewClient(basePath: "", username: "test", password: "test")
|
||||
} catch ClientError.Generic(let message) {
|
||||
XCTAssertNotNil(message.range(of: "Read-only file system"))
|
||||
} catch {
|
||||
XCTFail("Not expecting any other kind of exception")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
static private var basePath: String {
|
||||
guard let url = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else {
|
||||
fatalError("Should always be able to retrieve the caches directory")
|
||||
}
|
||||
|
||||
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: false, attributes: nil)
|
||||
|
||||
return url.path
|
||||
}
|
||||
}
|
||||
39
bindings/apple/README.md
Normal file
39
bindings/apple/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Apple platforms support
|
||||
|
||||
This project and build script demonstrate how to create an XCFramework that can be imported into an Xcode project and run on Apple platforms.
|
||||
|
||||
## Building the universal framework
|
||||
|
||||
```
|
||||
sh build_xcframework.sh
|
||||
```
|
||||
|
||||
**Prerequisites**
|
||||
|
||||
* the Rust toolchain
|
||||
* UniFFI - `cargo install uniffi_bindge`
|
||||
* Apple targets (e.g. `rustup target add aarch64-apple-ios`)
|
||||
* `xcodebuild` command line tool from [Apple](https://developer.apple.com/library/archive/technotes/tn2339/_index.html)
|
||||
* `lipo` for creating the fat static libs
|
||||
|
||||
|
||||
The `build_xcframework.sh` script will go through all the steps required to generate a fully usable `.xcframework`:
|
||||
|
||||
1. compile `matrix-sdk-ffi` libraries for iOS, the iOS simulator, MacOS, and Mac Catalyst under `/target`. Some targets are not part of the standard library and they will be built using the nightly toolchain.
|
||||
* `lipo` together the libraries for the same platform under `/generated`
|
||||
* run `uniffi` and generate the C header, module map and swift files
|
||||
* `xcodebuild` an `xcframework` from the fat static libs and the original iOS one, and add the header and module map to it under `generated/MatrixSDKFFI.xcframework`
|
||||
* cleanup and delete the generated files except the .xcframework and the swift sources (that aren't part of the framework)
|
||||
|
||||
## Running the Xcode project
|
||||
|
||||
The Xcode project is meant to provide a simple example on how to integrate everything together but also a place to run unit and integration tests from.
|
||||
|
||||
It's pre-configured to link to the generated .xcframework and .swift files so successfully running the script first is necessary for it to compile.
|
||||
|
||||
It makes the compiled code available to swift by importing the C header through its bridging header.
|
||||
|
||||
Once all the generated components are available running it should be as easy as choosing a platform and clicking run.
|
||||
|
||||
## Distribution
|
||||
The generated framework and Swift code can be distributed and integrated directly but in order to make things simpler we bundle them together as a Swift package available [TBD](here)
|
||||
89
bindings/apple/build_xcframework.sh
Executable file
89
bindings/apple/build_xcframework.sh
Executable file
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eEu
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Path to the repo root
|
||||
SRC_ROOT=../..
|
||||
|
||||
TARGET_DIR="${SRC_ROOT}/target"
|
||||
|
||||
GENERATED_DIR="${SRC_ROOT}/generated"
|
||||
mkdir -p ${GENERATED_DIR}
|
||||
|
||||
REL_FLAG="--release"
|
||||
REL_TYPE_DIR="release"
|
||||
|
||||
# Build static libs for all the different architectures
|
||||
|
||||
# iOS
|
||||
cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-ios"
|
||||
|
||||
# MacOS
|
||||
cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-darwin"
|
||||
cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "x86_64-apple-darwin"
|
||||
|
||||
# iOS Simulator
|
||||
cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-ios-sim"
|
||||
cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "x86_64-apple-ios"
|
||||
|
||||
# Mac Catalyst
|
||||
cargo +nightly build -Z build-std -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-ios-macabi"
|
||||
cargo +nightly build -Z build-std -p matrix-sdk-ffi ${REL_FLAG} --target "x86_64-apple-ios-macabi"
|
||||
|
||||
# Lipo together the libraries for the same platform
|
||||
|
||||
# MacOS
|
||||
lipo -create \
|
||||
"${TARGET_DIR}/x86_64-apple-darwin/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \
|
||||
"${TARGET_DIR}/aarch64-apple-darwin/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \
|
||||
-output "${GENERATED_DIR}/libmatrix_sdk_ffi_macos.a"
|
||||
|
||||
# iOS Simulator
|
||||
lipo -create \
|
||||
"${TARGET_DIR}/x86_64-apple-ios/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \
|
||||
"${TARGET_DIR}/aarch64-apple-ios-sim/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \
|
||||
-output "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a"
|
||||
|
||||
# Mac Catalyst
|
||||
lipo -create \
|
||||
"${TARGET_DIR}/x86_64-apple-ios-macabi/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \
|
||||
"${TARGET_DIR}/aarch64-apple-ios-macabi/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \
|
||||
-output "${GENERATED_DIR}/libmatrix_sdk_ffi_maccatalyst.a"
|
||||
|
||||
|
||||
# Generate uniffi files
|
||||
uniffi-bindgen generate "${SRC_ROOT}/crates/matrix-sdk-ffi/src/api.udl" --language swift --out-dir ${GENERATED_DIR}
|
||||
|
||||
# Move them to the right place
|
||||
HEADERS_DIR=${GENERATED_DIR}/headers
|
||||
mkdir -p ${HEADERS_DIR}
|
||||
|
||||
mv ${GENERATED_DIR}/*.h ${GENERATED_DIR}/*.modulemap ${HEADERS_DIR}
|
||||
|
||||
SWIFT_DIR="${GENERATED_DIR}/swift"
|
||||
mkdir -p ${SWIFT_DIR}
|
||||
|
||||
mv ${GENERATED_DIR}/*.swift ${SWIFT_DIR}
|
||||
|
||||
# Build the xcframework
|
||||
|
||||
if [ -d "${GENERATED_DIR}/MatrixSDKFFI.xcframework" ]; then rm -rf "${GENERATED_DIR}/MatrixSDKFFI.xcframework"; fi
|
||||
|
||||
xcodebuild -create-xcframework \
|
||||
-library "${GENERATED_DIR}/libmatrix_sdk_ffi_macos.a" \
|
||||
-headers ${HEADERS_DIR} \
|
||||
-library "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" \
|
||||
-headers ${HEADERS_DIR} \
|
||||
-library "${GENERATED_DIR}/libmatrix_sdk_ffi_maccatalyst.a" \
|
||||
-headers ${HEADERS_DIR} \
|
||||
-library "${TARGET_DIR}/aarch64-apple-ios/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \
|
||||
-headers ${HEADERS_DIR} \
|
||||
-output "${GENERATED_DIR}/MatrixSDKFFI.xcframework"
|
||||
|
||||
# Cleanup
|
||||
|
||||
if [ -f "${GENERATED_DIR}/libmatrix_sdk_ffi_macos.a" ]; then rm -rf "${GENERATED_DIR}/libmatrix_sdk_ffi_macos.a"; fi
|
||||
if [ -f "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" ]; then rm -rf "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a"; fi
|
||||
if [ -f "${GENERATED_DIR}/libmatrix_sdk_ffi_maccatalyst.a" ]; then rm -rf "${GENERATED_DIR}/libmatrix_sdk_ffi_maccatalyst.a"; fi
|
||||
if [ -d ${HEADERS_DIR} ]; then rm -rf ${HEADERS_DIR}; fi
|
||||
72
bindings/apple/debug_build_xcframework.sh
Executable file
72
bindings/apple/debug_build_xcframework.sh
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eEu
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
IS_CI=false
|
||||
|
||||
if [ $# -eq 1 ]; then
|
||||
IS_CI=true
|
||||
echo "Running CI build"
|
||||
else
|
||||
echo "Running debug build"
|
||||
fi
|
||||
|
||||
# Path to the repo root
|
||||
SRC_ROOT=../..
|
||||
|
||||
TARGET_DIR="${SRC_ROOT}/target"
|
||||
|
||||
GENERATED_DIR="${SRC_ROOT}/generated"
|
||||
mkdir -p ${GENERATED_DIR}
|
||||
|
||||
# Release for now. Debug builds cause crashes deep inside the Tokio runtime.
|
||||
REL_FLAG="--release"
|
||||
REL_TYPE_DIR="release"
|
||||
|
||||
# iOS Simulator
|
||||
cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-ios-sim"
|
||||
cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "x86_64-apple-ios"
|
||||
|
||||
lipo -create \
|
||||
"${TARGET_DIR}/x86_64-apple-ios/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \
|
||||
"${TARGET_DIR}/aarch64-apple-ios-sim/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \
|
||||
-output "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a"
|
||||
|
||||
# Generate uniffi files
|
||||
uniffi-bindgen generate "${SRC_ROOT}/crates/matrix-sdk-ffi/src/api.udl" --language swift --out-dir ${GENERATED_DIR}
|
||||
|
||||
# Move them to the right place
|
||||
HEADERS_DIR=${GENERATED_DIR}/headers
|
||||
mkdir -p ${HEADERS_DIR}
|
||||
|
||||
mv ${GENERATED_DIR}/*.h ${GENERATED_DIR}/*.modulemap ${HEADERS_DIR}
|
||||
|
||||
SWIFT_DIR="${GENERATED_DIR}/swift"
|
||||
mkdir -p ${SWIFT_DIR}
|
||||
|
||||
mv ${GENERATED_DIR}/*.swift ${SWIFT_DIR}
|
||||
|
||||
# Build the xcframework
|
||||
|
||||
if [ -d "${GENERATED_DIR}/MatrixSDKFFI.xcframework" ]; then rm -rf "${GENERATED_DIR}/MatrixSDKFFI.xcframework"; fi
|
||||
|
||||
xcodebuild -create-xcframework \
|
||||
-library "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" \
|
||||
-headers ${HEADERS_DIR} \
|
||||
-output "${GENERATED_DIR}/MatrixSDKFFI.xcframework"
|
||||
|
||||
# Cleanup
|
||||
|
||||
# if [ -f "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a" ]; then rm -rf "${GENERATED_DIR}/libmatrix_sdk_ffi_iossimulator.a"; fi
|
||||
# if [ -d ${HEADERS_DIR} ]; then rm -rf ${HEADERS_DIR}; fi
|
||||
|
||||
if [ "$IS_CI" = false ] ; then
|
||||
echo "Preparing matrix-rust-components-swift"
|
||||
|
||||
# Debug -> Copy generated files over to ../../../matrix-rust-components-swift
|
||||
echo "$(echo "import MatrixSDKFFIWrapper\n"; cat "${SWIFT_DIR}/sdk.swift")" > "${SWIFT_DIR}/sdk.swift"
|
||||
|
||||
rsync -a --delete "${GENERATED_DIR}/MatrixSDKFFI.xcframework" "${SRC_ROOT}/../matrix-rust-components-swift/"
|
||||
rsync -a --delete "${GENERATED_DIR}/swift/" "${SRC_ROOT}/../matrix-rust-components-swift/Sources/MatrixRustSDK"
|
||||
fi
|
||||
39
codecov.yaml
39
codecov.yaml
@@ -2,15 +2,34 @@ coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
# Commits pushed to master should not make the overall
|
||||
# project coverage decrease by more than 1%:
|
||||
target: auto
|
||||
threshold: 1%
|
||||
patch:
|
||||
default:
|
||||
# Be tolerant on slight code coverage diff on PRs to limit
|
||||
# noisy red coverage status on github PRs.
|
||||
# Note: The coverage stats are still uploaded
|
||||
# to codecov so that PR reviewers can see uncovered lines
|
||||
# by default, we only care about test coverage of the main
|
||||
# rust crates
|
||||
target: auto
|
||||
threshold: 1%
|
||||
paths:
|
||||
- "crates/matrix-sdk/"
|
||||
- "crates/matrix-sdk-appservice/"
|
||||
- "crates/matrix-sdk-base/"
|
||||
- "crates/matrix-sdk-common/"
|
||||
- "crates/matrix-sdk-crypto/"
|
||||
- "crates/matrix-sdk-qrcode/"
|
||||
- "crates/matrix-sdk-sled/"
|
||||
- "crates/matrix-sdk-store-encryption/"
|
||||
# Coverage of wasm tests isn't supported at the moment,
|
||||
# see rustwasm/wasm-bindgen#2276
|
||||
# - "crates/matrix-sdk-indexeddb"
|
||||
bindings:
|
||||
# Coverage of binding tests is recorded but for informational
|
||||
# purposes only
|
||||
informational: true
|
||||
paths:
|
||||
- "bindings/"
|
||||
- "crates/matrix-sdk-crypto-ffi/"
|
||||
- "crates/matrix-sdk-ffi/"
|
||||
labs:
|
||||
# Coverage of lab tests is recorded but for informational
|
||||
# purposes only
|
||||
informational: true
|
||||
paths:
|
||||
- "labs/"
|
||||
patch: off
|
||||
|
||||
@@ -8,9 +8,14 @@ use matrix_sdk_appservice::{
|
||||
events::room::member::{MembershipState, OriginalSyncRoomMemberEvent},
|
||||
UserId,
|
||||
},
|
||||
HttpError,
|
||||
},
|
||||
AppService, AppServiceRegistration, Result,
|
||||
};
|
||||
use ruma::api::{
|
||||
client::{error::ErrorKind, uiaa::UiaaResponse},
|
||||
error::{FromHttpResponseError, ServerError},
|
||||
};
|
||||
use tracing::trace;
|
||||
|
||||
pub async fn handle_room_member(
|
||||
@@ -22,7 +27,9 @@ pub async fn handle_room_member(
|
||||
trace!("not an appservice user: {}", event.state_key);
|
||||
} else if let MembershipState::Invite = event.content.membership {
|
||||
let user_id = UserId::parse(event.state_key.as_str())?;
|
||||
appservice.register_virtual_user(user_id.localpart()).await?;
|
||||
if let Err(error) = appservice.register_virtual_user(user_id.localpart()).await {
|
||||
error_if_user_not_in_use(error)?;
|
||||
}
|
||||
|
||||
let client = appservice.virtual_user_client(user_id.localpart()).await?;
|
||||
client.join_room_by_id(room.room_id()).await?;
|
||||
@@ -31,6 +38,17 @@ pub async fn handle_room_member(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn error_if_user_not_in_use(error: matrix_sdk_appservice::Error) -> Result<()> {
|
||||
match error {
|
||||
// If user is already in use that's OK.
|
||||
matrix_sdk_appservice::Error::Matrix(matrix_sdk::Error::Http(HttpError::UiaaError(
|
||||
FromHttpResponseError::Server(ServerError::Known(UiaaResponse::MatrixError(error))),
|
||||
))) if matches!(error.kind, ErrorKind::UserInUse) => Ok(()),
|
||||
// In all other cases return with an error.
|
||||
error => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env::set_var("RUST_LOG", "matrix_sdk=debug,matrix_sdk_appservice=debug");
|
||||
@@ -41,6 +59,7 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let registration = AppServiceRegistration::try_from_yaml_file("./tests/registration.yaml")?;
|
||||
|
||||
let appservice = AppService::new(homeserver_url, server_name, registration).await?;
|
||||
appservice.register_user_query(Box::new(|_, _| Box::pin(async { true }))).await;
|
||||
appservice
|
||||
.register_event_handler_context(appservice.clone())?
|
||||
.register_event_handler(
|
||||
|
||||
@@ -1043,7 +1043,7 @@ impl BaseClient {
|
||||
content: impl MessageLikeEventContent,
|
||||
) -> Result<RoomEncryptedEventContent> {
|
||||
match self.olm_machine() {
|
||||
Some(o) => Ok(o.encrypt(room_id, content).await?),
|
||||
Some(o) => Ok(o.encrypt_room_event(room_id, content).await?),
|
||||
None => panic!("Olm machine wasn't started"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
mod members;
|
||||
mod normal;
|
||||
|
||||
use std::{cmp::max, collections::HashSet, fmt};
|
||||
use std::{collections::HashSet, fmt};
|
||||
|
||||
pub use members::RoomMember;
|
||||
pub use normal::{Room, RoomInfo, RoomType};
|
||||
@@ -143,11 +143,7 @@ impl BaseRoomInfo {
|
||||
self.tombstone = Some(t.into());
|
||||
}
|
||||
AnySyncStateEvent::RoomPowerLevels(p) => {
|
||||
self.max_power_level = p
|
||||
.power_levels()
|
||||
.users
|
||||
.values()
|
||||
.fold(self.max_power_level, |acc, &p| max(acc, p.into()));
|
||||
self.max_power_level = p.power_levels().max().into();
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
@@ -192,11 +188,7 @@ impl BaseRoomInfo {
|
||||
self.tombstone = Some(t.into());
|
||||
}
|
||||
AnyStrippedStateEvent::RoomPowerLevels(p) => {
|
||||
self.max_power_level = p
|
||||
.content
|
||||
.users
|
||||
.values()
|
||||
.fold(self.max_power_level, |acc, &p| max(acc, p.into()));
|
||||
self.max_power_level = p.power_levels().max().into();
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
|
||||
@@ -177,7 +177,13 @@ impl Room {
|
||||
|
||||
/// Get the avatar url of this room.
|
||||
pub fn avatar_url(&self) -> Option<OwnedMxcUri> {
|
||||
self.inner.read().unwrap().base_info.avatar.as_ref()?.as_original()?.content.url.clone()
|
||||
self.inner
|
||||
.read()
|
||||
.unwrap()
|
||||
.base_info
|
||||
.avatar
|
||||
.as_ref()
|
||||
.and_then(|e| e.as_original().and_then(|e| e.content.url.clone()))
|
||||
}
|
||||
|
||||
/// Get the canonical alias of this room.
|
||||
@@ -194,7 +200,13 @@ impl Room {
|
||||
/// It can also be redacted in current room versions, leaving only the
|
||||
/// `creator` field.
|
||||
pub fn create_content(&self) -> Option<RoomCreateEventContent> {
|
||||
Some(self.inner.read().unwrap().base_info.create.as_ref()?.as_original()?.content.clone())
|
||||
self.inner
|
||||
.read()
|
||||
.unwrap()
|
||||
.base_info
|
||||
.create
|
||||
.as_ref()
|
||||
.and_then(|e| e.as_original().map(|e| e.content.clone()))
|
||||
}
|
||||
|
||||
/// Is this room considered a direct message.
|
||||
|
||||
@@ -10,8 +10,8 @@ license = "Apache-2.0"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
name = "matrix_crypto"
|
||||
crate-type = ["cdylib", "staticlib"]
|
||||
name = "matrix_crypto_ffi"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
@@ -55,7 +55,8 @@ default_features = false
|
||||
features = ["rt-multi-thread"]
|
||||
|
||||
[dependencies.vodozemac]
|
||||
version = "0.2.0"
|
||||
git = "https://github.com/matrix-org/vodozemac/"
|
||||
rev = "d0e744287a14319c2a9148fef3747548c740fc36"
|
||||
|
||||
[build-dependencies]
|
||||
uniffi_build = { version = "0.17.0", features = ["builtin-bindgen"] }
|
||||
|
||||
@@ -48,7 +48,7 @@ pub struct MigrationData {
|
||||
/// The list of Megolm inbound group sessions.
|
||||
inbound_group_sessions: Vec<PickledInboundGroupSession>,
|
||||
/// The Olm pickle key that was used to pickle all the Olm objects.
|
||||
pickle_key: String,
|
||||
pickle_key: Vec<u8>,
|
||||
/// The backup version that is currently active.
|
||||
backup_version: Option<String>,
|
||||
// The backup recovery key, as a base58 encoded string.
|
||||
@@ -521,7 +521,9 @@ mod test {
|
||||
"backed_up":true
|
||||
}
|
||||
],
|
||||
"pickle_key":"\u{0011}$xJ_N8$>{\u{0005}iJoF03eBVt\u{000e}rUU\\,GYc7J",
|
||||
"pickle_key": [17, 36, 120, 74, 95, 78, 56, 36, 62, 123, 5, 105, 74,
|
||||
111, 70, 48, 51, 101, 66, 86, 116, 14, 114, 85, 85,
|
||||
92, 44, 71, 89, 99, 55, 74],
|
||||
"backup_version":"3",
|
||||
"backup_recovery_key":"EsTHScmRV5oT1WBhe2mj2Gn3odeYantZ4NEk7L51p6L8hrmB",
|
||||
"cross_signing":{
|
||||
|
||||
@@ -33,7 +33,7 @@ use ruma::{
|
||||
},
|
||||
events::{
|
||||
key::verification::VerificationMethod, room::encrypted::OriginalSyncRoomEncryptedEvent,
|
||||
AnyMessageLikeEventContent, AnySyncMessageLikeEvent, EventContent,
|
||||
AnySyncMessageLikeEvent,
|
||||
},
|
||||
DeviceKeyAlgorithm, EventId, OwnedTransactionId, OwnedUserId, RoomId, UserId,
|
||||
};
|
||||
@@ -519,12 +519,11 @@ impl OlmMachine {
|
||||
content: &str,
|
||||
) -> Result<String, CryptoStoreError> {
|
||||
let room_id = RoomId::parse(room_id)?;
|
||||
let content: Box<RawValue> = serde_json::from_str(content)?;
|
||||
let content: Value = serde_json::from_str(content)?;
|
||||
|
||||
let content = AnyMessageLikeEventContent::from_parts(event_type, &content)?;
|
||||
let encrypted_content = self
|
||||
.runtime
|
||||
.block_on(self.inner.encrypt(&room_id, content))
|
||||
.block_on(self.inner.encrypt_room_event_raw(&room_id, content, event_type))
|
||||
.expect("Encrypting an event produced an error");
|
||||
|
||||
Ok(serde_json::to_string(&encrypted_content)?)
|
||||
@@ -1331,7 +1330,7 @@ impl OlmMachine {
|
||||
Ok(self.runtime.block_on(self.inner.backup_machine().room_key_counts())?.into())
|
||||
}
|
||||
|
||||
/// Store the recovery key in the cryptostore.
|
||||
/// Store the recovery key in the crypto store.
|
||||
///
|
||||
/// This is useful if the client wants to support gossiping of the backup
|
||||
/// key.
|
||||
|
||||
@@ -451,7 +451,7 @@ dictionary MigrationData {
|
||||
sequence<PickledInboundGroupSession> inbound_group_sessions;
|
||||
string? backup_version;
|
||||
string? backup_recovery_key;
|
||||
string pickle_key;
|
||||
sequence<u8> pickle_key;
|
||||
CrossSigningKeyExport cross_signing;
|
||||
sequence<string> tracked_users;
|
||||
};
|
||||
|
||||
@@ -53,7 +53,8 @@ version = "0.6.1"
|
||||
features = ["client-api-c", "js", "rand", "unstable-msc2676", "unstable-msc2677"]
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies.vodozemac]
|
||||
version = "0.2.0"
|
||||
git = "https://github.com/matrix-org/vodozemac/"
|
||||
rev = "d0e744287a14319c2a9148fef3747548c740fc36"
|
||||
features = ["js"]
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.ruma]
|
||||
@@ -61,7 +62,8 @@ version = "0.6.1"
|
||||
features = ["client-api-c", "rand", "unstable-msc2676", "unstable-msc2677"]
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.vodozemac]
|
||||
version = "0.2.0"
|
||||
git = "https://github.com/matrix-org/vodozemac/"
|
||||
rev = "d0e744287a14319c2a9148fef3747548c740fc36"
|
||||
|
||||
[dev-dependencies]
|
||||
futures = { version = "0.3.21", default-features = false, features = ["executor"] }
|
||||
|
||||
@@ -220,7 +220,7 @@ impl BackupMachine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Store the recovery key in the cryptostore.
|
||||
/// Store the recovery key in the crypto store.
|
||||
///
|
||||
/// This is useful if the client wants to support gossiping of the backup
|
||||
/// key.
|
||||
|
||||
@@ -43,7 +43,7 @@ use crate::{
|
||||
};
|
||||
|
||||
/// An error describing why a key share request won't be honored.
|
||||
#[derive(Debug, Clone, Error, PartialEq)]
|
||||
#[derive(Debug, Clone, Error, PartialEq, Eq)]
|
||||
pub enum KeyForwardDecision {
|
||||
/// The key request is from a device that we don't own, we're only sharing
|
||||
/// sessions that we know the requesting device already was supposed to get.
|
||||
|
||||
@@ -348,7 +348,7 @@ impl UserDevices {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
/// The local trust state of a device.
|
||||
pub enum LocalTrust {
|
||||
/// The device has been verified and is trusted.
|
||||
@@ -456,8 +456,8 @@ impl ReadOnlyDevice {
|
||||
|
||||
/// Set the trust state of the device to the given state.
|
||||
///
|
||||
/// Note: This should only done in the cryptostore where the trust state can
|
||||
/// be stored.
|
||||
/// Note: This should only done in the crypto store where the trust state
|
||||
/// can be stored.
|
||||
pub(crate) fn set_trust_state(&self, state: LocalTrust) {
|
||||
self.trust_state.store(state, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||
use ruma::OwnedRoomId;
|
||||
|
||||
/// Return type for the room key importing.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RoomKeyImportResult {
|
||||
/// The number of room keys that were imported.
|
||||
pub imported_count: usize,
|
||||
|
||||
@@ -648,21 +648,23 @@ impl OlmMachine {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a group session for the given room wasn't shared beforehand.
|
||||
pub async fn encrypt(
|
||||
///
|
||||
/// [`share_group_session`]: Self::share_group_session
|
||||
pub async fn encrypt_room_event(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
content: impl MessageLikeEventContent,
|
||||
) -> MegolmResult<RoomEncryptedEventContent> {
|
||||
let event_type = content.event_type().to_string();
|
||||
let content = serde_json::to_value(&content)?;
|
||||
|
||||
self.group_session_manager.encrypt(room_id, content, &event_type).await
|
||||
self.encrypt_room_event_raw(room_id, content, &event_type).await
|
||||
}
|
||||
|
||||
/// Encrypt a json [`Value`] content for the given room.
|
||||
///
|
||||
/// This method is equivalent to the [`encrypt()`] method but operates on an
|
||||
/// arbitrary JSON value instead of strongly-typed event content struct.
|
||||
/// This method is equivalent to the [`OlmMachine::encrypt_room_event()`]
|
||||
/// method but operates on an arbitrary JSON value instead of strongly-typed
|
||||
/// event content struct.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
@@ -677,9 +679,7 @@ impl OlmMachine {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a group session for the given room wasn't shared beforehand.
|
||||
///
|
||||
/// [`encrypt()`]: #method.encrypt
|
||||
pub async fn encrypt_raw(
|
||||
pub async fn encrypt_room_event_raw(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
content: Value,
|
||||
@@ -1954,7 +1954,7 @@ pub(crate) mod tests {
|
||||
let content = RoomMessageEventContent::text_plain(plaintext);
|
||||
|
||||
let encrypted_content = alice
|
||||
.encrypt(room_id, AnyMessageLikeEventContent::RoomMessage(content.clone()))
|
||||
.encrypt_room_event(room_id, AnyMessageLikeEventContent::RoomMessage(content.clone()))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ use ruma::{
|
||||
},
|
||||
AnyToDeviceEvent, OlmV1Keys,
|
||||
},
|
||||
serde::{CanonicalJsonValue, Raw},
|
||||
serde::Raw,
|
||||
DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, OwnedDeviceId,
|
||||
OwnedDeviceKeyId, OwnedUserId, RoomId, SecondsSinceUnixEpoch, UInt, UserId,
|
||||
};
|
||||
@@ -49,8 +49,8 @@ use vodozemac::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
EncryptionSettings, InboundGroupSession, OutboundGroupSession, PrivateCrossSigningIdentity,
|
||||
Session,
|
||||
utility::SignJson, EncryptionSettings, InboundGroupSession, OutboundGroupSession,
|
||||
PrivateCrossSigningIdentity, Session,
|
||||
};
|
||||
use crate::{
|
||||
error::{EventError, OlmResult, SessionCreationError},
|
||||
@@ -752,10 +752,14 @@ impl ReadOnlyAccount {
|
||||
// get signed.
|
||||
let json_device_keys =
|
||||
serde_json::to_value(&device_keys).expect("device key is always safe to serialize");
|
||||
let signature = self
|
||||
.sign_json(json_device_keys)
|
||||
.await
|
||||
.expect("Newly created device keys can always be signed");
|
||||
|
||||
device_keys.signatures.entry(self.user_id().to_owned()).or_default().insert(
|
||||
DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.device_id),
|
||||
self.sign_json(json_device_keys).await.to_base64(),
|
||||
signature.to_base64(),
|
||||
);
|
||||
|
||||
device_keys
|
||||
@@ -773,7 +777,7 @@ impl ReadOnlyAccount {
|
||||
&self,
|
||||
cross_signing_key: &mut CrossSigningKey,
|
||||
) -> Result<(), SignatureError> {
|
||||
let signature = self.sign_json(serde_json::to_value(&cross_signing_key)?).await;
|
||||
let signature = self.sign_json(serde_json::to_value(&cross_signing_key)?).await?;
|
||||
|
||||
cross_signing_key.signatures.entry(self.user_id().to_owned()).or_default().insert(
|
||||
DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
|
||||
@@ -809,19 +813,8 @@ impl ReadOnlyAccount {
|
||||
///
|
||||
/// * `json` - The value that should be converted into a canonical JSON
|
||||
/// string.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panics if the json value can't be serialized.
|
||||
pub async fn sign_json(&self, mut json: Value) -> Ed25519Signature {
|
||||
let object = json.as_object_mut().expect("Canonical json value isn't an object");
|
||||
object.remove("unsigned");
|
||||
object.remove("signatures");
|
||||
|
||||
let canonical_json: CanonicalJsonValue =
|
||||
json.try_into().expect("Can't canonicalize the json value");
|
||||
|
||||
self.sign(&canonical_json.to_string()).await
|
||||
pub async fn sign_json(&self, json: Value) -> Result<Ed25519Signature, SignatureError> {
|
||||
self.inner.lock().await.sign_json(json)
|
||||
}
|
||||
|
||||
/// Generate, sign and prepare one-time keys to be uploaded.
|
||||
@@ -885,8 +878,10 @@ impl ReadOnlyAccount {
|
||||
SignedKey::new(key.to_owned())
|
||||
};
|
||||
|
||||
let signature =
|
||||
self.sign_json(serde_json::to_value(&key).expect("Can't serialize a signed key")).await;
|
||||
let signature = self
|
||||
.sign_json(serde_json::to_value(&key).expect("Can't serialize a signed key"))
|
||||
.await
|
||||
.expect("Newly created one-time keys can always be signed");
|
||||
|
||||
let signatures = BTreeMap::from([(
|
||||
self.user_id().to_owned(),
|
||||
@@ -1012,8 +1007,7 @@ impl ReadOnlyAccount {
|
||||
message: &PreKeyMessage,
|
||||
) -> Result<InboundCreationResult, SessionCreationError> {
|
||||
let their_identity_key = Curve25519PublicKey::from_base64(their_identity_key)?;
|
||||
let result =
|
||||
self.inner.lock().await.create_inbound_session(&their_identity_key, message)?;
|
||||
let result = self.inner.lock().await.create_inbound_session(their_identity_key, message)?;
|
||||
|
||||
let now = SecondsSinceUnixEpoch::now();
|
||||
let session_id = result.session.session_id();
|
||||
|
||||
@@ -651,13 +651,16 @@ impl PrivateCrossSigningIdentity {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use matrix_sdk_test::async_test;
|
||||
use ruma::{device_id, user_id, UserId};
|
||||
use ruma::{device_id, user_id, DeviceKeyAlgorithm, DeviceKeyId, UserId};
|
||||
use serde_json::json;
|
||||
|
||||
use super::{PrivateCrossSigningIdentity, Signing};
|
||||
use crate::{
|
||||
identities::{ReadOnlyDevice, ReadOnlyUserIdentity},
|
||||
olm::ReadOnlyAccount,
|
||||
olm::{utility::SignJson, ReadOnlyAccount},
|
||||
};
|
||||
|
||||
fn user_id() -> &'static UserId {
|
||||
@@ -667,11 +670,25 @@ mod tests {
|
||||
#[test]
|
||||
fn signature_verification() {
|
||||
let signing = Signing::new();
|
||||
let user_id = user_id();
|
||||
let key_id = DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, "DEVICEID".into());
|
||||
|
||||
let message = "Hello world";
|
||||
let json = json!({
|
||||
"hello": "world"
|
||||
});
|
||||
|
||||
let signature = signing.sign(message);
|
||||
assert!(signing.verify(message, &signature).is_ok());
|
||||
let signature =
|
||||
signing.sign_json(json).expect("We should be able to sign a simple json object");
|
||||
let signatures =
|
||||
BTreeMap::from([(user_id, BTreeMap::from([(key_id.clone(), signature.to_base64())]))]);
|
||||
|
||||
let mut json = json!({
|
||||
"hello": "world",
|
||||
"signatures": signatures,
|
||||
|
||||
});
|
||||
|
||||
assert!(signing.verify_json(user_id, &key_id, &mut json).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -12,11 +12,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::{collections::BTreeMap, convert::TryInto};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use ruma::{
|
||||
encryption::KeyUsage, serde::CanonicalJsonValue, DeviceKeyAlgorithm, DeviceKeyId, OwnedUserId,
|
||||
};
|
||||
use ruma::{encryption::KeyUsage, DeviceKeyAlgorithm, DeviceKeyId, OwnedUserId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Error as JsonError, Value};
|
||||
use thiserror::Error;
|
||||
@@ -25,6 +23,7 @@ use vodozemac::{Ed25519PublicKey, Ed25519SecretKey, Ed25519Signature, KeyError};
|
||||
use crate::{
|
||||
error::SignatureError,
|
||||
identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey},
|
||||
olm::utility::SignJson,
|
||||
types::{CrossSigningKey, CrossSigningKeySignatures, DeviceKeys},
|
||||
utilities::{encode, DecodeError},
|
||||
ReadOnlyUserIdentity,
|
||||
@@ -64,6 +63,12 @@ impl PartialEq for Signing {
|
||||
}
|
||||
}
|
||||
|
||||
impl SignJson for Signing {
|
||||
fn sign_json(&self, value: Value) -> Result<Ed25519Signature, SignatureError> {
|
||||
self.inner.sign_json(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct MasterSigning {
|
||||
pub inner: Signing,
|
||||
@@ -215,12 +220,9 @@ impl SelfSigning {
|
||||
Ok(Self { inner, public_key })
|
||||
}
|
||||
|
||||
pub fn sign_device_helper(&self, value: Value) -> Result<Ed25519Signature, SignatureError> {
|
||||
self.inner.sign_json(value)
|
||||
}
|
||||
|
||||
pub fn sign_device(&self, device_keys: &mut DeviceKeys) -> Result<(), SignatureError> {
|
||||
let signature = self.sign_device_helper(serde_json::to_value(&device_keys)?)?;
|
||||
let serialized = serde_json::to_value(&device_keys)?;
|
||||
let signature = self.inner.sign_json(serialized)?;
|
||||
|
||||
device_keys.signatures.entry(self.public_key.user_id().to_owned()).or_default().insert(
|
||||
DeviceKeyId::from_parts(
|
||||
@@ -311,27 +313,18 @@ impl Signing {
|
||||
CrossSigningKey::new(user_id, vec![usage], keys, BTreeMap::new())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn verify(
|
||||
&self,
|
||||
message: &str,
|
||||
signature: &Ed25519Signature,
|
||||
) -> Result<(), SignatureError> {
|
||||
Ok(self.public_key.verify(message.as_bytes(), signature)?)
|
||||
}
|
||||
|
||||
pub fn sign_json(&self, mut json: Value) -> Result<Ed25519Signature, SignatureError> {
|
||||
let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?;
|
||||
let _ = json_object.remove("signatures");
|
||||
let _ = json_object.remove("unsigned");
|
||||
|
||||
let canonical_json: CanonicalJsonValue =
|
||||
json.try_into().expect("Can't canonicalize the json value");
|
||||
|
||||
Ok(self.sign(&canonical_json.to_string()))
|
||||
}
|
||||
|
||||
pub fn sign(&self, message: &str) -> Ed25519Signature {
|
||||
self.inner.sign(message.as_bytes())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn verify_json(
|
||||
&self,
|
||||
user_id: &ruma::UserId,
|
||||
key_id: &DeviceKeyId,
|
||||
message: &mut Value,
|
||||
) -> Result<(), SignatureError> {
|
||||
use crate::olm::VerifyJson;
|
||||
self.public_key.verify_json(user_id, key_id, message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,39 @@ use std::convert::TryInto;
|
||||
|
||||
use ruma::{serde::CanonicalJsonValue, DeviceKeyAlgorithm, DeviceKeyId, UserId};
|
||||
use serde_json::Value;
|
||||
use vodozemac::{olm::Account, Ed25519SecretKey, Ed25519Signature};
|
||||
|
||||
use crate::error::SignatureError;
|
||||
|
||||
pub trait SignJson {
|
||||
fn sign_json(&self, value: Value) -> Result<Ed25519Signature, SignatureError>;
|
||||
|
||||
fn to_signable_json(mut value: Value) -> Result<String, SignatureError> {
|
||||
let json_object = value.as_object_mut().ok_or(SignatureError::NotAnObject)?;
|
||||
let _ = json_object.remove("signatures");
|
||||
let _ = json_object.remove("unsigned");
|
||||
|
||||
let canonical_json: CanonicalJsonValue = value.try_into().unwrap();
|
||||
Ok(canonical_json.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl SignJson for Account {
|
||||
fn sign_json(&self, value: Value) -> Result<Ed25519Signature, SignatureError> {
|
||||
let serialized = Self::to_signable_json(value)?;
|
||||
|
||||
Ok(self.sign(serialized.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl SignJson for Ed25519SecretKey {
|
||||
fn sign_json(&self, value: Value) -> Result<Ed25519Signature, SignatureError> {
|
||||
let serialized = Self::to_signable_json(value)?;
|
||||
|
||||
Ok(self.sign(serialized.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VerifyJson {
|
||||
/// Verify a signed JSON object.
|
||||
///
|
||||
|
||||
@@ -640,8 +640,8 @@ impl CryptoStoreError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait abstracting a store that the `OlmMachine` uses to store cryptographic
|
||||
/// keys.
|
||||
/// Represents a store that the `OlmMachine` uses to store E2EE data (such as
|
||||
/// cryptographic keys).
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait CryptoStore: AsyncTraitDeps {
|
||||
|
||||
@@ -77,7 +77,7 @@ impl CrossSigningKey {
|
||||
/// Currently cross signing keys support an ed25519 keypair. The keys transport
|
||||
/// format is a base64 encoded string, any unknown key type will be left as such
|
||||
/// a string.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum SigningKey {
|
||||
/// The ed25519 cross-signing key.
|
||||
Ed25519(Ed25519PublicKey),
|
||||
|
||||
@@ -115,7 +115,7 @@ impl UnsignedDeviceInfo {
|
||||
/// Currently devices have a curve25519 and ed25519 keypair. The keys transport
|
||||
/// format is a base64 encoded string, any unknown key type will be left as such
|
||||
/// a string.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum DeviceKey {
|
||||
/// The curve25519 device key.
|
||||
Curve25519(Curve25519PublicKey),
|
||||
|
||||
@@ -29,7 +29,7 @@ use vodozemac::{Curve25519PublicKey, Ed25519Signature};
|
||||
pub type SignedKeySignatures = BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, Ed25519Signature>>;
|
||||
|
||||
/// A key for the SignedCurve25519 algorithm
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct SignedKey {
|
||||
// /// The Curve25519 key that can be used to establish Olm sessions.
|
||||
#[serde(deserialize_with = "deserialize_curve_key", serialize_with = "serialize_curve_key")]
|
||||
@@ -151,7 +151,7 @@ impl SignedKey {
|
||||
}
|
||||
|
||||
/// A one-time public key for "pre-key" messages.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(untagged)]
|
||||
pub enum OneTimeKey {
|
||||
/// A signed Curve25519 one-time key.
|
||||
|
||||
@@ -376,7 +376,7 @@ impl Cancelled {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, PartialOrd)]
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd)]
|
||||
pub enum FlowId {
|
||||
ToDevice(OwnedTransactionId),
|
||||
InRoom(OwnedRoomId, OwnedEventId),
|
||||
|
||||
35
crates/matrix-sdk-ffi/Cargo.toml
Normal file
35
crates/matrix-sdk-ffi/Cargo.toml
Normal file
@@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "matrix-sdk-ffi"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
homepage = "https://github.com/matrix-org/matrix-rust-sdk"
|
||||
keywords = ["matrix", "chat", "messaging", "ffi"]
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
rust-version = "1.56"
|
||||
repository = "https://github.com/matrix-org/matrix-rust-sdk"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "staticlib"]
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
uniffi_build = { version = "0.18.0", features = ["builtin-bindgen"] }
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.51"
|
||||
extension-trait = "1.0.1"
|
||||
futures-core = "0.3.17"
|
||||
futures-util = { version = "0.3.17", default-features = false }
|
||||
matrix-sdk = { path = "../matrix-sdk", features = ["experimental-timeline", "markdown"] }
|
||||
once_cell = "1.10.0"
|
||||
parking_lot = "0.12.0"
|
||||
sanitize-filename-reader-friendly = "2.2.1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = { version = "1" }
|
||||
thiserror = "1.0.30"
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
||||
tokio-stream = "0.1.8"
|
||||
tracing = "0.1.32"
|
||||
uniffi = "0.18.0"
|
||||
uniffi_macros = "0.18.0"
|
||||
13
crates/matrix-sdk-ffi/README.md
Normal file
13
crates/matrix-sdk-ffi/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# FFI bindings for the rust matrix SDK
|
||||
|
||||
This uses [´uniffi´](https://mozilla.github.io/uniffi-rs/Overview.html) to build the matrix bindings for native support and wasm-bindgen for web-browser assembly support. Please refer to the specific section to figure out how to build and use the bindings for your platform.
|
||||
|
||||
## Platforms
|
||||
|
||||
### Swift/iOS sync
|
||||
|
||||
|
||||
|
||||
### Swift/iOS async
|
||||
|
||||
TBD
|
||||
3
crates/matrix-sdk-ffi/build.rs
Normal file
3
crates/matrix-sdk-ffi/build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
uniffi_build::generate_scaffolding("./src/api.udl").expect("Building the UDL file failed");
|
||||
}
|
||||
137
crates/matrix-sdk-ffi/src/api.udl
Normal file
137
crates/matrix-sdk-ffi/src/api.udl
Normal file
@@ -0,0 +1,137 @@
|
||||
namespace sdk {
|
||||
[Throws=ClientError]
|
||||
Client login_new_client(string base_path, string username, string password);
|
||||
|
||||
[Throws=ClientError]
|
||||
Client guest_client(string base_path, string homeserver);
|
||||
|
||||
[Throws=ClientError]
|
||||
Client login_with_token(string base_path, string restore_token);
|
||||
|
||||
MediaSource media_source_from_url(string url);
|
||||
MessageEventContent message_event_content_from_markdown(string md);
|
||||
string gen_transaction_id();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface ClientError {
|
||||
Generic(string msg);
|
||||
};
|
||||
|
||||
callback interface ClientDelegate {
|
||||
void did_receive_sync_update();
|
||||
};
|
||||
|
||||
interface Client {
|
||||
void set_delegate(ClientDelegate? delegate);
|
||||
|
||||
void start_sync();
|
||||
|
||||
[Throws=ClientError]
|
||||
string restore_token();
|
||||
|
||||
boolean is_guest();
|
||||
|
||||
boolean has_first_synced();
|
||||
|
||||
boolean is_syncing();
|
||||
|
||||
[Throws=ClientError]
|
||||
string user_id();
|
||||
|
||||
[Throws=ClientError]
|
||||
string display_name();
|
||||
|
||||
[Throws=ClientError]
|
||||
string avatar_url();
|
||||
|
||||
[Throws=ClientError]
|
||||
string device_id();
|
||||
|
||||
sequence<Room> rooms();
|
||||
|
||||
[Throws=ClientError]
|
||||
sequence<u8> get_media_content(MediaSource source);
|
||||
};
|
||||
|
||||
callback interface RoomDelegate {
|
||||
void did_receive_message(AnyMessage message);
|
||||
};
|
||||
|
||||
interface Room {
|
||||
void set_delegate(RoomDelegate? delegate);
|
||||
|
||||
string id();
|
||||
string? name();
|
||||
string? topic();
|
||||
string? avatar_url();
|
||||
|
||||
boolean is_direct();
|
||||
boolean is_public();
|
||||
boolean is_space();
|
||||
boolean is_encrypted();
|
||||
boolean is_tombstoned();
|
||||
|
||||
[Throws=ClientError]
|
||||
string display_name();
|
||||
|
||||
[Throws=ClientError]
|
||||
string? member_avatar_url(string user_id);
|
||||
|
||||
[Throws=ClientError]
|
||||
string? member_display_name(string user_id);
|
||||
|
||||
BackwardsStream? start_live_event_listener();
|
||||
void stop_live_event_listener();
|
||||
|
||||
[Throws=ClientError]
|
||||
void send(MessageEventContent msg, string? txn_id);
|
||||
};
|
||||
|
||||
interface BackwardsStream {
|
||||
sequence<AnyMessage> paginate_backwards(u64 count);
|
||||
};
|
||||
|
||||
interface MessageEventContent {};
|
||||
|
||||
interface AnyMessage {
|
||||
TextMessage? text_message();
|
||||
ImageMessage? image_message();
|
||||
NoticeMessage? notice_message();
|
||||
EmoteMessage? emote_message();
|
||||
};
|
||||
|
||||
interface BaseMessage {
|
||||
string id();
|
||||
string body();
|
||||
string sender();
|
||||
u64 origin_server_ts();
|
||||
string? transaction_id();
|
||||
};
|
||||
|
||||
interface TextMessage {
|
||||
BaseMessage base_message();
|
||||
string? html_body();
|
||||
};
|
||||
|
||||
interface ImageMessage {
|
||||
BaseMessage base_message();
|
||||
MediaSource source();
|
||||
u64? width();
|
||||
u64? height();
|
||||
string? blurhash();
|
||||
};
|
||||
|
||||
interface NoticeMessage {
|
||||
BaseMessage base_message();
|
||||
string? html_body();
|
||||
};
|
||||
|
||||
interface EmoteMessage {
|
||||
BaseMessage base_message();
|
||||
string? html_body();
|
||||
};
|
||||
|
||||
interface MediaSource {
|
||||
string url();
|
||||
};
|
||||
42
crates/matrix-sdk-ffi/src/backward_stream.rs
Normal file
42
crates/matrix-sdk-ffi/src/backward_stream.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use core::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures_core::Stream;
|
||||
use matrix_sdk::{deserialized_responses::SyncRoomEvent, locks::Mutex, Result};
|
||||
use tokio_stream::StreamExt;
|
||||
use tracing::error;
|
||||
|
||||
use super::{
|
||||
messages::{sync_event_to_message, AnyMessage},
|
||||
RUNTIME,
|
||||
};
|
||||
|
||||
type MsgStream = Pin<Box<dyn Stream<Item = Result<SyncRoomEvent>> + Send>>;
|
||||
|
||||
pub struct BackwardsStream {
|
||||
stream: Arc<Mutex<MsgStream>>,
|
||||
}
|
||||
|
||||
impl BackwardsStream {
|
||||
pub fn new(stream: MsgStream) -> Self {
|
||||
BackwardsStream { stream: Arc::new(Mutex::new(Box::pin(stream))) }
|
||||
}
|
||||
|
||||
pub fn paginate_backwards(&self, count: u64) -> Vec<Arc<AnyMessage>> {
|
||||
let stream = self.stream.clone();
|
||||
RUNTIME.block_on(async move {
|
||||
let mut stream = stream.lock().await;
|
||||
(&mut *stream)
|
||||
.take(count as usize)
|
||||
.filter_map(|r| match r {
|
||||
Ok(ev) => sync_event_to_message(ev),
|
||||
Err(e) => {
|
||||
error!("Pagniation error: {e}");
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
162
crates/matrix-sdk-ffi/src/client.rs
Normal file
162
crates/matrix-sdk-ffi/src/client.rs
Normal file
@@ -0,0 +1,162 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use matrix_sdk::{
|
||||
config::SyncSettings,
|
||||
media::{MediaFormat, MediaRequest},
|
||||
ruma::{
|
||||
api::client::{
|
||||
filter::{FilterDefinition, LazyLoadOptions, RoomEventFilter, RoomFilter},
|
||||
sync::sync_events::v3::Filter,
|
||||
},
|
||||
events::room::MediaSource,
|
||||
TransactionId,
|
||||
},
|
||||
Client as MatrixClient, LoopCtrl,
|
||||
};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use super::{room::Room, ClientState, RestoreToken, RUNTIME};
|
||||
|
||||
impl std::ops::Deref for Client {
|
||||
type Target = MatrixClient;
|
||||
fn deref(&self) -> &MatrixClient {
|
||||
&self.client
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ClientDelegate: Sync + Send {
|
||||
fn did_receive_sync_update(&self);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Client {
|
||||
client: MatrixClient,
|
||||
state: Arc<RwLock<ClientState>>,
|
||||
delegate: Arc<RwLock<Option<Box<dyn ClientDelegate>>>>,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new(client: MatrixClient, state: ClientState) -> Self {
|
||||
Client {
|
||||
client,
|
||||
state: Arc::new(RwLock::new(state)),
|
||||
delegate: Arc::new(RwLock::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_delegate(&self, delegate: Option<Box<dyn ClientDelegate>>) {
|
||||
*self.delegate.write() = delegate;
|
||||
}
|
||||
|
||||
pub fn start_sync(&self) {
|
||||
let client = self.client.clone();
|
||||
let state = self.state.clone();
|
||||
let delegate = self.delegate.clone();
|
||||
RUNTIME.spawn(async move {
|
||||
let mut filter = FilterDefinition::default();
|
||||
let mut room_filter = RoomFilter::default();
|
||||
let mut event_filter = RoomEventFilter::default();
|
||||
|
||||
event_filter.lazy_load_options =
|
||||
LazyLoadOptions::Enabled { include_redundant_members: false };
|
||||
room_filter.state = event_filter;
|
||||
filter.room = room_filter;
|
||||
|
||||
let filter_id = client.get_or_upload_filter("sync", filter).await.unwrap();
|
||||
|
||||
let sync_settings = SyncSettings::new().filter(Filter::FilterId(&filter_id));
|
||||
|
||||
client
|
||||
.sync_with_callback(sync_settings, |_| async {
|
||||
if !state.read().has_first_synced {
|
||||
state.write().has_first_synced = true
|
||||
}
|
||||
|
||||
if state.read().should_stop_syncing {
|
||||
state.write().is_syncing = false;
|
||||
return LoopCtrl::Break;
|
||||
} else if !state.read().is_syncing {
|
||||
state.write().is_syncing = true;
|
||||
}
|
||||
|
||||
if let Some(ref delegate) = *delegate.read() {
|
||||
delegate.did_receive_sync_update()
|
||||
}
|
||||
LoopCtrl::Continue
|
||||
})
|
||||
.await;
|
||||
});
|
||||
}
|
||||
|
||||
/// Indication whether we've received a first sync response since
|
||||
/// establishing the client (in memory)
|
||||
pub fn has_first_synced(&self) -> bool {
|
||||
self.state.read().has_first_synced
|
||||
}
|
||||
|
||||
/// Indication whether we are currently syncing
|
||||
pub fn is_syncing(&self) -> bool {
|
||||
self.state.read().has_first_synced
|
||||
}
|
||||
|
||||
/// Is this a guest account?
|
||||
pub fn is_guest(&self) -> bool {
|
||||
self.state.read().is_guest
|
||||
}
|
||||
|
||||
pub fn restore_token(&self) -> anyhow::Result<String> {
|
||||
RUNTIME.block_on(async move {
|
||||
let session = self.client.session().expect("Missing session").clone();
|
||||
let homeurl = self.client.homeserver().await.into();
|
||||
Ok(serde_json::to_string(&RestoreToken {
|
||||
session,
|
||||
homeurl,
|
||||
is_guest: self.state.read().is_guest,
|
||||
})?)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn rooms(&self) -> Vec<Arc<Room>> {
|
||||
self.client.rooms().into_iter().map(|room| Arc::new(Room::new(room))).collect()
|
||||
}
|
||||
|
||||
pub fn user_id(&self) -> anyhow::Result<String> {
|
||||
let user_id = self.client.user_id().expect("No User ID found");
|
||||
Ok(user_id.to_string())
|
||||
}
|
||||
|
||||
pub fn display_name(&self) -> anyhow::Result<String> {
|
||||
let l = self.client.clone();
|
||||
RUNTIME.block_on(async move {
|
||||
let display_name = l.account().get_display_name().await?.expect("No User ID found");
|
||||
Ok(display_name)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn avatar_url(&self) -> anyhow::Result<String> {
|
||||
let l = self.client.clone();
|
||||
RUNTIME.block_on(async move {
|
||||
let avatar_url = l.account().get_avatar_url().await?.expect("No User ID found");
|
||||
Ok(avatar_url.to_string())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn device_id(&self) -> anyhow::Result<String> {
|
||||
let device_id = self.client.device_id().expect("No Device ID found");
|
||||
Ok(device_id.to_string())
|
||||
}
|
||||
|
||||
pub fn get_media_content(&self, media_source: Arc<MediaSource>) -> anyhow::Result<Vec<u8>> {
|
||||
let l = self.client.clone();
|
||||
let source = (*media_source).clone();
|
||||
|
||||
RUNTIME.block_on(async move {
|
||||
Ok(l.get_media_content(&MediaRequest { source, format: MediaFormat::File }, true)
|
||||
.await?)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_transaction_id() -> String {
|
||||
TransactionId::new().to_string()
|
||||
}
|
||||
110
crates/matrix-sdk-ffi/src/lib.rs
Normal file
110
crates/matrix-sdk-ffi/src/lib.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
// TODO: target-os conditional would be good.
|
||||
|
||||
#![allow(unused_qualifications)]
|
||||
|
||||
pub mod backward_stream;
|
||||
pub mod client;
|
||||
pub mod messages;
|
||||
pub mod room;
|
||||
mod uniffi_api;
|
||||
|
||||
use std::{fs, path, sync::Arc};
|
||||
|
||||
use client::Client;
|
||||
use matrix_sdk::{store::make_store_config, Client as MatrixClient, ClientBuilder, Session};
|
||||
use once_cell::sync::Lazy;
|
||||
use sanitize_filename_reader_friendly::sanitize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::runtime::Runtime;
|
||||
pub use uniffi_api::*;
|
||||
|
||||
pub static RUNTIME: Lazy<Runtime> =
|
||||
Lazy::new(|| Runtime::new().expect("Can't start Tokio runtime"));
|
||||
|
||||
pub use matrix_sdk::ruma::{api::client::account::register, UserId};
|
||||
|
||||
pub use self::{backward_stream::*, client::*, messages::*, room::*};
|
||||
|
||||
pub fn guest_client(base_path: String, homeurl: String) -> anyhow::Result<Arc<Client>> {
|
||||
let builder = new_client_builder(base_path, homeurl.clone())?.homeserver_url(&homeurl);
|
||||
let mut guest_registration = register::v3::Request::new();
|
||||
guest_registration.kind = register::RegistrationKind::Guest;
|
||||
RUNTIME.block_on(async move {
|
||||
let client = builder.build().await?;
|
||||
let register = client.register(guest_registration).await?;
|
||||
let session = Session {
|
||||
access_token: register.access_token.expect("no access token given"),
|
||||
user_id: register.user_id,
|
||||
device_id: register.device_id.clone().expect("device id is given by server"),
|
||||
};
|
||||
client.restore_login(session).await?;
|
||||
let c = Client::new(client, ClientState { is_guest: true, ..ClientState::default() });
|
||||
Ok(Arc::new(c))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn login_with_token(base_path: String, restore_token: String) -> anyhow::Result<Arc<Client>> {
|
||||
let RestoreToken { session, homeurl, is_guest } = serde_json::from_str(&restore_token)?;
|
||||
let builder = new_client_builder(base_path, session.user_id.to_string())?
|
||||
.homeserver_url(&homeurl)
|
||||
.user_id(&session.user_id);
|
||||
// First we need to log in.
|
||||
RUNTIME.block_on(async move {
|
||||
let client = builder.build().await?;
|
||||
client.restore_login(session).await?;
|
||||
let c = Client::new(client, ClientState { is_guest, ..ClientState::default() });
|
||||
Ok(Arc::new(c))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn login_new_client(
|
||||
base_path: String,
|
||||
username: String,
|
||||
password: String,
|
||||
) -> anyhow::Result<Arc<Client>> {
|
||||
let builder = new_client_builder(base_path, username.clone())?;
|
||||
let user = Box::<UserId>::try_from(username)?;
|
||||
// First we need to log in.
|
||||
RUNTIME.block_on(async move {
|
||||
let client = builder.user_id(&user).build().await?;
|
||||
client.login(user, &password, None, None).await?;
|
||||
let c = Client::new(client, ClientState { is_guest: false, ..ClientState::default() });
|
||||
Ok(Arc::new(c))
|
||||
})
|
||||
}
|
||||
|
||||
fn new_client_builder(base_path: String, home: String) -> anyhow::Result<ClientBuilder> {
|
||||
let data_path = path::PathBuf::from(base_path).join(sanitize(&home));
|
||||
|
||||
fs::create_dir_all(&data_path)?;
|
||||
let store_config = make_store_config(&data_path, None)?;
|
||||
|
||||
Ok(MatrixClient::builder().user_agent("rust-sdk-ios").store_config(store_config))
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ClientState {
|
||||
is_guest: bool,
|
||||
has_first_synced: bool,
|
||||
is_syncing: bool,
|
||||
should_stop_syncing: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct RestoreToken {
|
||||
is_guest: bool,
|
||||
homeurl: String,
|
||||
session: Session,
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum ClientError {
|
||||
#[error("client error: {msg}")]
|
||||
Generic { msg: String },
|
||||
}
|
||||
|
||||
impl From<anyhow::Error> for ClientError {
|
||||
fn from(e: anyhow::Error) -> ClientError {
|
||||
ClientError::Generic { msg: e.to_string() }
|
||||
}
|
||||
}
|
||||
254
crates/matrix-sdk-ffi/src/messages.rs
Normal file
254
crates/matrix-sdk-ffi/src/messages.rs
Normal file
@@ -0,0 +1,254 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use extension_trait::extension_trait;
|
||||
pub use matrix_sdk::ruma::events::room::{
|
||||
message::RoomMessageEventContent as MessageEventContent, MediaSource,
|
||||
};
|
||||
use matrix_sdk::{
|
||||
deserialized_responses::SyncRoomEvent,
|
||||
ruma::events::{
|
||||
room::{
|
||||
message::{ImageMessageEventContent, MessageFormat, MessageType},
|
||||
ImageInfo,
|
||||
},
|
||||
AnySyncMessageLikeEvent, AnySyncRoomEvent, SyncMessageLikeEvent,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BaseMessage {
|
||||
id: String,
|
||||
body: String,
|
||||
sender: String,
|
||||
origin_server_ts: u64,
|
||||
transaction_id: Option<String>,
|
||||
}
|
||||
|
||||
impl BaseMessage {
|
||||
pub fn id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
pub fn body(&self) -> String {
|
||||
self.body.clone()
|
||||
}
|
||||
|
||||
pub fn sender(&self) -> String {
|
||||
self.sender.clone()
|
||||
}
|
||||
|
||||
pub fn origin_server_ts(&self) -> u64 {
|
||||
self.origin_server_ts
|
||||
}
|
||||
|
||||
pub fn transaction_id(&self) -> Option<String> {
|
||||
self.transaction_id.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextMessage {
|
||||
base_message: Arc<BaseMessage>,
|
||||
html_body: Option<String>,
|
||||
}
|
||||
|
||||
impl TextMessage {
|
||||
pub fn base_message(&self) -> Arc<BaseMessage> {
|
||||
self.base_message.clone()
|
||||
}
|
||||
|
||||
pub fn html_body(&self) -> Option<String> {
|
||||
self.html_body.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ImageMessage {
|
||||
base_message: Arc<BaseMessage>,
|
||||
source: Arc<MediaSource>,
|
||||
info: Option<Box<ImageInfo>>,
|
||||
}
|
||||
|
||||
impl ImageMessage {
|
||||
pub fn base_message(&self) -> Arc<BaseMessage> {
|
||||
self.base_message.clone()
|
||||
}
|
||||
|
||||
pub fn source(&self) -> Arc<MediaSource> {
|
||||
self.source.clone()
|
||||
}
|
||||
|
||||
pub fn width(&self) -> Option<u64> {
|
||||
self.info.clone()?.width?.try_into().ok()
|
||||
}
|
||||
|
||||
pub fn height(&self) -> Option<u64> {
|
||||
self.info.clone()?.height?.try_into().ok()
|
||||
}
|
||||
|
||||
pub fn blurhash(&self) -> Option<String> {
|
||||
self.info.clone()?.blurhash
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NoticeMessage {
|
||||
base_message: Arc<BaseMessage>,
|
||||
html_body: Option<String>,
|
||||
}
|
||||
|
||||
impl NoticeMessage {
|
||||
pub fn base_message(&self) -> Arc<BaseMessage> {
|
||||
self.base_message.clone()
|
||||
}
|
||||
|
||||
pub fn html_body(&self) -> Option<String> {
|
||||
self.html_body.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EmoteMessage {
|
||||
base_message: Arc<BaseMessage>,
|
||||
html_body: Option<String>,
|
||||
}
|
||||
|
||||
impl EmoteMessage {
|
||||
pub fn base_message(&self) -> Arc<BaseMessage> {
|
||||
self.base_message.clone()
|
||||
}
|
||||
|
||||
pub fn html_body(&self) -> Option<String> {
|
||||
self.html_body.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyMessage {
|
||||
text: Option<Arc<TextMessage>>,
|
||||
image: Option<Arc<ImageMessage>>,
|
||||
notice: Option<Arc<NoticeMessage>>,
|
||||
emote: Option<Arc<EmoteMessage>>,
|
||||
}
|
||||
|
||||
impl AnyMessage {
|
||||
pub fn text_message(&self) -> Option<Arc<TextMessage>> {
|
||||
self.text.clone()
|
||||
}
|
||||
|
||||
pub fn image_message(&self) -> Option<Arc<ImageMessage>> {
|
||||
self.image.clone()
|
||||
}
|
||||
|
||||
pub fn notice_message(&self) -> Option<Arc<NoticeMessage>> {
|
||||
self.notice.clone()
|
||||
}
|
||||
|
||||
pub fn emote_message(&self) -> Option<Arc<EmoteMessage>> {
|
||||
self.emote.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sync_event_to_message(sync_event: SyncRoomEvent) -> Option<Arc<AnyMessage>> {
|
||||
match sync_event.event.deserialize() {
|
||||
Ok(AnySyncRoomEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage(
|
||||
SyncMessageLikeEvent::Original(m),
|
||||
))) => {
|
||||
let base_message = Arc::new(BaseMessage {
|
||||
id: m.event_id.to_string(),
|
||||
body: m.content.body().to_owned(),
|
||||
sender: m.sender.to_string(),
|
||||
origin_server_ts: m.origin_server_ts.as_secs().into(),
|
||||
transaction_id: m.unsigned.transaction_id.map(|txn_id| txn_id.to_string()),
|
||||
});
|
||||
|
||||
match m.content.msgtype {
|
||||
MessageType::Image(ImageMessageEventContent { source, info, .. }) => {
|
||||
let any_message = AnyMessage {
|
||||
text: None,
|
||||
image: Some(Arc::new(ImageMessage {
|
||||
base_message,
|
||||
source: Arc::new(source),
|
||||
info,
|
||||
})),
|
||||
notice: None,
|
||||
emote: None,
|
||||
};
|
||||
|
||||
Some(Arc::new(any_message))
|
||||
}
|
||||
MessageType::Text(content) => {
|
||||
let mut html_body: Option<String> = None;
|
||||
if let Some(formatted_body) = content.formatted {
|
||||
if formatted_body.format == MessageFormat::Html {
|
||||
html_body = Some(formatted_body.body);
|
||||
}
|
||||
}
|
||||
|
||||
let any_message = AnyMessage {
|
||||
text: Some(Arc::new(TextMessage { base_message, html_body })),
|
||||
image: None,
|
||||
notice: None,
|
||||
emote: None,
|
||||
};
|
||||
Some(Arc::new(any_message))
|
||||
}
|
||||
MessageType::Notice(content) => {
|
||||
let mut html_body: Option<String> = None;
|
||||
if let Some(formatted_body) = content.formatted {
|
||||
if formatted_body.format == MessageFormat::Html {
|
||||
html_body = Some(formatted_body.body);
|
||||
}
|
||||
}
|
||||
|
||||
let any_message = AnyMessage {
|
||||
text: None,
|
||||
image: None,
|
||||
notice: Some(Arc::new(NoticeMessage { base_message, html_body })),
|
||||
emote: None,
|
||||
};
|
||||
Some(Arc::new(any_message))
|
||||
}
|
||||
MessageType::Emote(content) => {
|
||||
let mut html_body: Option<String> = None;
|
||||
if let Some(formatted_body) = content.formatted {
|
||||
if formatted_body.format == MessageFormat::Html {
|
||||
html_body = Some(formatted_body.body);
|
||||
}
|
||||
}
|
||||
|
||||
let any_message = AnyMessage {
|
||||
text: None,
|
||||
image: None,
|
||||
notice: None,
|
||||
emote: Some(Arc::new(EmoteMessage { base_message, html_body })),
|
||||
};
|
||||
Some(Arc::new(any_message))
|
||||
}
|
||||
_ => {
|
||||
let any_message = AnyMessage {
|
||||
text: Some(Arc::new(TextMessage { base_message, html_body: None })),
|
||||
image: None,
|
||||
notice: None,
|
||||
emote: None,
|
||||
};
|
||||
Some(Arc::new(any_message))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn media_source_from_url(url: String) -> Arc<MediaSource> {
|
||||
Arc::new(MediaSource::Plain(url.into()))
|
||||
}
|
||||
|
||||
pub fn message_event_content_from_markdown(md: String) -> Arc<MessageEventContent> {
|
||||
Arc::new(MessageEventContent::text_markdown(md))
|
||||
}
|
||||
|
||||
#[extension_trait]
|
||||
pub impl MediaSourceExt for MediaSource {
|
||||
fn url(&self) -> String {
|
||||
match self {
|
||||
MediaSource::Plain(url) => url.to_string(),
|
||||
MediaSource::Encrypted(file) => file.url.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
159
crates/matrix-sdk-ffi/src/room.rs
Normal file
159
crates/matrix-sdk-ffi/src/room.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use futures_util::{pin_mut, StreamExt};
|
||||
use matrix_sdk::{
|
||||
room::Room as MatrixRoom,
|
||||
ruma::{events::room::message::RoomMessageEventContent, UserId},
|
||||
};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use super::{
|
||||
backward_stream::BackwardsStream,
|
||||
messages::{sync_event_to_message, AnyMessage},
|
||||
RUNTIME,
|
||||
};
|
||||
|
||||
pub trait RoomDelegate: Sync + Send {
|
||||
fn did_receive_message(&self, messages: Arc<AnyMessage>);
|
||||
}
|
||||
|
||||
pub struct Room {
|
||||
room: MatrixRoom,
|
||||
delegate: Arc<RwLock<Option<Box<dyn RoomDelegate>>>>,
|
||||
is_listening_to_live_events: Arc<RwLock<bool>>,
|
||||
}
|
||||
|
||||
impl Room {
|
||||
pub fn new(room: MatrixRoom) -> Self {
|
||||
Room {
|
||||
room,
|
||||
delegate: Arc::new(RwLock::new(None)),
|
||||
is_listening_to_live_events: Arc::new(RwLock::new(false)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_delegate(&self, delegate: Option<Box<dyn RoomDelegate>>) {
|
||||
*self.delegate.write() = delegate;
|
||||
}
|
||||
|
||||
pub fn id(&self) -> String {
|
||||
self.room.room_id().to_string()
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Option<String> {
|
||||
self.room.name()
|
||||
}
|
||||
|
||||
pub fn display_name(&self) -> Result<String> {
|
||||
let r = self.room.clone();
|
||||
RUNTIME.block_on(async move { Ok(r.display_name().await?.to_string()) })
|
||||
}
|
||||
|
||||
pub fn topic(&self) -> Option<String> {
|
||||
self.room.topic()
|
||||
}
|
||||
|
||||
pub fn avatar_url(&self) -> Option<String> {
|
||||
self.room.avatar_url().map(|m| m.to_string())
|
||||
}
|
||||
|
||||
pub fn member_avatar_url(&self, user_id: String) -> Result<Option<String>> {
|
||||
let room = self.room.clone();
|
||||
let user_id = user_id;
|
||||
RUNTIME.block_on(async move {
|
||||
let user_id = <&UserId>::try_from(&*user_id).expect("Invalid user id.");
|
||||
let member = room.get_member(user_id).await?.expect("No user found");
|
||||
let avatar_url_string = member.avatar_url().map(|m| m.to_string());
|
||||
Ok(avatar_url_string)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn member_display_name(&self, user_id: String) -> Result<Option<String>> {
|
||||
let room = self.room.clone();
|
||||
let user_id = user_id;
|
||||
RUNTIME.block_on(async move {
|
||||
let user_id = <&UserId>::try_from(&*user_id).expect("Invalid user id.");
|
||||
let member = room.get_member(user_id).await?.expect("No user found");
|
||||
let avatar_url_string = member.display_name().map(|m| m.to_owned());
|
||||
Ok(avatar_url_string)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_direct(&self) -> bool {
|
||||
self.room.is_direct()
|
||||
}
|
||||
|
||||
pub fn is_public(&self) -> bool {
|
||||
self.room.is_public()
|
||||
}
|
||||
|
||||
pub fn is_encrypted(&self) -> bool {
|
||||
self.room.is_encrypted()
|
||||
}
|
||||
|
||||
pub fn is_space(&self) -> bool {
|
||||
self.room.is_space()
|
||||
}
|
||||
|
||||
pub fn is_tombstoned(&self) -> bool {
|
||||
self.room.is_tombstoned()
|
||||
}
|
||||
|
||||
pub fn start_live_event_listener(&self) -> Option<Arc<BackwardsStream>> {
|
||||
if *self.is_listening_to_live_events.read() {
|
||||
return None;
|
||||
}
|
||||
|
||||
*self.is_listening_to_live_events.write() = true;
|
||||
|
||||
let room = self.room.clone();
|
||||
let delegate = self.delegate.clone();
|
||||
let is_listening_to_live_events = self.is_listening_to_live_events.clone();
|
||||
|
||||
let (forward_stream, backwards) = RUNTIME.block_on(async move {
|
||||
room.timeline().await.expect("Failed acquiring timeline streams")
|
||||
});
|
||||
|
||||
RUNTIME.spawn(async move {
|
||||
pin_mut!(forward_stream);
|
||||
|
||||
while let Some(sync_event) = forward_stream.next().await {
|
||||
if !(*is_listening_to_live_events.read()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(delegate) = &*delegate.read() {
|
||||
if let Some(message) = sync_event_to_message(sync_event) {
|
||||
delegate.did_receive_message(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Some(Arc::new(BackwardsStream::new(Box::pin(backwards))))
|
||||
}
|
||||
|
||||
pub fn stop_live_event_listener(&self) {
|
||||
*self.is_listening_to_live_events.write() = false;
|
||||
}
|
||||
|
||||
pub fn send(&self, msg: Arc<RoomMessageEventContent>, txn_id: Option<String>) -> Result<()> {
|
||||
let room = match &self.room {
|
||||
MatrixRoom::Joined(j) => j.clone(),
|
||||
_ => bail!("Can't send to a room that isn't in joined state"),
|
||||
};
|
||||
|
||||
RUNTIME.block_on(async move {
|
||||
room.send((*msg).to_owned(), txn_id.as_deref().map(Into::into)).await
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Room {
|
||||
type Target = MatrixRoom;
|
||||
fn deref(&self) -> &MatrixRoom {
|
||||
&self.room
|
||||
}
|
||||
}
|
||||
5
crates/matrix-sdk-ffi/src/uniffi_api.rs
Normal file
5
crates/matrix-sdk-ffi/src/uniffi_api.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use crate::*;
|
||||
|
||||
uniffi_macros::include_scaffolding!("api");
|
||||
@@ -33,7 +33,7 @@ use matrix_sdk_crypto::{
|
||||
};
|
||||
use matrix_sdk_store_encryption::StoreCipher;
|
||||
use ruma::{DeviceId, OwnedDeviceId, OwnedUserId, RoomId, TransactionId, UserId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::IdbKeyRange;
|
||||
|
||||
@@ -284,7 +284,7 @@ impl IndexeddbStore {
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_value<T: for<'b> Deserialize<'b>>(
|
||||
fn deserialize_value<T: DeserializeOwned>(
|
||||
&self,
|
||||
value: JsValue,
|
||||
) -> Result<T, CryptoStoreError> {
|
||||
|
||||
@@ -46,7 +46,7 @@ use ruma::{
|
||||
signatures::{redact_in_place, CanonicalJsonObject},
|
||||
RoomVersionId,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::IdbKeyRange;
|
||||
|
||||
@@ -275,7 +275,7 @@ impl IndexeddbStore {
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_event<T: for<'b> Deserialize<'b>>(
|
||||
fn deserialize_event<T: DeserializeOwned>(
|
||||
&self,
|
||||
event: JsValue,
|
||||
) -> std::result::Result<T, SerializationError> {
|
||||
|
||||
@@ -29,4 +29,5 @@ ruma-common = "0.9.0"
|
||||
thiserror = "1.0.30"
|
||||
|
||||
[dependencies.vodozemac]
|
||||
version = "0.2.0"
|
||||
git = "https://github.com/matrix-org/vodozemac/"
|
||||
rev = "d0e744287a14319c2a9148fef3747548c740fc36"
|
||||
|
||||
@@ -32,7 +32,7 @@ use crate::{
|
||||
};
|
||||
|
||||
/// An enum representing the different modes a QR verification can be in.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum QrVerificationData {
|
||||
/// The QR verification is verifying another user
|
||||
Verification(VerificationData),
|
||||
@@ -375,7 +375,7 @@ impl QrVerificationData {
|
||||
///
|
||||
/// This mode is used for verification between two users using their master
|
||||
/// cross signing keys.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct VerificationData {
|
||||
event_id: OwnedEventId,
|
||||
first_master_key: Ed25519PublicKey,
|
||||
@@ -474,7 +474,7 @@ impl From<VerificationData> for QrVerificationData {
|
||||
/// This mode is used for verification between two devices of the same user
|
||||
/// where this device, that is creating this QR code, is trusting or owning
|
||||
/// the cross signing master key.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SelfVerificationData {
|
||||
transaction_id: String,
|
||||
master_key: Ed25519PublicKey,
|
||||
@@ -577,7 +577,7 @@ impl From<SelfVerificationData> for QrVerificationData {
|
||||
/// This mode is used for verification between two devices of the same user
|
||||
/// where this device, that is creating this QR code, is not trusting the
|
||||
/// cross signing master key.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SelfVerificationNoMasterKey {
|
||||
transaction_id: String,
|
||||
device_key: Ed25519PublicKey,
|
||||
|
||||
@@ -38,7 +38,7 @@ use ruma::{
|
||||
events::room_key_request::RequestedKeyInfo, DeviceId, OwnedDeviceId, OwnedUserId, RoomId,
|
||||
TransactionId, UserId,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
pub use sled::Error;
|
||||
use sled::{
|
||||
transaction::{ConflictableTransactionError, TransactionError},
|
||||
@@ -205,7 +205,7 @@ impl std::fmt::Debug for SledStore {
|
||||
}
|
||||
|
||||
impl SledStore {
|
||||
/// Open the sled based cryptostore at the given path using the given
|
||||
/// Open the sled-based crypto store at the given path using the given
|
||||
/// passphrase to encrypt private data.
|
||||
pub fn open_with_passphrase(
|
||||
path: impl AsRef<Path>,
|
||||
@@ -224,7 +224,7 @@ impl SledStore {
|
||||
SledStore::open_helper(db, Some(path), store_cipher)
|
||||
}
|
||||
|
||||
/// Create a sled based cryptostore using the given sled database.
|
||||
/// Create a sled-based crypto store using the given sled database.
|
||||
/// The given passphrase will be used to encrypt private data.
|
||||
pub fn open_with_database(
|
||||
db: Db,
|
||||
@@ -245,10 +245,7 @@ impl SledStore {
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_value<T: for<'b> Deserialize<'b>>(
|
||||
&self,
|
||||
event: &[u8],
|
||||
) -> Result<T, CryptoStoreError> {
|
||||
fn deserialize_value<T: DeserializeOwned>(&self, event: &[u8]) -> Result<T, CryptoStoreError> {
|
||||
if let Some(key) = &self.store_cipher {
|
||||
key.decrypt_value(event).map_err(CryptoStoreError::backend)
|
||||
} else {
|
||||
|
||||
@@ -51,7 +51,9 @@ use ruma::{
|
||||
signatures::{redact_in_place, CanonicalJsonObject},
|
||||
RoomVersionId,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
use serde::Deserialize;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use sled::{
|
||||
transaction::{ConflictableTransactionError, TransactionError},
|
||||
Config, Db, Transactional, Tree,
|
||||
@@ -368,10 +370,7 @@ impl SledStore {
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_event<T: for<'b> Deserialize<'b>>(
|
||||
&self,
|
||||
event: &[u8],
|
||||
) -> Result<T, SledStoreError> {
|
||||
fn deserialize_event<T: DeserializeOwned>(&self, event: &[u8]) -> Result<T, SledStoreError> {
|
||||
if let Some(key) = &self.store_cipher {
|
||||
Ok(key.decrypt_value(event)?)
|
||||
} else {
|
||||
|
||||
@@ -25,7 +25,7 @@ use displaydoc::Display;
|
||||
use hmac::Hmac;
|
||||
use pbkdf2::pbkdf2;
|
||||
use rand::{thread_rng, Error as RandomError, Fill};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
@@ -76,7 +76,7 @@ pub enum Error {
|
||||
/// let decrypted: Value = store_cipher.decrypt_value(&encrypted)?;
|
||||
///
|
||||
/// assert_eq!(value, decrypted);
|
||||
/// # Result::<_, anyhow::Error>::Ok(()) };
|
||||
/// # anyhow::Ok(()) };
|
||||
/// ```
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct StoreCipher {
|
||||
@@ -115,7 +115,7 @@ impl StoreCipher {
|
||||
/// let export = store_cipher.export("secret-passphrase");
|
||||
///
|
||||
/// // Save the export in your key/value store.
|
||||
/// # Result::<_, anyhow::Error>::Ok(()) };
|
||||
/// # anyhow::Ok(()) };
|
||||
/// ```
|
||||
pub fn export(&self, passphrase: &str) -> Result<Vec<u8>, Error> {
|
||||
let mut rng = thread_rng();
|
||||
@@ -171,7 +171,7 @@ impl StoreCipher {
|
||||
/// let imported = StoreCipher::import("secret-passphrase", &export)?;
|
||||
///
|
||||
/// // Save the export in your key/value store.
|
||||
/// # Result::<_, anyhow::Error>::Ok(()) };
|
||||
/// # anyhow::Ok(()) };
|
||||
/// ```
|
||||
pub fn import(passphrase: &str, encrypted: &[u8]) -> Result<Self, Error> {
|
||||
let encrypted: EncryptedStoreCipher = serde_json::from_slice(encrypted)?;
|
||||
@@ -245,7 +245,7 @@ impl StoreCipher {
|
||||
/// let hashed_key = store_cipher.hash_key("list-of-pokemon", key.as_ref());
|
||||
///
|
||||
/// // It's now safe to insert the key into our key/value store.
|
||||
/// # Result::<_, anyhow::Error>::Ok(()) };
|
||||
/// # anyhow::Ok(()) };
|
||||
/// ```
|
||||
pub fn hash_key(&self, table_name: &str, key: &[u8]) -> [u8; 32] {
|
||||
let mac_key = self.inner.get_mac_key_for_table(table_name);
|
||||
@@ -281,7 +281,7 @@ impl StoreCipher {
|
||||
/// let decrypted: Value = store_cipher.decrypt_value(&encrypted)?;
|
||||
///
|
||||
/// assert_eq!(value, decrypted);
|
||||
/// # Result::<_, anyhow::Error>::Ok(()) };
|
||||
/// # anyhow::Ok(()) };
|
||||
/// ```
|
||||
pub fn encrypt_value(&self, value: &impl Serialize) -> Result<Vec<u8>, Error> {
|
||||
Ok(serde_json::to_vec(&self.encrypt_value_typed(value)?)?)
|
||||
@@ -351,7 +351,7 @@ impl StoreCipher {
|
||||
/// let decrypted = store_cipher.decrypt_value_data(encrypted)?;
|
||||
///
|
||||
/// assert_eq!(value, decrypted);
|
||||
/// # Result::<_, anyhow::Error>::Ok(()) };
|
||||
/// # anyhow::Ok(()) };
|
||||
/// ```
|
||||
pub fn encrypt_value_data(&self, mut data: Vec<u8>) -> Result<EncryptedValue, Error> {
|
||||
let nonce = Keys::get_nonce()?;
|
||||
@@ -391,9 +391,9 @@ impl StoreCipher {
|
||||
/// let decrypted: Value = store_cipher.decrypt_value(&encrypted)?;
|
||||
///
|
||||
/// assert_eq!(value, decrypted);
|
||||
/// # Result::<_, anyhow::Error>::Ok(()) };
|
||||
/// # anyhow::Ok(()) };
|
||||
/// ```
|
||||
pub fn decrypt_value<T: for<'b> Deserialize<'b>>(&self, value: &[u8]) -> Result<T, Error> {
|
||||
pub fn decrypt_value<T: DeserializeOwned>(&self, value: &[u8]) -> Result<T, Error> {
|
||||
let value: EncryptedValue = serde_json::from_slice(value)?;
|
||||
self.decrypt_value_typed(value)
|
||||
}
|
||||
@@ -427,9 +427,9 @@ impl StoreCipher {
|
||||
/// let decrypted: Value = store_cipher.decrypt_value_typed(encrypted)?;
|
||||
///
|
||||
/// assert_eq!(value, decrypted);
|
||||
/// # Result::<_, anyhow::Error>::Ok(()) };
|
||||
/// # anyhow::Ok(()) };
|
||||
/// ```
|
||||
pub fn decrypt_value_typed<T: for<'b> Deserialize<'b>>(
|
||||
pub fn decrypt_value_typed<T: DeserializeOwned>(
|
||||
&self,
|
||||
value: EncryptedValue,
|
||||
) -> Result<T, Error> {
|
||||
@@ -467,7 +467,7 @@ impl StoreCipher {
|
||||
/// let decrypted = store_cipher.decrypt_value_data(encrypted)?;
|
||||
///
|
||||
/// assert_eq!(value, decrypted);
|
||||
/// # Result::<_, anyhow::Error>::Ok(()) };
|
||||
/// # anyhow::Ok(()) };
|
||||
/// ```
|
||||
pub fn decrypt_value_data(&self, value: EncryptedValue) -> Result<Vec<u8>, Error> {
|
||||
if value.version != VERSION {
|
||||
@@ -500,7 +500,7 @@ impl MacKey {
|
||||
|
||||
/// Encrypted value, ready for storage, as created by the
|
||||
/// [`StoreCipher::encrypt_value_data()`]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct EncryptedValue {
|
||||
version: u8,
|
||||
ciphertext: Vec<u8>,
|
||||
@@ -557,7 +557,7 @@ impl Keys {
|
||||
}
|
||||
|
||||
/// Version specific info for the key derivation method that is used.
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
enum KdfInfo {
|
||||
/// The PBKDF2 to Chacha key derivation variant.
|
||||
Pbkdf2ToChaCha20Poly1305 {
|
||||
@@ -572,7 +572,7 @@ enum KdfInfo {
|
||||
|
||||
/// Version specific info for encryption method that is used to encrypt our
|
||||
/// store cipher.
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
enum CipherTextInfo {
|
||||
/// A store cipher encrypted using the ChaCha20Poly1305 AEAD.
|
||||
ChaCha20Poly1305 {
|
||||
@@ -585,7 +585,7 @@ enum CipherTextInfo {
|
||||
|
||||
/// An encrypted version of our store cipher, this can be safely stored in a
|
||||
/// database.
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
struct EncryptedStoreCipher {
|
||||
/// Info about the key derivation method that was used to expand the
|
||||
/// passphrase into an encryption key.
|
||||
|
||||
@@ -46,6 +46,17 @@ pub static DEVICES: Lazy<JsonValue> = Lazy::new(|| {
|
||||
})
|
||||
});
|
||||
|
||||
pub static GET_ALIAS: Lazy<JsonValue> = Lazy::new(|| {
|
||||
json!({
|
||||
"room_id": "!lUbmUPdxdXxEQurqOs:example.net",
|
||||
"servers": [
|
||||
"example.org",
|
||||
"example.net",
|
||||
"matrix.org",
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
pub static WELL_KNOWN: Lazy<JsonValue> = Lazy::new(|| {
|
||||
json!({
|
||||
"m.homeserver": {
|
||||
|
||||
@@ -210,30 +210,6 @@ pub static SYNC: Lazy<JsonValue> = Lazy::new(|| {
|
||||
"replaces_state": "$152034819067QWJxM:localhost"
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"membership": "leave",
|
||||
"reason": "offline",
|
||||
"avatar_url": "mxc://avatar.com/d0dV9jLpe",
|
||||
"displayname": "example"
|
||||
},
|
||||
"event_id": "$1585345508297748AIUBh:matrix.org",
|
||||
"origin_server_ts": 158534550,
|
||||
"sender": "@example:localhost",
|
||||
"state_key": "@example:localhost",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"replaces_state": "$1585345354296486IGZfp:localhost",
|
||||
"prev_content": {
|
||||
"avatar_url": "mxc://avatar.com/d0dV9jLpe",
|
||||
"displayname": "example",
|
||||
"membership": "join"
|
||||
},
|
||||
"prev_sender": "@example2:localhost",
|
||||
"age": 6992
|
||||
},
|
||||
"room_id": "!roomid:room.com"
|
||||
}
|
||||
]
|
||||
},
|
||||
"timeline": {
|
||||
|
||||
@@ -123,7 +123,7 @@ default-features = false
|
||||
optional = true
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
async-once-cell = "0.3.0"
|
||||
async-once-cell = "0.3.1"
|
||||
wasm-timer = "0.2.5"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
|
||||
@@ -59,19 +59,19 @@ More examples can be found in the [examples] directory.
|
||||
|
||||
The following crate feature flags are available:
|
||||
|
||||
| Feature | Default | Description |
|
||||
| ------------------- | :-----: | --------------------------------------------------------------------- |
|
||||
| `anyhow` | No | Better logging for event handlers that return `anyhow::Result` |
|
||||
| `e2e-encryption` | Yes | Enable End-to-end encryption support |
|
||||
| `eyre` | No | Better logging for event handlers that return `eyre::Result` |
|
||||
| `image-proc` | No | Enables image processing to generate thumbnails |
|
||||
| `image-rayon` | No | Enables faster image processing |
|
||||
| `markdown` | No | Support to send Markdown-formatted messages |
|
||||
| `qrcode` | Yes | QR code verification support |
|
||||
| `sled` | Yes | Persistent storage of state and E2EE-Data using sled (if `e2e-encryption` is activated)
|
||||
| `indexeddb` | No | Persistent storage of state and E2EE-Data for browsers using indexeddb (if `e2e-encryption` is activated)
|
||||
| `socks` | No | Enables SOCKS support in the default HTTP client, [`reqwest`] |
|
||||
| `sso-login` | No | Enables SSO login with a local HTTP server |
|
||||
| Feature | Default | Description |
|
||||
| ------------------- | :-----: | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `anyhow` | No | Better logging for event handlers that return `anyhow::Result` |
|
||||
| `e2e-encryption` | Yes | End-to-end encryption (E2EE) support |
|
||||
| `eyre` | No | Better logging for event handlers that return `eyre::Result` |
|
||||
| `image-proc` | No | Image processing for generating thumbnails |
|
||||
| `image-rayon` | No | Enables faster image processing |
|
||||
| `markdown` | No | Support for sending Markdown-formatted messages |
|
||||
| `qrcode` | Yes | QR code verification support |
|
||||
| `sled` | Yes | Persistent storage of state and E2EE data (optionally, if feature `e2e-encryption` is enabled), via Sled |
|
||||
| `indexeddb` | No | Persistent storage of state and E2EE data (optionally, if feature `e2e-encryption` is enabled) for browsers, via IndexedDB |
|
||||
| `socks` | No | SOCKS support in the default HTTP client, [`reqwest`] |
|
||||
| `sso-login` | No | Support for SSO login with a local HTTP server |
|
||||
|
||||
[`reqwest`]: https://docs.rs/reqwest/0.11.5/reqwest/index.html
|
||||
|
||||
|
||||
@@ -37,11 +37,7 @@ async fn bootstrap(client: Client, user_id: OwnedUserId, password: String) {
|
||||
}
|
||||
}
|
||||
|
||||
async fn login(
|
||||
homeserver_url: String,
|
||||
username: &str,
|
||||
password: &str,
|
||||
) -> Result<(), matrix_sdk::Error> {
|
||||
async fn login(homeserver_url: String, username: &str, password: &str) -> matrix_sdk::Result<()> {
|
||||
let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL");
|
||||
let client = Client::new(homeserver_url).await.unwrap();
|
||||
|
||||
@@ -72,7 +68,7 @@ async fn login(
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), matrix_sdk::Error> {
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let (homeserver_url, username, password) =
|
||||
@@ -87,5 +83,7 @@ async fn main() -> Result<(), matrix_sdk::Error> {
|
||||
}
|
||||
};
|
||||
|
||||
login(homeserver_url, &username, &password).await
|
||||
login(homeserver_url, &username, &password).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -65,11 +65,7 @@ async fn print_devices(user_id: &UserId, client: &Client) {
|
||||
}
|
||||
}
|
||||
|
||||
async fn login(
|
||||
homeserver_url: String,
|
||||
username: &str,
|
||||
password: &str,
|
||||
) -> Result<(), matrix_sdk::Error> {
|
||||
async fn login(homeserver_url: String, username: &str, password: &str) -> matrix_sdk::Result<()> {
|
||||
let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL");
|
||||
let client = Client::new(homeserver_url).await.unwrap();
|
||||
|
||||
@@ -201,7 +197,7 @@ async fn login(
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), matrix_sdk::Error> {
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let (homeserver_url, username, password) =
|
||||
@@ -216,5 +212,7 @@ async fn main() -> Result<(), matrix_sdk::Error> {
|
||||
}
|
||||
};
|
||||
|
||||
login(homeserver_url, &username, &password).await
|
||||
login(homeserver_url, &username, &password).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ async fn login(
|
||||
homeserver_url: String,
|
||||
username: &str,
|
||||
password: &str,
|
||||
) -> Result<Client, matrix_sdk::Error> {
|
||||
) -> matrix_sdk::Result<Client> {
|
||||
let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL");
|
||||
let client = Client::new(homeserver_url).await.unwrap();
|
||||
|
||||
@@ -45,7 +45,7 @@ async fn login(
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), matrix_sdk::Error> {
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let (homeserver_url, username, password) =
|
||||
|
||||
@@ -56,7 +56,7 @@ async fn login_and_sync(
|
||||
username: String,
|
||||
password: String,
|
||||
image: File,
|
||||
) -> Result<(), matrix_sdk::Error> {
|
||||
) -> matrix_sdk::Result<()> {
|
||||
let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL");
|
||||
let client = Client::new(homeserver_url).await.unwrap();
|
||||
|
||||
@@ -74,7 +74,7 @@ async fn login_and_sync(
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), matrix_sdk::Error> {
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
let (homeserver_url, username, password, image_path) =
|
||||
match (env::args().nth(1), env::args().nth(2), env::args().nth(3), env::args().nth(4)) {
|
||||
|
||||
@@ -30,11 +30,7 @@ async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) {
|
||||
}
|
||||
}
|
||||
|
||||
async fn login(
|
||||
homeserver_url: String,
|
||||
username: &str,
|
||||
password: &str,
|
||||
) -> Result<(), matrix_sdk::Error> {
|
||||
async fn login(homeserver_url: String, username: &str, password: &str) -> matrix_sdk::Result<()> {
|
||||
let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL");
|
||||
let client = Client::new(homeserver_url).await.unwrap();
|
||||
|
||||
@@ -47,7 +43,7 @@ async fn login(
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), matrix_sdk::Error> {
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let (homeserver_url, username, password) =
|
||||
@@ -62,5 +58,7 @@ async fn main() -> Result<(), matrix_sdk::Error> {
|
||||
}
|
||||
};
|
||||
|
||||
login(homeserver_url, &username, &password).await
|
||||
login(homeserver_url, &username, &password).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ async fn print_timeline(room: Room) {
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), matrix_sdk::Error> {
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let (homeserver_url, username, password, room_id) =
|
||||
|
||||
@@ -67,7 +67,7 @@ impl Account {
|
||||
/// if let Some(name) = client.account().get_display_name().await? {
|
||||
/// println!("Logged in as user '{}' with display name '{}'", user, name);
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn get_display_name(&self) -> Result<Option<String>> {
|
||||
let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
|
||||
@@ -90,7 +90,7 @@ impl Account {
|
||||
/// client.login(user, "password", None, None).await?;
|
||||
///
|
||||
/// client.account().set_display_name(Some("Alice")).await?;
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn set_display_name(&self, name: Option<&str>) -> Result<()> {
|
||||
let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
|
||||
@@ -115,7 +115,7 @@ impl Account {
|
||||
/// if let Some(url) = client.account().get_avatar_url().await? {
|
||||
/// println!("Your avatar's mxc url is {}", url);
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn get_avatar_url(&self) -> Result<Option<OwnedMxcUri>> {
|
||||
let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
|
||||
@@ -164,7 +164,7 @@ impl Account {
|
||||
/// if let Some(avatar) = client.account().get_avatar(MediaFormat::File).await? {
|
||||
/// std::fs::write("avatar.png", avatar);
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn get_avatar(&self, format: MediaFormat) -> Result<Option<Vec<u8>>> {
|
||||
if let Some(url) = self.get_avatar_url().await? {
|
||||
@@ -199,7 +199,7 @@ impl Account {
|
||||
/// let mut image = File::open(&path)?;
|
||||
///
|
||||
/// client.account().upload_avatar(&mime::IMAGE_JPEG, &mut image).await?;
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn upload_avatar<R: Read>(
|
||||
&self,
|
||||
@@ -230,7 +230,7 @@ impl Account {
|
||||
/// profile.avatar_url
|
||||
/// );
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn get_profile(&self) -> Result<get_profile::v3::Response> {
|
||||
let user_id = self.client.user_id().ok_or(Error::AuthenticationRequired)?;
|
||||
@@ -276,7 +276,7 @@ impl Account {
|
||||
/// "myverysecretpassword",
|
||||
/// Some(AuthData::Dummy(Dummy::new())),
|
||||
/// ).await?;
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
/// [uiaa]: https://spec.matrix.org/v1.2/client-server-api/#user-interactive-authentication-api
|
||||
/// [`UiaaResponse`]: ruma::api::client::uiaa::UiaaResponse
|
||||
@@ -326,7 +326,7 @@ impl Account {
|
||||
///
|
||||
/// // Proceed with UIAA.
|
||||
///
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
/// [3pid]: https://spec.matrix.org/v1.2/appendices/#3pid-types
|
||||
/// [uiaa]: https://spec.matrix.org/v1.2/client-server-api/#user-interactive-authentication-api
|
||||
@@ -362,7 +362,7 @@ impl Account {
|
||||
/// for threepid in threepids {
|
||||
/// println!("Found 3PID '{}' of type '{}'", threepid.address, threepid.medium);
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
/// [3pid]: https://spec.matrix.org/v1.2/appendices/#3pid-types
|
||||
pub async fn get_3pids(&self) -> Result<get_3pids::v3::Response> {
|
||||
@@ -429,7 +429,7 @@ impl Account {
|
||||
///
|
||||
/// // Proceed with UIAA.
|
||||
///
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
/// [3pid]: https://spec.matrix.org/v1.2/appendices/#3pid-types
|
||||
/// [`ErrorKind::ThreepidInUse`]: ruma::api::client::error::ErrorKind::ThreepidInUse
|
||||
@@ -511,7 +511,7 @@ impl Account {
|
||||
///
|
||||
/// // Proceed with UIAA.
|
||||
///
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
/// [3pid]: https://spec.matrix.org/v1.2/appendices/#3pid-types
|
||||
/// [`ErrorKind::ThreepidInUse`]: ruma::api::client::error::ErrorKind::ThreepidInUse
|
||||
@@ -617,7 +617,7 @@ impl Account {
|
||||
/// _ => println!("Could not unbind 3PID from the Identity Server"),
|
||||
/// }
|
||||
///
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
/// [3pid]: https://spec.matrix.org/v1.2/appendices/#3pid-types
|
||||
/// [`ThirdPartyIdRemovalStatus::Success`]: ruma::api::client::account::ThirdPartyIdRemovalStatus::Success
|
||||
|
||||
@@ -336,7 +336,7 @@ impl<'a, R: Read> AttachmentConfig<'a, R> {
|
||||
/// config,
|
||||
/// ).await?;
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
#[cfg(feature = "image-proc")]
|
||||
pub fn generate_image_thumbnail<R: BufRead + Seek>(
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use async_once_cell::OnceCell;
|
||||
use matrix_sdk_base::{locks::RwLock, store::StoreConfig, BaseClient, StateStore};
|
||||
use ruma::{
|
||||
api::{client::discovery::discover_homeserver, error::FromHttpResponseError, MatrixVersion},
|
||||
OwnedServerName, ServerName, UserId,
|
||||
};
|
||||
use thiserror::Error;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::sync::OnceCell;
|
||||
use url::Url;
|
||||
|
||||
use super::{Client, ClientInner};
|
||||
@@ -109,7 +113,7 @@ impl ClientBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Create a new `ClientConfig` with the given [`StoreConfig`].
|
||||
/// Create a new `ClientBuilder` with the given [`StoreConfig`].
|
||||
///
|
||||
/// The easiest way to get a [`StoreConfig`] is to use the
|
||||
/// [`make_store_config`] method from the [`store`] module or directly from
|
||||
@@ -186,7 +190,7 @@ impl ClientBuilder {
|
||||
/// let client_config = Client::builder()
|
||||
/// .proxy("http://localhost:8080");
|
||||
///
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(())
|
||||
/// # anyhow::Ok(())
|
||||
/// # });
|
||||
/// ```
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -318,23 +322,11 @@ impl ClientBuilder {
|
||||
let homeserver = Arc::new(RwLock::new(Url::parse(&homeserver)?));
|
||||
let http_client = mk_http_client(homeserver.clone());
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let server_versions = {
|
||||
let cell = async_once_cell::OnceCell::new();
|
||||
if let Some(server_versions) = self.server_versions {
|
||||
cell.get_or_init(async move { server_versions }).await;
|
||||
}
|
||||
cell
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let server_versions = tokio::sync::OnceCell::new_with(self.server_versions);
|
||||
|
||||
let inner = Arc::new(ClientInner {
|
||||
homeserver,
|
||||
http_client,
|
||||
base_client,
|
||||
server_versions,
|
||||
server_versions: OnceCell::new_with(self.server_versions),
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
group_session_locks: Default::default(),
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
|
||||
@@ -43,6 +43,7 @@ use ruma::{
|
||||
api::{
|
||||
client::{
|
||||
account::{register, whoami},
|
||||
alias::get_alias,
|
||||
device::{delete_devices, get_devices},
|
||||
directory::{get_public_rooms, get_public_rooms_filtered},
|
||||
discovery::{
|
||||
@@ -64,8 +65,8 @@ use ruma::{
|
||||
assign,
|
||||
events::room::MediaSource,
|
||||
presence::PresenceState,
|
||||
DeviceId, MxcUri, OwnedDeviceId, OwnedRoomId, OwnedServerName, RoomId, RoomOrAliasId,
|
||||
ServerName, UInt, UserId,
|
||||
DeviceId, MxcUri, OwnedDeviceId, OwnedRoomId, OwnedServerName, RoomAliasId, RoomId,
|
||||
RoomOrAliasId, ServerName, UInt, UserId,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -238,7 +239,7 @@ impl Client {
|
||||
/// // Change password
|
||||
/// }
|
||||
///
|
||||
/// # Result::<_, anyhow::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn get_capabilities(&self) -> HttpResult<Capabilities> {
|
||||
let res = self.send(get_capabilities::v3::Request::new(), None).await?;
|
||||
@@ -606,6 +607,20 @@ impl Client {
|
||||
self.store().get_room(room_id).and_then(|room| room::Left::new(self.clone(), room))
|
||||
}
|
||||
|
||||
/// Resolve a room alias to a room id and a list of servers which know
|
||||
/// about it.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// `room_alias` - The room alias to be resolved.
|
||||
pub async fn resolve_room_alias(
|
||||
&self,
|
||||
room_alias: &RoomAliasId,
|
||||
) -> HttpResult<get_alias::v3::Response> {
|
||||
let request = get_alias::v3::Request::new(room_alias);
|
||||
self.send(request, None).await
|
||||
}
|
||||
|
||||
/// Gets the homeserver’s supported login types.
|
||||
///
|
||||
/// This should be the first step when trying to login so you can call the
|
||||
@@ -701,7 +716,7 @@ impl Client {
|
||||
/// "Logged in as {}, got device_id {} and access_token {}",
|
||||
/// user, response.device_id, response.access_token
|
||||
/// );
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
///
|
||||
/// [`restore_login`]: #method.restore_login
|
||||
@@ -1371,7 +1386,7 @@ impl Client {
|
||||
/// for room in response.chunk {
|
||||
/// println!("Found room {:?}", room);
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn public_rooms_filtered(
|
||||
&self,
|
||||
@@ -1475,7 +1490,7 @@ impl Client {
|
||||
///
|
||||
/// // Check the corresponding Response struct to find out what types are
|
||||
/// // returned
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn send<Request>(
|
||||
&self,
|
||||
@@ -1543,7 +1558,7 @@ impl Client {
|
||||
/// device.display_name.as_deref().unwrap_or("")
|
||||
/// );
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn devices(&self) -> HttpResult<get_devices::v3::Response> {
|
||||
let request = get_devices::v3::Request::new();
|
||||
@@ -1600,7 +1615,7 @@ impl Client {
|
||||
/// .await?;
|
||||
/// }
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
pub async fn delete_devices(
|
||||
&self,
|
||||
devices: &[OwnedDeviceId],
|
||||
@@ -1689,7 +1704,7 @@ impl Client {
|
||||
/// // Now keep on syncing forever. `sync()` will use the stored sync token
|
||||
/// // from our `sync_once()` call automatically.
|
||||
/// client.sync(SyncSettings::default()).await;
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
///
|
||||
/// [`sync`]: #method.sync
|
||||
@@ -1791,7 +1806,7 @@ impl Client {
|
||||
/// // Now keep on syncing forever. `sync()` will use the latest sync token
|
||||
/// // automatically.
|
||||
/// client.sync(SyncSettings::default()).await;
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
///
|
||||
/// [argument docs]: #method.sync_once
|
||||
@@ -1920,7 +1935,7 @@ impl Client {
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
#[instrument(skip(self))]
|
||||
pub async fn sync_stream<'a>(
|
||||
@@ -2532,6 +2547,19 @@ pub(crate) mod tests {
|
||||
assert!(client.devices().await.is_ok());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn resolve_room_alias() {
|
||||
let client = no_retry_test_client().await;
|
||||
|
||||
let _m = mock("GET", "/_matrix/client/r0/directory/room/%23alias%3Aexample%2Eorg")
|
||||
.with_status(200)
|
||||
.with_body(test_json::GET_ALIAS.to_string())
|
||||
.create();
|
||||
|
||||
let alias = ruma::room_alias_id!("#alias:example.org");
|
||||
assert!(client.resolve_room_alias(alias).await.is_ok());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_join_leave_room() {
|
||||
let room_id = room_id!("!SVkFJHzfwvuaIEawgC:localhost");
|
||||
@@ -3361,7 +3389,7 @@ pub(crate) mod tests {
|
||||
let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap();
|
||||
let members: Vec<RoomMember> = room.active_members().await.unwrap();
|
||||
|
||||
assert_eq!(1, members.len());
|
||||
assert_eq!(2, members.len());
|
||||
// assert!(room.power_levels.is_some())
|
||||
}
|
||||
|
||||
|
||||
@@ -198,9 +198,9 @@ pub enum RoomKeyImportError {
|
||||
#[error(transparent)]
|
||||
SerdeJson(#[from] JsonError),
|
||||
|
||||
/// The cryptostore isn't yet open, logging in is required to open the
|
||||
/// cryptostore.
|
||||
#[error("The cryptostore hasn't been yet opened, can't import yet.")]
|
||||
/// The crypto store isn't yet open. Logging in is required to open the
|
||||
/// crypto store.
|
||||
#[error("The crypto store hasn't been yet opened, can't import yet.")]
|
||||
StoreClosed,
|
||||
|
||||
/// An IO error happened.
|
||||
|
||||
@@ -205,15 +205,20 @@ impl Common {
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
if let Some(machine) = self.client.olm_machine() {
|
||||
for event in http_response.chunk {
|
||||
let decrypted_event =
|
||||
if let AnySyncRoomEvent::MessageLike(AnySyncMessageLikeEvent::RoomEncrypted(
|
||||
SyncMessageLikeEvent::Original(encrypted_event),
|
||||
)) = event.deserialize_as::<AnySyncRoomEvent>()?
|
||||
{
|
||||
machine.decrypt_room_event(&encrypted_event, room_id).await?
|
||||
let decrypted_event = if let Ok(AnySyncRoomEvent::MessageLike(
|
||||
AnySyncMessageLikeEvent::RoomEncrypted(SyncMessageLikeEvent::Original(
|
||||
encrypted_event,
|
||||
)),
|
||||
)) = event.deserialize_as::<AnySyncRoomEvent>()
|
||||
{
|
||||
if let Ok(event) = machine.decrypt_room_event(&encrypted_event, room_id).await {
|
||||
event
|
||||
} else {
|
||||
RoomEvent { event, encryption_info: None }
|
||||
};
|
||||
}
|
||||
} else {
|
||||
RoomEvent { event, encryption_info: None }
|
||||
};
|
||||
|
||||
response.chunk.push(decrypted_event);
|
||||
}
|
||||
@@ -292,7 +297,7 @@ impl Common {
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(())
|
||||
/// # anyhow::Ok(())
|
||||
/// # });
|
||||
/// ```
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
@@ -361,7 +366,7 @@ impl Common {
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(())
|
||||
/// # anyhow::Ok(())
|
||||
/// # });
|
||||
/// ```
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
@@ -425,7 +430,7 @@ impl Common {
|
||||
/// });
|
||||
/// }
|
||||
///
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(())
|
||||
/// # anyhow::Ok(())
|
||||
/// # });
|
||||
/// ```
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
@@ -478,12 +483,15 @@ impl Common {
|
||||
let event = self.client.send(request, None).await?.event;
|
||||
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
if let AnySyncRoomEvent::MessageLike(AnySyncMessageLikeEvent::RoomEncrypted(
|
||||
SyncMessageLikeEvent::Original(encrypted_event),
|
||||
)) = event.deserialize_as::<AnySyncRoomEvent>()?
|
||||
{
|
||||
Ok(self.decrypt_event(&encrypted_event).await?)
|
||||
} else {
|
||||
if let Ok(AnySyncRoomEvent::MessageLike(AnySyncMessageLikeEvent::RoomEncrypted(
|
||||
SyncMessageLikeEvent::Original(encrypted_event),
|
||||
))) = event.deserialize_as::<AnySyncRoomEvent>()
|
||||
{
|
||||
if let Ok(event) = self.decrypt_event(&encrypted_event).await {
|
||||
return Ok(event);
|
||||
}
|
||||
}
|
||||
Ok(RoomEvent { event, encryption_info: None })
|
||||
}
|
||||
|
||||
@@ -837,7 +845,7 @@ impl Common {
|
||||
///
|
||||
/// room.set_tag(TagName::User(user_tag), tag_info ).await?;
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn set_tag(
|
||||
&self,
|
||||
|
||||
@@ -177,7 +177,7 @@ impl Joined {
|
||||
/// if let Some(room) = client.get_joined_room(&room_id) {
|
||||
/// room.typing_notice(true).await?
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn typing_notice(&self, typing: bool) -> Result<()> {
|
||||
// Only send a request to the homeserver if the old timeout has elapsed
|
||||
@@ -288,7 +288,7 @@ impl Joined {
|
||||
/// if let Some(room) = client.get_joined_room(&room_id) {
|
||||
/// room.enable_encryption().await?
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn enable_encryption(&self) -> Result<()> {
|
||||
use ruma::{
|
||||
@@ -458,7 +458,7 @@ impl Joined {
|
||||
/// if let Some(room) = client.get_joined_room(&room_id) {
|
||||
/// room.send(content, Some(&txn_id)).await?;
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
///
|
||||
/// [`SyncMessageLikeEvent`]: ruma::events::SyncMessageLikeEvent
|
||||
@@ -526,7 +526,7 @@ impl Joined {
|
||||
/// if let Some(room) = client.get_joined_room(&room_id) {
|
||||
/// room.send_raw(content, "m.room.message", None).await?;
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
///
|
||||
/// [`SyncMessageLikeEvent`]: ruma::events::SyncMessageLikeEvent
|
||||
@@ -575,7 +575,7 @@ impl Joined {
|
||||
let olm = self.client.olm_machine().expect("Olm machine wasn't started");
|
||||
|
||||
let encrypted_content =
|
||||
olm.encrypt_raw(self.inner.room_id(), content, event_type).await?;
|
||||
olm.encrypt_room_event_raw(self.inner.room_id(), content, event_type).await?;
|
||||
let raw_content = Raw::new(&encrypted_content)
|
||||
.expect("Failed to serialize encrypted event")
|
||||
.cast();
|
||||
@@ -648,7 +648,7 @@ impl Joined {
|
||||
/// AttachmentConfig::new(),
|
||||
/// ).await?;
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn send_attachment<R: Read + Seek, T: Read>(
|
||||
&self,
|
||||
@@ -813,7 +813,7 @@ impl Joined {
|
||||
/// if let Some(room) = client.get_joined_room(&room_id) {
|
||||
/// room.send_state_event(content, "").await?;
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn send_state_event(
|
||||
&self,
|
||||
@@ -907,7 +907,7 @@ impl Joined {
|
||||
/// let reason = Some("Indecent material");
|
||||
/// room.redact(&event_id, reason, None).await?;
|
||||
/// }
|
||||
/// # Result::<_, matrix_sdk::Error>::Ok(()) });
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
pub async fn redact(
|
||||
&self,
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
//! The re-exports present here depend on the store-related features that are
|
||||
//! enabled:
|
||||
//!
|
||||
//! 1. `sled` provides a `StateStore`, and a `CryptoStore` for encryption data
|
||||
//! if `e2e-encryption` is enabled. This is the default persistent store
|
||||
//! implementation for non-WebAssembly targets.
|
||||
//! 2. `indexeddb`, too, provides a `StateStore` and a `CryptoStore` if
|
||||
//! `encryption` is also enabled. This is the default persistent store
|
||||
//! implementation for WebAssembly targets.
|
||||
//! 1. The `sled` feature provides a `StateStore` for storing state and
|
||||
//! a `CryptoStore` for E2EE data (if `e2e-encryption` is enabled). This is
|
||||
//! the default persistent store implementation for non-WebAssembly targets.
|
||||
//! 2. The `indexeddb` feature also provides a `StateStore` for storing state
|
||||
//! and a `CryptoStore` (if `e2e-encryption` is enabled). This is the default
|
||||
//! persistent store implementation for WebAssembly targets.
|
||||
//!
|
||||
//! Both options provide a `make_store_config` convenience method to create a
|
||||
//! [`StoreConfig`] for [`ClientBuilder::store_config()`].
|
||||
|
||||
Reference in New Issue
Block a user