summaryrefslogtreecommitdiffhomepage
path: root/ios
diff options
context:
space:
mode:
authormojganii <mojgan.jelodar@codic.se>2025-03-26 15:24:08 +0100
committermojganii <mojgan.jelodar@codic.se>2025-04-15 15:38:45 +0200
commit4f52ef7f5f4072ff53d1ddc1f358cbf3e35a6d14 (patch)
tree610ce7ffe5e27b92bd4621e63e0e000a453ff802 /ios
parentf8aeeb42fa5cc7f1cc428bb4b34cb8d5de09bb51 (diff)
downloadmullvadvpn-4f52ef7f5f4072ff53d1ddc1f358cbf3e35a6d14.tar.xz
mullvadvpn-4f52ef7f5f4072ff53d1ddc1f358cbf3e35a6d14.zip
Add metadata support to send problem report
Diffstat (limited to 'ios')
-rw-r--r--ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift14
-rw-r--r--ios/MullvadREST/Extensions/String+UnsafePointer.swift8
-rw-r--r--ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift2
-rw-r--r--ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift38
-rw-r--r--ios/MullvadREST/Rust/RustProblemReportRequest.swift57
-rw-r--r--ios/MullvadRustRuntime/MullvadApiCancellable.swift5
-rw-r--r--ios/MullvadRustRuntime/include/mullvad_rust_runtime.h17
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj16
-rw-r--r--ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift2
9 files changed, 108 insertions, 51 deletions
diff --git a/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift b/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift
index 39585a88b7..d45745446e 100644
--- a/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift
+++ b/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift
@@ -263,20 +263,6 @@ extension REST {
let newExpiry: Date
}
- public struct ProblemReportRequest: Codable, Sendable {
- public let address: String
- public let message: String
- public let log: String
- public let metadata: [String: String]
-
- public init(address: String, message: String, log: String, metadata: [String: String]) {
- self.address = address
- self.message = message
- self.log = log
- self.metadata = metadata
- }
- }
-
private struct SubmitVoucherRequest: Encodable, Sendable {
let voucherCode: String
}
diff --git a/ios/MullvadREST/Extensions/String+UnsafePointer.swift b/ios/MullvadREST/Extensions/String+UnsafePointer.swift
index 4d60a1c77e..bd699bd2a8 100644
--- a/ios/MullvadREST/Extensions/String+UnsafePointer.swift
+++ b/ios/MullvadREST/Extensions/String+UnsafePointer.swift
@@ -7,10 +7,14 @@
//
import Foundation
extension String {
- func toUnsafePointer() -> UnsafePointer<UInt8>? {
+ /// Converts the `String` to an `UnsafePointer<UInt8>`. The memory allocated for the pointer
+ /// is not null-terminated, so the caller must ensure that they manage the memory properly.
+ func toUnsafePointer() -> (pointer: UnsafePointer<UInt8>?, size: UInt)? {
guard let data = self.data(using: .utf8) else { return nil }
+
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
data.copyBytes(to: buffer, count: data.count)
- return UnsafePointer(buffer)
+
+ return (UnsafePointer(buffer), UInt(data.count))
}
}
diff --git a/ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift b/ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift
index 5d2555f07f..4350161a3a 100644
--- a/ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift
+++ b/ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift
@@ -201,7 +201,7 @@ extension REST {
let newExpiry: Date
}
- public struct ProblemReportRequest: Encodable, Sendable {
+ public struct ProblemReportRequest: Codable, Sendable {
public let address: String
public let message: String
public let log: String
diff --git a/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift b/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift
index 970f70ef76..647b1b71be 100644
--- a/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift
+++ b/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift
@@ -24,43 +24,44 @@ public struct MullvadApiRequestFactory: Sendable {
let rawCompletionPointer = Unmanaged.passRetained(completionPointer).toOpaque()
- return switch request {
+ switch request {
case let .getAddressList(retryStrategy):
- MullvadApiCancellable(handle: mullvad_api_get_addresses(
+ return MullvadApiCancellable(handle: mullvad_api_get_addresses(
apiContext.context,
rawCompletionPointer,
retryStrategy.toRustStrategy()
))
case let .getRelayList(retryStrategy, etag: etag):
- MullvadApiCancellable(handle: mullvad_api_get_relays(
+ return MullvadApiCancellable(handle: mullvad_api_get_relays(
apiContext.context,
rawCompletionPointer,
retryStrategy.toRustStrategy(),
etag
))
case let .sendProblemReport(retryStrategy, problemReportRequest):
- MullvadApiCancellable(handle: mullvad_api_send_problem_report(
+ let rustRequest = RustProblemReportRequest(from: problemReportRequest)
+ return MullvadApiCancellable(handle: mullvad_api_send_problem_report(
apiContext.context,
rawCompletionPointer,
retryStrategy.toRustStrategy(),
- problemReportRequest.toRust()
+ rustRequest.toRust()
))
case let .getAccount(retryStrategy, accountNumber: accountNumber):
- MullvadApiCancellable(handle: mullvad_api_get_account(
+ return MullvadApiCancellable(handle: mullvad_api_get_account(
apiContext.context,
rawCompletionPointer,
retryStrategy.toRustStrategy(),
accountNumber
))
case let .createAccount(retryStrategy):
- MullvadApiCancellable(handle: mullvad_api_create_account(
+ return MullvadApiCancellable(handle: mullvad_api_create_account(
apiContext.context,
rawCompletionPointer,
retryStrategy.toRustStrategy()
))
case let .deleteAccount(retryStrategy, accountNumber: accountNumber):
- MullvadApiCancellable(handle: mullvad_api_delete_account(
+ return MullvadApiCancellable(handle: mullvad_api_delete_account(
apiContext.context,
rawCompletionPointer,
retryStrategy.toRustStrategy(),
@@ -74,24 +75,3 @@ public struct MullvadApiRequestFactory: Sendable {
extension REST {
public typealias MullvadApiRequestHandler = (((MullvadApiResponse) throws -> Void)?) -> MullvadApiCancellable
}
-
-private extension REST.ProblemReportRequest {
- func toRust() -> UnsafePointer<SwiftProblemReportRequest> {
- let structPointer = UnsafeMutablePointer<SwiftProblemReportRequest>.allocate(capacity: 1)
-
- let addressPointer = address.toUnsafePointer()
- let messagePointer = message.toUnsafePointer()
- let logPointer = log.toUnsafePointer()
-
- structPointer.initialize(to: SwiftProblemReportRequest(
- address: addressPointer,
- address_len: UInt(address.utf8.count),
- message: messagePointer,
- message_len: UInt(message.utf8.count),
- log: logPointer,
- log_len: UInt(log.utf8.count)
- ))
-
- return UnsafePointer(structPointer)
- }
-}
diff --git a/ios/MullvadREST/Rust/RustProblemReportRequest.swift b/ios/MullvadREST/Rust/RustProblemReportRequest.swift
new file mode 100644
index 0000000000..8bd8a4490f
--- /dev/null
+++ b/ios/MullvadREST/Rust/RustProblemReportRequest.swift
@@ -0,0 +1,57 @@
+//
+// RustProblemReportRequest.swift
+// MullvadVPN
+//
+// Created by Mojgan on 2025-03-21.
+// Copyright © 2025 Mullvad VPN AB. All rights reserved.
+//
+import MullvadLogging
+import MullvadRustRuntime
+
+final public class RustProblemReportRequest {
+ typealias StringPointer = (pointer: UnsafePointer<UInt8>?, size: UInt)?
+ private let logger = Logger(label: "RustProblemReportRequest")
+ private let problemReportMetaData: ProblemReportMetadata
+ private let addressPointer: StringPointer
+ private let messagePointer: StringPointer
+ private let logPointer: StringPointer
+ public init(from request: REST.ProblemReportRequest) {
+ addressPointer = request.address.toUnsafePointer()
+ messagePointer = request.message.toUnsafePointer()
+ logPointer = request.log.toUnsafePointer()
+
+ self.problemReportMetaData = swift_problem_report_meta_data_new()
+
+ for (key, value) in request.metadata {
+ key.withCString { keyPtr in
+ value.withCString { valuePtr in
+ if swift_problem_report_meta_data_add(problemReportMetaData, keyPtr, valuePtr) == false {
+ logger
+ .error(
+ "Failed to add metadata. Key: '\(key)' might be invalid or contain unsupported characters."
+ )
+ }
+ }
+ }
+ }
+ }
+
+ public func toRust() -> SwiftProblemReportRequest {
+ return SwiftProblemReportRequest(
+ address: addressPointer?.pointer ?? nil,
+ address_len: addressPointer?.size ?? 0,
+ message: messagePointer?.pointer ?? nil,
+ message_len: messagePointer?.size ?? 0,
+ log: logPointer?.pointer ?? nil,
+ log_len: logPointer?.size ?? 0,
+ meta_data: problemReportMetaData
+ )
+ }
+
+ public func release() {
+ swift_problem_report_meta_data_free(problemReportMetaData)
+ addressPointer?.pointer?.deallocate()
+ messagePointer?.pointer?.deallocate()
+ logPointer?.pointer?.deallocate()
+ }
+}
diff --git a/ios/MullvadRustRuntime/MullvadApiCancellable.swift b/ios/MullvadRustRuntime/MullvadApiCancellable.swift
index 6c76bc3c14..3c7c184096 100644
--- a/ios/MullvadRustRuntime/MullvadApiCancellable.swift
+++ b/ios/MullvadRustRuntime/MullvadApiCancellable.swift
@@ -10,12 +10,15 @@ import MullvadTypes
public class MullvadApiCancellable: Cancellable {
private let handle: SwiftCancelHandle
+ private let deinitializer: (() -> Void)?
- public init(handle: consuming SwiftCancelHandle) {
+ public init(handle: consuming SwiftCancelHandle, deinitializer: (() -> Void)? = nil) {
self.handle = handle
+ self.deinitializer = deinitializer
}
deinit {
+ deinitializer?()
mullvad_api_cancel_task_drop(handle)
}
diff --git a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
index 77dac6b4da..958144c802 100644
--- a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
+++ b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
@@ -24,6 +24,8 @@ typedef struct EncryptedDnsProxyState EncryptedDnsProxyState;
typedef struct ExchangeCancelToken ExchangeCancelToken;
+typedef struct Map Map;
+
typedef struct RequestCancelHandle RequestCancelHandle;
typedef struct RetryStrategy RetryStrategy;
@@ -54,6 +56,10 @@ typedef struct CompletionCookie {
void *inner;
} CompletionCookie;
+typedef struct ProblemReportMetadata {
+ struct Map *inner;
+} ProblemReportMetadata;
+
typedef struct SwiftProblemReportRequest {
const uint8_t *address;
uintptr_t address_len;
@@ -61,6 +67,7 @@ typedef struct SwiftProblemReportRequest {
uintptr_t message_len;
const uint8_t *log;
uintptr_t log_len;
+ struct ProblemReportMetadata meta_data;
} SwiftProblemReportRequest;
typedef struct ProxyHandle {
@@ -270,7 +277,15 @@ struct SwiftRetryStrategy mullvad_api_retry_strategy_exponential(uintptr_t max_r
struct SwiftCancelHandle mullvad_api_send_problem_report(struct SwiftApiContext api_context,
void *completion_cookie,
struct SwiftRetryStrategy retry_strategy,
- const struct SwiftProblemReportRequest *request);
+ struct SwiftProblemReportRequest request);
+
+struct ProblemReportMetadata swift_problem_report_meta_data_new(void);
+
+bool swift_problem_report_meta_data_add(struct ProblemReportMetadata map,
+ const char *key,
+ const char *value);
+
+void swift_problem_report_meta_data_free(struct ProblemReportMetadata map);
/**
* Initializes a valid pointer to an instance of `EncryptedDnsProxyState`.
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 8c64f72340..7adfaf5e2d 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -1081,6 +1081,7 @@
F0E8E4C32A602E0D00ED26A3 /* AccountDeletionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E8E4C22A602E0D00ED26A3 /* AccountDeletionViewModel.swift */; };
F0E8E4C52A60499100ED26A3 /* AccountDeletionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E8E4C42A60499100ED26A3 /* AccountDeletionViewController.swift */; };
F0E8E4C92A604E7400ED26A3 /* AccountDeletionInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E8E4C82A604E7400ED26A3 /* AccountDeletionInteractor.swift */; };
+ F0EEFBA22D8D61A9007FE4B3 /* RustProblemReportRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0EEFB9E2D8D60E1007FE4B3 /* RustProblemReportRequest.swift */; };
F0EF50D52A949F8E0031E8DF /* ChangeLogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0EF50D42A949F8E0031E8DF /* ChangeLogViewModel.swift */; };
F0F316192BF3572B0078DBCF /* RelaySelectorResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0F316182BF3572B0078DBCF /* RelaySelectorResult.swift */; };
F0F3161B2BF358590078DBCF /* NoRelaysSatisfyingConstraintsError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0F3161A2BF358590078DBCF /* NoRelaysSatisfyingConstraintsError.swift */; };
@@ -2481,6 +2482,7 @@
F0E8E4C22A602E0D00ED26A3 /* AccountDeletionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeletionViewModel.swift; sourceTree = "<group>"; };
F0E8E4C42A60499100ED26A3 /* AccountDeletionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeletionViewController.swift; sourceTree = "<group>"; };
F0E8E4C82A604E7400ED26A3 /* AccountDeletionInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeletionInteractor.swift; sourceTree = "<group>"; };
+ F0EEFB9E2D8D60E1007FE4B3 /* RustProblemReportRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RustProblemReportRequest.swift; sourceTree = "<group>"; };
F0EF50D42A949F8E0031E8DF /* ChangeLogViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeLogViewModel.swift; sourceTree = "<group>"; };
F0F1EF8C2BE8FF0A00CED01D /* LaunchArguments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchArguments.swift; sourceTree = "<group>"; };
F0F316182BF3572B0078DBCF /* RelaySelectorResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelaySelectorResult.swift; sourceTree = "<group>"; };
@@ -2700,6 +2702,7 @@
F0DC779F2B2222D20087F09D /* Relay */,
06FAE67B28F83CA50033DD93 /* REST.swift */,
F0DC77A12B2313330087F09D /* RetryStrategy */,
+ F0EEFBA02D8D6133007FE4B3 /* Rust */,
F0DC77A02B2223290087F09D /* Transport */,
);
path = MullvadREST;
@@ -4513,16 +4516,16 @@
A992DA1E2C24709F00DE7CE5 /* MullvadRustRuntime */ = {
isa = PBXGroup;
children = (
- A9D9A4D32C36E1EA004088DD /* mullvad_rust_runtime.h */,
- A992DA1F2C24709F00DE7CE5 /* MullvadRustRuntime.h */,
014449942CA293B100C0C2F2 /* EncryptedDNSProxy.swift */,
A948809A2BC9308D0090A44C /* EphemeralPeerExchangeActor.swift */,
A9EB4F9C2B7FAB21002A2D7A /* EphemeralPeerNegotiator.swift */,
A9A557F42B7E3E5C0017ADA8 /* EphemeralPeerReceiver.swift */,
+ A9D9A4D32C36E1EA004088DD /* mullvad_rust_runtime.h */,
7A99D3702D56220E00891FF7 /* MullvadApiCancellable.swift */,
7A3215702D392F0B005DF395 /* MullvadApiCompletion.swift */,
7AB931232D43C2C2005FCEBA /* MullvadApiContext.swift */,
7AB931252D43D222005FCEBA /* MullvadApiResponse.swift */,
+ A992DA1F2C24709F00DE7CE5 /* MullvadRustRuntime.h */,
F0DDE40F2B220458006B57A7 /* ShadowSocksProxy.swift */,
584023212A406BF5007B27AC /* TunnelObfuscator.swift */,
);
@@ -4798,6 +4801,14 @@
path = AccountDeletion;
sourceTree = "<group>";
};
+ F0EEFBA02D8D6133007FE4B3 /* Rust */ = {
+ isa = PBXGroup;
+ children = (
+ F0EEFB9E2D8D60E1007FE4B3 /* RustProblemReportRequest.swift */,
+ );
+ path = Rust;
+ sourceTree = "<group>";
+ };
F0EF50D12A8FA47E0031E8DF /* ChangeLog */ = {
isa = PBXGroup;
children = (
@@ -5797,6 +5808,7 @@
F0B894EF2BF751C500817A42 /* RelayWithLocation.swift in Sources */,
F0DDE42C2B220A15006B57A7 /* Midpoint.swift in Sources */,
A90763C72B2858DC0045ADF0 /* CancellableChain.swift in Sources */,
+ F0EEFBA22D8D61A9007FE4B3 /* RustProblemReportRequest.swift in Sources */,
7AB9312F2D4A5D0A005FCEBA /* MullvadApiNetworkOperation.swift in Sources */,
06799AF128F98E4800ACD94E /* RESTAPIProxy.swift in Sources */,
F0DDE42A2B220A15006B57A7 /* Haversine.swift in Sources */,
diff --git a/ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift b/ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift
index 55fc1df7d3..18bf3c88a5 100644
--- a/ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift
+++ b/ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift
@@ -80,7 +80,7 @@ final class ProblemReportInteractor: @unchecked Sendable {
metadata: metadataDict
)
- _ = self.apiProxy.mullvadSendProblemReport(request, retryStrategy: .default, completionHandler: { result in
+ _ = self.apiProxy.sendProblemReport(request, retryStrategy: .default, completionHandler: { result in
DispatchQueue.main.async {
completion(result)
}