diff --git a/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/NativeCryptoFacadeReceiveDispatcher.kt b/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/NativeCryptoFacadeReceiveDispatcher.kt index 27933003084c..733a50f11251 100644 --- a/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/NativeCryptoFacadeReceiveDispatcher.kt +++ b/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/NativeCryptoFacadeReceiveDispatcher.kt @@ -2,17 +2,16 @@ @file:Suppress("NAME_SHADOWING") - package de.tutao.tutashared.ipc -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json +import kotlinx.serialization.* +import kotlinx.serialization.json.* class NativeCryptoFacadeReceiveDispatcher( private val json: Json, private val facade: NativeCryptoFacade, ) { - + suspend fun dispatch(method: String, arg: List): String { when (method) { "rsaEncrypt" -> { @@ -26,7 +25,6 @@ class NativeCryptoFacadeReceiveDispatcher( ) return json.encodeToString(result) } - "rsaDecrypt" -> { val privateKey: RsaPrivateKey = json.decodeFromString(arg[0]) val data: DataWrapper = json.decodeFromString(arg[1]) @@ -36,7 +34,6 @@ class NativeCryptoFacadeReceiveDispatcher( ) return json.encodeToString(result) } - "aesEncryptFile" -> { val key: DataWrapper = json.decodeFromString(arg[0]) val fileUri: String = json.decodeFromString(arg[1]) @@ -48,7 +45,6 @@ class NativeCryptoFacadeReceiveDispatcher( ) return json.encodeToString(result) } - "aesDecryptFile" -> { val key: DataWrapper = json.decodeFromString(arg[0]) val fileUri: String = json.decodeFromString(arg[1]) @@ -58,7 +54,6 @@ class NativeCryptoFacadeReceiveDispatcher( ) return json.encodeToString(result) } - "argon2idGeneratePassphraseKey" -> { val passphrase: String = json.decodeFromString(arg[0]) val salt: DataWrapper = json.decodeFromString(arg[1]) @@ -68,7 +63,6 @@ class NativeCryptoFacadeReceiveDispatcher( ) return json.encodeToString(result) } - "generateKyberKeypair" -> { val seed: DataWrapper = json.decodeFromString(arg[0]) val result: KyberKeyPair = this.facade.generateKyberKeypair( @@ -76,7 +70,6 @@ class NativeCryptoFacadeReceiveDispatcher( ) return json.encodeToString(result) } - "kyberEncapsulate" -> { val publicKey: KyberPublicKey = json.decodeFromString(arg[0]) val seed: DataWrapper = json.decodeFromString(arg[1]) @@ -86,7 +79,6 @@ class NativeCryptoFacadeReceiveDispatcher( ) return json.encodeToString(result) } - "kyberDecapsulate" -> { val privateKey: KyberPrivateKey = json.decodeFromString(arg[0]) val ciphertext: DataWrapper = json.decodeFromString(arg[1]) @@ -96,7 +88,6 @@ class NativeCryptoFacadeReceiveDispatcher( ) return json.encodeToString(result) } - else -> throw Error("unknown method for NativeCryptoFacade: $method") } } diff --git a/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/PersistedCredentials.kt b/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/PersistedCredentials.kt index d53164af8797..4fecd5244436 100644 --- a/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/PersistedCredentials.kt +++ b/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/PersistedCredentials.kt @@ -3,7 +3,8 @@ package de.tutao.tutashared.ipc -import kotlinx.serialization.Serializable +import kotlinx.serialization.* +import kotlinx.serialization.json.* /** diff --git a/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/UnencryptedCredentials.kt b/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/UnencryptedCredentials.kt index c031d4109b73..20170615e6aa 100644 --- a/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/UnencryptedCredentials.kt +++ b/app-android/tutashared/src/main/java/de/tutao/tutashared/generated_ipc/UnencryptedCredentials.kt @@ -3,7 +3,8 @@ package de.tutao.tutashared.ipc -import kotlinx.serialization.Serializable +import kotlinx.serialization.* +import kotlinx.serialization.json.* /** diff --git a/buildSrc/DesktopBuilder.js b/buildSrc/DesktopBuilder.js index 8e720566d072..08c94619ec50 100644 --- a/buildSrc/DesktopBuilder.js +++ b/buildSrc/DesktopBuilder.js @@ -121,7 +121,10 @@ async function rollupDesktop(dirname, outDir, version, platform, architecture, d input: [path.join(dirname, "src/common/desktop/DesktopMain.ts"), path.join(dirname, "src/common/desktop/sqlworker.ts")], // some transitive dep of a transitive dev-dep requires https://www.npmjs.com/package/url // which rollup for some reason won't distinguish from the node builtin. - external: ["url", "util", "path", "fs", "os", "http", "https", "crypto", "child_process", "electron"], + external: (id, parent, isResolved) => { + if (parent != null && parent.endsWith("node-mimimi/dist/binding.cjs")) return true + return ["url", "util", "path", "fs", "os", "http", "https", "crypto", "child_process", "electron"].includes(id) + }, preserveEntrySignatures: false, plugins: [ copyNativeModulePlugin({ @@ -131,6 +134,16 @@ async function rollupDesktop(dirname, outDir, version, platform, architecture, d architecture, nodeModule: "better-sqlite3", }), + { + // todo: this needs to work everywhere + name: "copy-mimimi-plugin", + async buildStart() { + const normalDst = path.join(path.normalize("./build/desktop/"), "node-mimimi.linux-x64-gnu.node") + const dstDir = path.dirname(normalDst) + await fs.promises.mkdir(dstDir, { recursive: true }) + await fs.promises.copyFile("./packages/node-mimimi/dist/node-mimimi.linux-x64-gnu.node", normalDst) + }, + }, typescript({ tsconfig: "tsconfig.json", outDir, diff --git a/buildSrc/DevBuild.js b/buildSrc/DevBuild.js index 298142e7f79e..203a9e6f691f 100644 --- a/buildSrc/DevBuild.js +++ b/buildSrc/DevBuild.js @@ -4,7 +4,7 @@ import { build as esbuild } from "esbuild" import { getTutanotaAppVersion, runStep, writeFile } from "./buildUtils.js" import "zx/globals" import * as env from "./env.js" -import { externalTranslationsPlugin, libDeps, preludeEnvPlugin, sqliteNativePlugin } from "./esbuildUtils.js" +import { externalTranslationsPlugin, libDeps, mimimiNativePlugin, preludeEnvPlugin, sqliteNativePlugin } from "./esbuildUtils.js" import { fileURLToPath } from "node:url" import * as LaunchHtml from "./LaunchHtml.js" import os from "node:os" @@ -173,9 +173,9 @@ async function buildDesktopPart({ version, app }) { format: "cjs", sourcemap: "linked", platform: "node", - external: ["electron"], + external: ["electron", "*.node"], banner: { - js: `globalThis.buildOptions = globalThis.buildOptions ?? {} + js: `globalThis.buildOptions = globalThis.buildOptions ?? {} globalThis.buildOptions.sqliteNativePath = "./better-sqlite3.node";`, }, plugins: [ @@ -187,6 +187,10 @@ globalThis.buildOptions.sqliteNativePath = "./better-sqlite3.node";`, architecture: process.arch, nativeBindingPath: "./better_sqlite3.node", }), + mimimiNativePlugin({ + dstPath: `./${buildDir}/desktop/`, + platform: process.platform, + }), preludeEnvPlugin(env.create({ staticUrl: null, version, mode: "Desktop", dist: false, domainConfigs })), externalTranslationsPlugin(), ], diff --git a/buildSrc/RustGenerator.js b/buildSrc/RustGenerator.js index 0190cd9e28a3..af9d1cf9ed07 100644 --- a/buildSrc/RustGenerator.js +++ b/buildSrc/RustGenerator.js @@ -13,7 +13,7 @@ import { AssociationType, Type } from "../src/common/api/common/EntityConstants. */ export function generateRustType({ type, modelName }) { let typeName = mapTypeName(type.name, modelName) - let buf = `#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + let buf = `#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ${typeName} {\n` for (let [valueName, valueProperties] of Object.entries(type.values)) { const rustType = rustValueType(valueName, type, valueProperties) @@ -59,20 +59,81 @@ pub struct ${typeName} {\n` if (type.encrypted || Object.values(type.values).some((v) => v.encrypted)) { buf += `\tpub _finalIvs: HashMap,\n` } + buf += "}" + buf += ` +impl Entity for ${typeName} { + fn type_ref() -> TypeRef { + TypeRef { + app: "${modelName}", + type_: "${typeName}", + } + } +} +` + + return buf + "\n\n" +} - buf += "\n\n" +export function generateRustServiceDefinition(appName, appVersion, services) { + let imports = new Set([ + "#![allow(unused_imports, dead_code, unused_variables)]", + "use crate::ApiCallError;", + "use crate::entities::Entity;", + "use crate::services::{PostService, GetService, PutService, DeleteService, Service, Executor, ExtraServiceParams};", + "use crate::rest_client::HttpMethod;", + "use crate::services::hidden::Nothing;", + ]) + const code = services + .map((s) => { + let serviceDefinition = ` +pub struct ${s.name}; - buf += `impl Entity for ${typeName} {\n` - buf += "\tfn type_ref() -> TypeRef {\n" - buf += `\t\tTypeRef {\n` - buf += `\t\t\tapp: "${modelName}",\n` - buf += `\t\t\ttype_: "${typeName}",\n` - buf += `\t\t}\n` - buf += "\t}\n" - buf += "}" +crate::service_impl!(base, ${s.name}, "${appName}/${s.name.toLowerCase()}", ${appVersion}); +` + + function getTypeRef(dataType) { + if (dataType) { + return `Some(${dataType}::type_ref())` + } else { + return "None" + } + } + + function addImports(appName, input, output) { + if (input) { + imports.add(`use crate::entities::${appName}::${input};`) + } + if (output) { + imports.add(`use crate::entities::${appName}::${output};`) + } + } + + function makeImpl(name, input, output) { + addImports(appName, input, output) + return `crate::service_impl!(${name}, ${s.name}, ${input ?? "()"}, ${output ?? "()"});\n` + } + + if (s.bodyTypes.POST_IN || s.bodyTypes.POST_OUT) { + serviceDefinition += makeImpl("POST", s.bodyTypes.POST_IN, s.bodyTypes.POST_OUT) + } + + if (s.bodyTypes.GET_IN || s.bodyTypes.GET_OUT) { + serviceDefinition += makeImpl("GET", s.bodyTypes.GET_IN, s.bodyTypes.GET_OUT) + } + + if (s.bodyTypes.PUT_IN || s.bodyTypes.PUT_OUT) { + serviceDefinition += makeImpl("PUT", s.bodyTypes.PUT_IN, s.bodyTypes.PUT_OUT) + } + + if (s.bodyTypes.DELETE_IN || s.bodyTypes.DELETE_OUT) { + serviceDefinition += makeImpl("DELETE", s.bodyTypes.DELETE_IN, s.bodyTypes.DELETE_OUt) + } - return buf + return serviceDefinition + }) + .join("\n") + return Array.from(imports).join("\n") + code } /** diff --git a/buildSrc/esbuildUtils.js b/buildSrc/esbuildUtils.js index 172d64f864e3..28c61b127c77 100644 --- a/buildSrc/esbuildUtils.js +++ b/buildSrc/esbuildUtils.js @@ -6,7 +6,7 @@ import { aliasPath as esbuildPluginAliasPath } from "esbuild-plugin-alias-path" /** * Little plugin that obtains compiled better-sqlite3, copies it to dstPath and sets the path to nativeBindingPath. - * We do not use default file loader from esbuild, it is much simpler and reliable to do it manually and it doesn't work for dynamic import (like in this case) + * We do not use default file loader from esbuild, it is much simpler and reliable to do it manually, and it doesn't work for dynamic import (like in this case) * anyway. * It will also replace `buildOptions.sqliteNativePath` with the nativeBindingPath */ @@ -37,6 +37,41 @@ export function sqliteNativePlugin({ environment, dstPath, nativeBindingPath, pl } } +export function mimimiNativePlugin({ dstPath, platform }) { + return { + name: "mimimi-native-plugin", + setup(build) { + const options = build.initialOptions + options.define = options.define ?? {} + + build.onStart(async () => { + let nativeBinaryName + switch (platform) { + case "linux": + nativeBinaryName = "node-mimimi.linux-x64-gnu.node" + break + case "win32": + nativeBinaryName = "node-mimimi.win32-x64-msvc.node" + break + case "darwin": + nativeBinaryName = "node-mimimi.darwin-universal.node" + break + default: + throw Error(`could not find node-mimimi binary: platform ${platform} is unknown`) + } + + // Replace mentions of buildOptions.mimimiNativePath with the actual path + options.define["buildOptions.mimimiNativePath"] = `"./${nativeBinaryName}"` + + const nativeBinarySourcePath = path.join(process.cwd(), "./packages/node-mimimi/dist", nativeBinaryName) + + await fs.promises.mkdir(path.dirname(dstPath), { recursive: true }) + await fs.promises.copyFile(nativeBinarySourcePath, path.join(process.cwd(), dstPath, nativeBinaryName)) + }) + }, + } +} + /** Little plugin that replaces imports for libs from dependencyMap with their prebuilt versions in libs directory. */ export function libDeps(prefix = ".") { const absoluteDependencyMap = Object.fromEntries( diff --git a/buildSrc/packageBuilderFunctions.js b/buildSrc/packageBuilderFunctions.js index a8bddf024f7b..917cb9382060 100644 --- a/buildSrc/packageBuilderFunctions.js +++ b/buildSrc/packageBuilderFunctions.js @@ -12,7 +12,7 @@ export async function buildRuntimePackages() { // tsconfig is rather JSON5, if it becomes a problem switch to JSON5 parser here const tsconfig = JSON.parse(await fs.readFile("tsconfig.json", { encoding: "utf-8" })) const packagePaths = tsconfig.references.map((ref) => ref.path) - await $`npx tsc -b ${packagePaths}` + await Promise.all(packagePaths.map((dir) => $`cd ${dir} && npm run build`)) } /** @@ -20,5 +20,5 @@ export async function buildRuntimePackages() { */ export async function buildPackages(pathPrefix = ".") { const packages = await glob(`${pathPrefix}/packages/*`, { deep: 1, onlyDirectories: true }) - await $`npx tsc -b ${packages}` + await Promise.all(packages.map((dir) => $`cd ${dir} && npm run build`)) } diff --git a/ipc-schema/facades/ImapImportSystemFacade.json b/ipc-schema/facades/ImapImportSystemFacade.json new file mode 100644 index 000000000000..bf08a6fd9868 --- /dev/null +++ b/ipc-schema/facades/ImapImportSystemFacade.json @@ -0,0 +1,28 @@ +{ + "name": "ImapImportSystemFacade", + "type": "facade", + "senders": ["web"], + "receivers": ["desktop"], + "doc": "Facade implemented by the native desktop client starting and stopping an IMAP import.", + "methods": { + "setup": { + "doc": "Initializing the IMAP import.", + "arg": [ + { + "imapCredentials": "ImapCredentials" + } + ], + "ret": "void" + }, + "startImport": { + "doc": "Start the IMAP import.", + "arg": [], + "ret": "void" + }, + "stopImport": { + "doc": "Stop a running IMAP import.", + "arg": [], + "ret": "void" + } + } +} diff --git a/ipc-schema/types/ImapCredentials.json b/ipc-schema/types/ImapCredentials.json new file mode 100644 index 000000000000..05c3815c6284 --- /dev/null +++ b/ipc-schema/types/ImapCredentials.json @@ -0,0 +1,7 @@ +{ + "name": "ImapCredentials", + "type": "typeref", + "location": { + "typescript": "../packages/node-mimimi/dist/binding.js" + } +} diff --git a/ipc-schema/types/MailBundle.json b/ipc-schema/types/MailBundle.json index 1b0a4fd50b5b..5339e21ebec0 100644 --- a/ipc-schema/types/MailBundle.json +++ b/ipc-schema/types/MailBundle.json @@ -2,6 +2,6 @@ "name": "MailBundle", "type": "typeref", "location": { - "typescript": "../src/mail-app/mail/export/Bundler.js" + "typescript": "../src/common/mailFunctionality/SharedMailUtils.js" } } diff --git a/package-lock.json b/package-lock.json index e46865dbb2a2..e206cbafc0c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,8 @@ "./packages/*" ], "dependencies": { + "@napi-rs/cli": "^2.18.4", + "@tutao/node-mimimi": "244.240923.0", "@tutao/oxmsg": "0.0.9-beta.0", "@tutao/tuta-wasm-loader": "247.241007.0", "@tutao/tutanota-crypto": "247.241007.0", @@ -1170,6 +1172,21 @@ "node": ">=10" } }, + "node_modules/@napi-rs/cli": { + "version": "2.18.4", + "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.18.4.tgz", + "integrity": "sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg==", + "bin": { + "napi": "scripts/index.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2040,6 +2057,10 @@ "resolved": "packages/licc", "link": true }, + "node_modules/@tutao/node-mimimi": { + "resolved": "packages/node-mimimi", + "link": true + }, "node_modules/@tutao/otest": { "resolved": "packages/otest", "link": true @@ -9272,6 +9293,10 @@ "node": "*" } }, + "node_modules/tuta-imap": { + "resolved": "packages/tuta-imap", + "link": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -10028,6 +10053,26 @@ "node": ">= 16.0.0" } }, + "packages/node-mimimi": { + "name": "@tutao/node-mimimi", + "version": "244.240917.0", + "license": "MIT", + "devDependencies": { + "@napi-rs/cli": "^2.18.4", + "@tutao/otest": "244.240910.0", + "typescript": "5.3.3", + "zx": "8.1.5" + }, + "engines": { + "node": ">= 20" + } + }, + "packages/node-mimimi/node_modules/@tutao/otest": { + "version": "244.240910.0", + "resolved": "https://registry.npmjs.org/@tutao/otest/-/otest-244.240910.0.tgz", + "integrity": "sha512-QYasrO+XMEjO2LcTd/gC+XgiHXJT5wxL4hWhwLtmZWfCMu4pCJGrirHFgrV+cTLkx+9sPCO4eisISAP3Fj7xCA==", + "dev": true + }, "packages/otest": { "name": "@tutao/otest", "version": "247.241007.0", @@ -10036,6 +10081,9 @@ "typescript": "5.3.3" } }, + "packages/tuta-imap": { + "version": "1.0.0" + }, "packages/tuta-wasm-loader": { "name": "@tutao/tuta-wasm-loader", "version": "247.241007.0", diff --git a/package.json b/package.json index 2cdd14c58fb4..29797208f2f6 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,8 @@ "fix": "npm run style:fix && npm run lint:fix" }, "dependencies": { + "@napi-rs/cli": "^2.18.4", + "@tutao/node-mimimi": "244.240923.0", "@tutao/oxmsg": "0.0.9-beta.0", "@tutao/tuta-wasm-loader": "247.241007.0", "@tutao/tutanota-crypto": "247.241007.0", diff --git a/packages/node-mimimi/.cargo/config.toml b/packages/node-mimimi/.cargo/config.toml new file mode 100644 index 000000000000..0c17df095caa --- /dev/null +++ b/packages/node-mimimi/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.x86_64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] \ No newline at end of file diff --git a/packages/node-mimimi/.gitignore b/packages/node-mimimi/.gitignore new file mode 100644 index 000000000000..a2b5be15ddfd --- /dev/null +++ b/packages/node-mimimi/.gitignore @@ -0,0 +1,197 @@ +# Created by https://www.toptal.com/developers/gitignore/api/node +# Edit at https://www.toptal.com/developers/gitignore?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# End of https://www.toptal.com/developers/gitignore/api/node + +# Created by https://www.toptal.com/developers/gitignore/api/macos +# Edit at https://www.toptal.com/developers/gitignore?templates=macos + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +# End of https://www.toptal.com/developers/gitignore/api/macos + +# Created by https://www.toptal.com/developers/gitignore/api/windows +# Edit at https://www.toptal.com/developers/gitignore?templates=windows + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/windows + +#Added by cargo + +/target +Cargo.lock + +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +*.node diff --git a/packages/node-mimimi/.npmignore b/packages/node-mimimi/.npmignore new file mode 100644 index 000000000000..ec144db2a711 --- /dev/null +++ b/packages/node-mimimi/.npmignore @@ -0,0 +1,13 @@ +target +Cargo.lock +.cargo +.github +npm +.eslintrc +.prettierignore +rustfmt.toml +yarn.lock +*.node +.yarn +__test__ +renovate.json diff --git a/packages/node-mimimi/Cargo.toml b/packages/node-mimimi/Cargo.toml new file mode 100644 index 000000000000..8f79859dc334 --- /dev/null +++ b/packages/node-mimimi/Cargo.toml @@ -0,0 +1,39 @@ +[package] +edition = "2021" +name = "tutao_node-mimimi" +version = "244.240917.0" + +[lib] +# need to have lib to be able to use this from rust examples +crate-type = ["cdylib", "lib"] + +[features] +default = [] +# needed to turn off the autogenerated ffi when using the examples +rust = [] + +[dependencies] +# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix +napi = { version = "2.16.10", default-features = false, features = ["napi9", "async", "tokio_rt"] } +napi-derive = "2.16.12" +tuta-sdk = { path = "../../tuta-sdk/rust/sdk", version = "*" } +tuta-imap = { path = "../tuta-imap", version = "*" } +async-trait = "0.1.83" +hyper = { version = "1.4.1", features = ["client"] } +hyper-util = { version = "0.1.9", features = ["full"] } +http-body-util = "0.1.2" +hyper-rustls = { version = "0.27.3", features = ["ring", "http2", "rustls-platform-verifier"] } +rustls = "0.23.14" + +[build-dependencies] +napi-build = "2.1.3" + + +[[example]] +name = "http_request" +path = "examples/http_request.rs" +required-features = ["rust"] + +[profile.release] +lto = true +strip = "symbols" diff --git a/packages/node-mimimi/README.md b/packages/node-mimimi/README.md new file mode 100644 index 000000000000..cc38f5a0e5ea --- /dev/null +++ b/packages/node-mimimi/README.md @@ -0,0 +1,34 @@ +# Node-Mimimi + +**M**ini **IM**AP **IM**porter **I**mplementation + +A native node module enabling the tuta mail desktop client to import mail from IMAP servers into your tuta account. +It's using [napi-rs](https://napi.rs/docs/introduction/getting-started) for project setup and to generate the bindings. + +## Building + +napi-rs by default generates a common js module that is supposed to be compatible with ESM named imports, but we had +problems getting it to import in all cases. One solution was found on +the [napi-rs github](https://github.com/napi-rs/napi-rs/issues/1429#issuecomment-1379743978). It works, but requires us +to build like this: + +`napi build --platform . --js binding.cjs --dts binding.d.ts` + +# Compilation + +See https://napi.rs/docs/cross-build/summary + +### Setup +1. `apt install clang llvm` + +#### Linux (from linux): +1. `rustup target add x86_64-unknown-linux-gnu` + +#### Windows (from linux): +1. `rustup target add x86_64-pc-windows-msvc` +2. `cargo install cargo-xwin` + +#### MacOS (**only** from MacOS): +1. `rustup target add x86_64-apple-darwin` +2. `rustup target add aarch64-apple-darwin` + diff --git a/packages/node-mimimi/build.rs b/packages/node-mimimi/build.rs new file mode 100644 index 000000000000..1f866b6a3c3a --- /dev/null +++ b/packages/node-mimimi/build.rs @@ -0,0 +1,5 @@ +extern crate napi_build; + +fn main() { + napi_build::setup(); +} diff --git a/packages/node-mimimi/examples/http_request.rs b/packages/node-mimimi/examples/http_request.rs new file mode 100644 index 000000000000..7030cadaf1cc --- /dev/null +++ b/packages/node-mimimi/examples/http_request.rs @@ -0,0 +1,43 @@ +use tutao_node_mimimi::net::native_rest_client::NativeRestClient; +use tutasdk::rest_client::{HttpMethod, RestClient, RestClientOptions}; +fn main() -> () { + let mut runtime_builder = napi::tokio::runtime::Builder::new_multi_thread(); + let runtime_builder = runtime_builder.enable_all(); + let Ok(runtime) = runtime_builder.build() else { + panic!("could not initialize tokio runtime"); + }; + + let handle = runtime.spawn(async_main()); + loop { + std::thread::sleep(std::time::Duration::from_secs(1)); + if handle.is_finished() { + break; + } + } +} + +async fn async_main() -> Result<(), &'static str> { + println!("starting a request"); + + let url = "https://echo.free.beeceptor.com"; + + let rest_client: NativeRestClient = NativeRestClient::try_new().expect("failed to get rest client"); + let response_pending = rest_client.request_binary( + url.to_string(), + HttpMethod::GET, + RestClientOptions { + headers: Default::default(), + body: Default::default(), + }, + ).await; + + let response = match response_pending { + Ok(res) => res, + Err(err) => panic!("failed to get response: {:?}", err), + }; + + println!("response status is {:?}", response.status); + println!("response body is {:?}", response.body.map(|v| String::from_utf8(v))); + + Ok(()) +} diff --git a/packages/node-mimimi/make.js b/packages/node-mimimi/make.js new file mode 100644 index 000000000000..256b37e03e16 --- /dev/null +++ b/packages/node-mimimi/make.js @@ -0,0 +1,41 @@ +import { Argument, program } from "commander" +import { $ } from "zx" + +await program + .usage("[options] [win|linux|darwin|native]") + .addArgument(new Argument("platform").choices(["win", "linux", "darwin", "native"]).default("native").argOptional()) + .option("-c, --clean", "clean build artifacts") + .option("-r, --release", "run a release build") + .option("-t, --test", "also build the test suite") + .action(run) + .parseAsync(process.argv) + +function getTarget(platform) { + switch (platform) { + case "win": + return "--target=x86_64-pc-windows-msvc" + case "linux": + return "--target=x86_64-unknown-linux-gnu" + case "darwin": + return "--target=x86_64-apple-darwin" + case "native": + return "" + default: + throw new Error(`unknown platform ${platform}`) + } +} + +async function run(platform, { clean, release, test }) { + if (clean) { + $`rm -r -f ./build` + $`rm -r -f ./target` + $`rm -r -f ./dist` + } + + const target = getTarget(platform) + const releaseFlag = release ? "--release" : "" + await $`napi build --platform dist --js binding.cjs --dts binding.d.ts ${target} ${releaseFlag}` + if (test) { + await $`tsc -b test` + } +} diff --git a/packages/node-mimimi/npm/darwin-universal/README.md b/packages/node-mimimi/npm/darwin-universal/README.md new file mode 100644 index 000000000000..71296da77cad --- /dev/null +++ b/packages/node-mimimi/npm/darwin-universal/README.md @@ -0,0 +1,3 @@ +# `@tutao/node-mimimi-darwin-universal` + +This is the **universal-apple-darwin** binary for `@tutao/node-mimimi` diff --git a/packages/node-mimimi/npm/darwin-universal/package.json b/packages/node-mimimi/npm/darwin-universal/package.json new file mode 100644 index 000000000000..d40371e8c5b5 --- /dev/null +++ b/packages/node-mimimi/npm/darwin-universal/package.json @@ -0,0 +1,15 @@ +{ + "name": "@tutao/node-mimimi-darwin-universal", + "version": "0.0.0", + "os": [ + "darwin" + ], + "main": "node-mimimi.darwin-universal.node", + "files": [ + "node-mimimi.darwin-universal.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/node-mimimi/npm/linux-x64-gnu/README.md b/packages/node-mimimi/npm/linux-x64-gnu/README.md new file mode 100644 index 000000000000..a6364039dd45 --- /dev/null +++ b/packages/node-mimimi/npm/linux-x64-gnu/README.md @@ -0,0 +1,3 @@ +# `@tutao/node-mimimi-linux-x64-gnu` + +This is the **x86_64-unknown-linux-gnu** binary for `@tutao/node-mimimi` diff --git a/packages/node-mimimi/npm/linux-x64-gnu/package.json b/packages/node-mimimi/npm/linux-x64-gnu/package.json new file mode 100644 index 000000000000..d7245e81837c --- /dev/null +++ b/packages/node-mimimi/npm/linux-x64-gnu/package.json @@ -0,0 +1,21 @@ +{ + "name": "@tutao/node-mimimi-linux-x64-gnu", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "main": "node-mimimi.linux-x64-gnu.node", + "files": [ + "node-mimimi.linux-x64-gnu.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "libc": [ + "glibc" + ] +} diff --git a/packages/node-mimimi/npm/win32-x64-msvc/README.md b/packages/node-mimimi/npm/win32-x64-msvc/README.md new file mode 100644 index 000000000000..0961fd962af8 --- /dev/null +++ b/packages/node-mimimi/npm/win32-x64-msvc/README.md @@ -0,0 +1,3 @@ +# `@tutao/node-mimimi-win32-x64-msvc` + +This is the **x86_64-pc-windows-msvc** binary for `@tutao/node-mimimi` diff --git a/packages/node-mimimi/npm/win32-x64-msvc/package.json b/packages/node-mimimi/npm/win32-x64-msvc/package.json new file mode 100644 index 000000000000..6f50d754ed3e --- /dev/null +++ b/packages/node-mimimi/npm/win32-x64-msvc/package.json @@ -0,0 +1,18 @@ +{ + "name": "@tutao/node-mimimi-win32-x64-msvc", + "version": "0.0.0", + "os": [ + "win32" + ], + "cpu": [ + "x64" + ], + "main": "node-mimimi.win32-x64-msvc.node", + "files": [ + "node-mimimi.win32-x64-msvc.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/node-mimimi/package.json b/packages/node-mimimi/package.json new file mode 100644 index 000000000000..2df9d977326b --- /dev/null +++ b/packages/node-mimimi/package.json @@ -0,0 +1,35 @@ +{ + "name": "@tutao/node-mimimi", + "version": "244.240917.0", + "main": "./dist/binding.cjs", + "types": "./dist/binding.d.ts", + "napi": { + "name": "node-mimimi", + "triples": { + "defaults": false, + "additional": [ + "x86_64-pc-windows-msvc", + "x86_64-unknown-linux-gnu", + "universal-apple-darwin" + ] + } + }, + "license": "MIT", + "devDependencies": { + "@tutao/otest": "244.240910.0", + "@napi-rs/cli": "^2.18.4", + "typescript": "5.3.3", + "zx": "8.1.5" + }, + "engines": { + "node": ">= 20" + }, + "type": "module", + "scripts": { + "build": "node make", + "prepublishOnly": "napi prepublish -t npm", + "test": "node make -t && cd build/test && node Suite.js && cargo test", + "universal": "napi universal", + "version": "napi version" + } +} diff --git a/packages/node-mimimi/rustfmt.toml b/packages/node-mimimi/rustfmt.toml new file mode 100644 index 000000000000..c22683acdcd6 --- /dev/null +++ b/packages/node-mimimi/rustfmt.toml @@ -0,0 +1,16 @@ +edition = "2024" + +tab_spaces = 4 +hard_tabs = true +newline_style = "Unix" +max_width = 100 +match_block_trailing_comma = true +use_field_init_shorthand = true + +# TODO: Very nice-to-have, but currently nightly only +#normalize_comments = true +#normalize_doc_attributes = true +#group_imports = "StdExternalCrate" +#reorder_impl_items = true +#combine_control_expr = false +#condense_wildcard_suffixes = true diff --git a/packages/node-mimimi/src/imap.rs b/packages/node-mimimi/src/imap.rs new file mode 100644 index 000000000000..f6dba8c74e32 --- /dev/null +++ b/packages/node-mimimi/src/imap.rs @@ -0,0 +1,2 @@ +pub mod imap_client; +pub mod credentials; \ No newline at end of file diff --git a/packages/node-mimimi/src/imap/credentials.rs b/packages/node-mimimi/src/imap/credentials.rs new file mode 100644 index 000000000000..3513440cd45f --- /dev/null +++ b/packages/node-mimimi/src/imap/credentials.rs @@ -0,0 +1,10 @@ +#[napi(object)] +/// passed in from js before being validated and used for logging into the imap server +pub struct ImapCredentials { + /// hostname of the imap server to import mail from + pub host: String, + pub port: String, + pub username: Option, + pub password: Option, + pub access_token: Option, +} \ No newline at end of file diff --git a/packages/node-mimimi/src/imap/imap_client.rs b/packages/node-mimimi/src/imap/imap_client.rs new file mode 100644 index 000000000000..7d052a45de83 --- /dev/null +++ b/packages/node-mimimi/src/imap/imap_client.rs @@ -0,0 +1,22 @@ +#[napi(object)] +#[derive(Clone)] +pub struct ImapImportParams { + pub root_import_mail_folder_name: String, +} + +/// current state of the imap import for this tuta account +/// requires an initialized SDK! +#[napi] +pub enum ImapImportStatus { + NotInitialized, + Paused, + Running, + Postponed, + Finished, +} + +#[napi(object)] +#[derive(Clone)] +pub struct ImapImportConfig { + pub params: ImapImportParams, +} diff --git a/packages/node-mimimi/src/importer.rs b/packages/node-mimimi/src/importer.rs new file mode 100644 index 000000000000..5b34b447048e --- /dev/null +++ b/packages/node-mimimi/src/importer.rs @@ -0,0 +1,96 @@ +use crate::imap::credentials::ImapCredentials; +use crate::logging::Console; +use crate::net::native_rest_client::NativeRestClient; +use crate::tuta::credentials::TutaCredentials; +use napi::bindgen_prelude::*; +use napi::threadsafe_function::ErrorStrategy::T; +use napi::tokio::sync::Mutex; +use napi::tokio::sync::OnceCell; +use napi::JsObject; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use tuta_imap::client::TutanotaImapClient; +use tutasdk::login::Credentials; +use tutasdk::rest_client::{HttpMethod, RestClient, RestClientError, RestClientOptions, RestResponse}; +use tutasdk::{LoggedInSdk, Sdk}; + +const TAG: &'static str = file!(); + +type Handle = Arc>; + +#[cfg(not(feature = "rust"))] +#[napi] +pub struct ImportCredentials { + console: &'static Console, + tuta_credentials: TutaCredentials, + imap_credentials: ImapCredentials, + + /// Represents a fallible async initialization process that + /// results in a logged in importer (tuta and imap) and that can be retried. + handle: Mutex>, +} + +#[cfg(not(feature = "rust"))] +#[napi] +impl ImportCredentials { + #[napi(factory)] + pub fn setup(env: Env, tuta_credentials: TutaCredentials, imap_credentials: ImapCredentials) -> Result { + let console = Console::get(env); + Ok(ImportCredentials { + console, + tuta_credentials, + imap_credentials, + handle: Mutex::new(None), + }) + } + + #[napi(ts_return_type = "Promise")] + /// Log into tuta + imap and return a handle to an object that can be used to control the import process. + pub fn login(&'static self, env: Env) -> Result { + env.execute_tokio_future( + async { self.prepare_inner().await }, + |env: &mut Env, inner: ImporterInner| Ok(Importer::new(inner)), + ) + } + + async fn prepare_inner(&self) -> Result { + let sse_origin: String = self.tuta_credentials.sse_origin.clone(); + let rest_client = NativeRestClient::try_new()?; + let client_version = self.tuta_credentials.client_version.clone(); + let sdk = Sdk::new(sse_origin, Arc::new(rest_client), client_version); + + let credentials: Credentials = self.tuta_credentials.clone().try_into(). + map_err(|_| napi::Error::from_reason("failed to convert credentials"))?; + let logged_in_sdk = sdk.login(credentials).await.map_err(|e| { + self.console.error(TAG, e.to_string().as_str()); + Error::from_reason("failed to log into tuta") + })?; + + let imap = Arc::new(TutanotaImapClient::start_new_session(42)); + + Ok(ImporterInner { + console: self.console, + sdk: logged_in_sdk, + imap, + }) + } +} + +struct ImporterInner { + console: &'static Console, + sdk: Arc, + imap: Arc, +} + +#[cfg(not(feature = "rust"))] +#[napi] +pub struct Importer { + inner: Handle, +} + +#[cfg(not(feature = "rust"))] +impl Importer { + fn new(inner: ImporterInner) -> Self { + Self { inner: Arc::new(Mutex::new(inner)) } + } +} \ No newline at end of file diff --git a/packages/node-mimimi/src/lib.rs b/packages/node-mimimi/src/lib.rs new file mode 100644 index 000000000000..6b0d582379e9 --- /dev/null +++ b/packages/node-mimimi/src/lib.rs @@ -0,0 +1,17 @@ +#![deny(clippy::all)] +#[macro_use] +extern crate napi_derive; +extern crate tutasdk; + +#[cfg(not(feature = "rust"))] +pub mod importer; + +#[cfg(not(feature = "rust"))] +pub mod tuta; + +#[cfg(not(feature = "rust"))] +pub mod imap; + +#[cfg(not(feature = "rust"))] +pub mod logging; +pub mod net; diff --git a/packages/node-mimimi/src/logging.rs b/packages/node-mimimi/src/logging.rs new file mode 100644 index 000000000000..f52ea6970eea --- /dev/null +++ b/packages/node-mimimi/src/logging.rs @@ -0,0 +1,13 @@ +use napi::bindgen_prelude::*; + +mod logger; +pub(crate) mod console; + +/// todo: plumb through SDK's log messages? it's currently using simple_logger when not compiled +/// todo: for ios or android. + +pub use crate::logging::console::Console; + + + + diff --git a/packages/node-mimimi/src/logging/console.rs b/packages/node-mimimi/src/logging/console.rs new file mode 100644 index 000000000000..c94861fe8af4 --- /dev/null +++ b/packages/node-mimimi/src/logging/console.rs @@ -0,0 +1,87 @@ +use crate::logging::logger::{LogLevel, LogMessage, Logger}; +use napi::Env; +use std::sync::OnceLock; + +const TAG: &'static str = file!(); + +pub static INSTANCE: OnceLock = OnceLock::new(); + +/// A way for the rust code to log messages to the main applications log files +/// without having to deal with obtaining a reference to console each time. +#[derive(Clone)] +pub struct Console { + tx: std::sync::mpsc::Sender, +} + +impl Console { + pub fn get(env: Env) -> &'static Self { + let (tx, rx) = std::sync::mpsc::channel::(); + let console = Console { tx }; + let logger = Logger::new(rx); + let Ok(()) = INSTANCE.set(console) else { + // some other thread already initialized the cell, we don't need to set up the logger. + return INSTANCE.get().expect("should already have been initialized!"); + }; + + // this may be the instance set by another thread, but that's okay. + let console = INSTANCE.get().expect("not initialized"); + let maybe_async_task = env.spawn(logger); + match maybe_async_task { + Ok(_) => console.log(TAG, "spawned logger"), + Err(e) => eprintln!("failed to spawn logger: {e}"), + }; + set_panic_hook(console); + console + } + + pub fn log(&self, tag: &str, message: &str) { + // todo: this returns Err if the logger closes the channel, what to do in that case? + let _ = self.tx.send(LogMessage { + level: LogLevel::Log, + tag: tag.into(), + message: message.into(), + }); + } + pub fn warn(&self, tag: &str, message: &str) { + let _ = self.tx.send(LogMessage { + level: LogLevel::Warn, + tag: tag.into(), + message: message.into(), + }); + } + + pub fn error(&self, tag: &str, message: &str) { + let _ = self.tx.send(LogMessage { + level: LogLevel::Error, + tag: tag.into(), + message: message.into(), + }); + } +} + +/// set a panic hook that tries to log the panic to the JS side before continuing +/// a normal unwind. should work unless the panicking thread is the main thread. +fn set_panic_hook(console: &'static Console) { + let logger_thread_id = std::thread::current().id(); + let panic_console = console.clone(); + let old_panic_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |panic_info| { + let formatted_info = panic_info.to_string(); + let formatted_stack = std::backtrace::Backtrace::force_capture().to_string(); + if logger_thread_id == std::thread::current().id() { + // logger is (probably) running on the currently panicking thread, + // so we can't use it to log to JS. this at least shows up in stderr. + eprintln!("PANIC MAIN {}", formatted_info); + eprintln!("PANIC MAIN {}", formatted_stack); + } else { + panic_console.error( + "PANIC", format!( + "thread {} {}", + std::thread::current().name().unwrap_or_else(|| ""), + formatted_info + ).as_str()); + panic_console.error("PANIC", formatted_stack.as_str()); + } + old_panic_hook(panic_info) + })); +} \ No newline at end of file diff --git a/packages/node-mimimi/src/logging/logger.rs b/packages/node-mimimi/src/logging/logger.rs new file mode 100644 index 000000000000..2e62e2f455da --- /dev/null +++ b/packages/node-mimimi/src/logging/logger.rs @@ -0,0 +1,108 @@ +use napi::bindgen_prelude::*; +use napi::{Env, JsObject, JsUndefined}; + +/// The part of the logging setup that receives log messages from the rust log +/// {@link struct Console} and forwards them to the node environment to log. +pub struct Logger { + /// This is an option because we need to Option::take it from the old instance before + /// rescheduling the listen job with a new one. + rx: Option>, +} + +impl Logger { + pub fn new(rx: std::sync::mpsc::Receiver) -> Self { + Self { rx: Some(rx) } + } + fn execute_log(&self, env: Env, log_message: LogMessage) { + let globals = env.get_global() + .expect("no globals in env"); + let console: JsObject = globals.get_named_property("console") + .expect("console property not found"); + + let formatted_message = format!("[{} {}] {}", log_message.marker(), log_message.tag, log_message.message); + let js_string: napi::JsString = env.create_string_from_std(formatted_message) + .expect("could not create string"); + + let js_error: JsFunction = console.get_named_property(log_message.method()) + .expect("logging fn not found"); + js_error.call(None, &[js_string]) + .expect("logging failed"); + } +} + +impl Task for Logger { + type Output = LogMessage; + type JsValue = JsUndefined; + + /// runs on the libuv thread pool. + fn compute(&mut self) -> Result { + if let Some(rx) = &self.rx { + Ok(rx.recv().unwrap_or_else(|_| LogMessage { + level: LogLevel::Finish, + tag: "Logger".to_string(), + message: "channel closed, logger finished".to_string(), + })) + } else { + // should not happen - each Logger instance listens for exactly one message and then + // gets dropped and reincarnated. + Ok(LogMessage { + level: LogLevel::Error, + tag: "Logger".to_string(), + message: "rx not available, already moved".to_string(), + }) + } + } + + /// runs on the main thread and receives the output produced by compute + fn resolve(&mut self, env: Env, output: Self::Output) -> Result { + let level = output.level; + self.execute_log(env, output); + if level != LogLevel::Finish { + // we only have a &mut self, so can't revive ourselves directly. + // I guess this is reincarnation. + let rx = self.rx.take(); + let _promise = env.spawn(Logger { rx }); + } + Ok(env.get_undefined()?) + } +} + + +/// determines the urgency and some formatting of the log message +#[derive(Eq, PartialEq, Copy, Clone)] +pub enum LogLevel { + /// used if we want to log the fact that all consoles have been dropped (there will not be any more log messages) + Finish, + Log, + Warn, + Error, +} + +/// a struct containing all information necessary to print the +pub struct LogMessage { + pub level: LogLevel, + pub message: String, + pub tag: String, +} + +impl LogMessage { + /// get a prefix for labeling the log level in cases where it's + /// not obvious from terminal colors or similar + pub fn marker(&self) -> &str { + match self.level { + LogLevel::Finish | LogLevel::Log => "I", + LogLevel::Warn => "W", + LogLevel::Error => "E", + } + } + + /// the name of the logging method to use for each log level. + /// very js-specific. + pub fn method(&self) -> &str { + match self.level { + LogLevel::Finish | LogLevel::Log => "log", + LogLevel::Warn => "warn", + LogLevel::Error => "error", + } + } +} \ No newline at end of file diff --git a/packages/node-mimimi/src/net.rs b/packages/node-mimimi/src/net.rs new file mode 100644 index 000000000000..de1be3821493 --- /dev/null +++ b/packages/node-mimimi/src/net.rs @@ -0,0 +1,3 @@ +pub mod native_rest_client; +mod vec_body; +mod uri; \ No newline at end of file diff --git a/packages/node-mimimi/src/net/native_rest_client.rs b/packages/node-mimimi/src/net/native_rest_client.rs new file mode 100644 index 000000000000..c74a02a6373b --- /dev/null +++ b/packages/node-mimimi/src/net/native_rest_client.rs @@ -0,0 +1,99 @@ +use crate::net::vec_body::{VecBody, VecBuf}; +use http_body_util::{BodyExt, Full}; +use hyper::body::Incoming; +use hyper::http::HeaderValue; +use hyper::{HeaderMap, Request, Response}; +use hyper_rustls::HttpsConnector; +use hyper_util::client::legacy::connect::HttpConnector; +use hyper_util::client::legacy::Client; +use hyper_util::rt::TokioExecutor; +use std::collections::HashMap; +use tutasdk::rest_client::{HttpMethod, RestClient, RestClientError, RestClientOptions, RestResponse}; + +pub struct NativeRestClient { + client: Client, VecBody>, +} + +impl NativeRestClient { + pub fn try_new() -> Result { + + let _ = rustls::crypto::ring::default_provider().install_default(); + + let https_conn = hyper_rustls::HttpsConnectorBuilder::new() + .with_native_roots()? + .https_or_http() + .enable_all_versions() + .build(); + + let client = Client::builder(TokioExecutor::new()).build(https_conn); + + Ok(NativeRestClient { client }) + } +} + + +#[async_trait::async_trait] +impl RestClient for NativeRestClient { + async fn request_binary( + &self, + url: String, + method: HttpMethod, + options: RestClientOptions, + ) -> Result { + let RestClientOptions { headers, body } = options; + let uri = super::uri::Uri::try_from(url.as_str())?; + + let req = Request::builder() + .header(hyper::header::HOST, uri.authority()) + .uri(uri.inner()); + + let mut req = match method { + HttpMethod::GET => req.method("GET"), + HttpMethod::POST => req.method("POST"), + HttpMethod::PUT => req.method("PUT"), + HttpMethod::DELETE => req.method("DELETE"), + }; + + for (header_name, header_value) in headers { + req = req.header(header_name, header_value); + } + + let req: Request = match body { + Some(bytes) => req.body(VecBuf(bytes).into()), + None => req.body(Full::default()), + }.map_err(|_| RestClientError::InvalidRequest)?; + + let mut res = self.client.request(req).await + .map_err(|_| RestClientError::NetworkError)?; + + let response_body = read_body(&mut res).await?; + + Ok(RestResponse { + status: res.status().as_u16().into(), + headers: read_headers(res.headers())?, + body: if response_body.is_empty() { None } else { Some(response_body) }, + }) + } +} + +fn read_headers(header_map: &HeaderMap) -> Result, RestClientError> { + let mut headers = HashMap::new(); + for (name, values) in header_map.iter() { + let name_str = name.as_str(); + let value_str = values.to_str().map_err(|_| RestClientError::InvalidResponse)?; + headers.insert(name_str.to_string(), value_str.to_string()); + } + Ok(headers) +} + +async fn read_body(res: &mut Response) -> Result, RestClientError> { + let mut body: Vec = vec![]; + while let Some(next) = res.frame().await { + let frame = next.map_err(|_| RestClientError::InvalidResponse)?; + if let Some(chunk) = frame.data_ref() { + body.append(&mut chunk.to_vec()); + } + } + + Ok(body) +} \ No newline at end of file diff --git a/packages/node-mimimi/src/net/uri.rs b/packages/node-mimimi/src/net/uri.rs new file mode 100644 index 000000000000..c3ce7c50b018 --- /dev/null +++ b/packages/node-mimimi/src/net/uri.rs @@ -0,0 +1,61 @@ +use tutasdk::rest_client::RestClientError; + +/// wrapper around hyper::Uri that saves some unwrapping +pub struct Uri(hyper::Uri); + +impl Uri { + pub fn scheme(&self) -> &hyper::http::uri::Scheme { + self.0.scheme().unwrap() + } + + pub fn authority(&self) -> String { + self.0.authority().unwrap().to_string() + } + + pub fn inner(self) -> hyper::Uri { + self.0 + } +} +impl TryFrom<&str> for Uri { + type Error = RestClientError; + + fn try_from(value: &str) -> Result { + let uri = value.parse::() + .map_err(|_| RestClientError::InvalidURL(value.to_string()))?; + + // it looks like hyper::Uri doesn't accept Urls without an authority + // but with a scheme (like http:///path) so this check could be omitted. + // feels safer this way though. + let Some(_) = uri.authority() else { + return Err(RestClientError::InvalidURL(value.to_string())); + }; + + let (Some("http") | Some("https")) = uri.scheme_str() else { + return Err(RestClientError::InvalidURL(value.to_string())); + }; + + + Ok(Self(uri)) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn should_work() { + super::Uri::try_from("http://localhost:8080").unwrap(); + super::Uri::try_from("https://localhost:8080").unwrap(); + super::Uri::try_from("https://localhost.com").unwrap(); + super::Uri::try_from("https://127.0.0.1:123").unwrap(); + super::Uri::try_from("https://localhost.com/path?with=params").unwrap(); + } + + #[test] + fn should_fail() { + // wrong scheme + assert!(super::Uri::try_from("ftp://localhost:8080").is_err()); + // no authority + assert!(super::Uri::try_from("/relative").is_err()); + assert!(super::Uri::try_from("").is_err()); + } +} \ No newline at end of file diff --git a/packages/node-mimimi/src/net/vec_body.rs b/packages/node-mimimi/src/net/vec_body.rs new file mode 100644 index 000000000000..f4af2537378a --- /dev/null +++ b/packages/node-mimimi/src/net/vec_body.rs @@ -0,0 +1,51 @@ +use http_body_util::Full; +use hyper::body::Buf; +pub type VecBody = Full; +pub struct VecBuf(pub Vec); + +impl Into for VecBuf { + fn into(self) -> VecBody { + Full::new(self) + } +} + +/// somewhat stolen from impl Buf for &[u8] +/// also don't know why impl Buf for Vec doesn't come with hyper +impl Buf for VecBuf { + #[inline] + fn remaining(&self) -> usize { + self.0.len() + } + + #[inline] + fn chunk(&self) -> &[u8] { + self.0.as_slice() + } + + #[inline] + fn advance(&mut self, cnt: usize) { + if self.0.len() < cnt { + panic_advance(cnt, self.0.len()); + } + self.0.drain(..cnt); + } + + #[inline] + fn copy_to_slice(&mut self, dst: &mut [u8]) { + if self.0.len() < dst.len() { + panic_advance(dst.len(), self.0.len()); + } + + dst.copy_from_slice(&self.0[..dst.len()]); + self.advance(dst.len()); + } +} + +/// Panic with a nice error message. +#[cold] +fn panic_advance(idx: usize, len: usize) -> ! { + panic!( + "advance out of bounds: the len is {} but advancing by {}", + len, idx + ); +} diff --git a/packages/node-mimimi/src/tuta.rs b/packages/node-mimimi/src/tuta.rs new file mode 100644 index 000000000000..48c828e752cc --- /dev/null +++ b/packages/node-mimimi/src/tuta.rs @@ -0,0 +1 @@ +pub mod credentials; \ No newline at end of file diff --git a/packages/node-mimimi/src/tuta/credentials.rs b/packages/node-mimimi/src/tuta/credentials.rs new file mode 100644 index 000000000000..3bd4f1d5bced --- /dev/null +++ b/packages/node-mimimi/src/tuta/credentials.rs @@ -0,0 +1,47 @@ +use tutasdk::generated_id::GeneratedId; +use tutasdk::login::CredentialType; + + +#[napi(object)] +#[derive(Clone)] +/// Passed in from js-side, will be validated before being converted to proper tuta sdk credentials. +pub struct TutaCredentials { + pub sse_origin: String, + pub client_version: String, + pub login: String, + pub user_id: String, + pub access_token: String, + pub encrypted_passphrase_key: Vec, + pub credential_type: TutaCredentialType, +} + +impl TryInto for TutaCredentials { + // todo: proper errors + type Error = (); + + fn try_into(self) -> Result { + // todo: validate! + Ok(tutasdk::login::Credentials { + login: self.login, + user_id: GeneratedId(self.user_id), + access_token: self.access_token, + encrypted_passphrase_key: self.encrypted_passphrase_key.clone().to_vec(), + credential_type: self.credential_type.into(), + }) + } +} + +#[napi] +pub enum TutaCredentialType { + Internal, + External, +} + +impl Into for TutaCredentialType { + fn into(self) -> CredentialType { + match self { + TutaCredentialType::Internal => { CredentialType::Internal } + TutaCredentialType::External => { CredentialType::External } + } + } +} \ No newline at end of file diff --git a/packages/node-mimimi/test/Suite.ts b/packages/node-mimimi/test/Suite.ts new file mode 100644 index 000000000000..14c54784a079 --- /dev/null +++ b/packages/node-mimimi/test/Suite.ts @@ -0,0 +1,3 @@ +import { getIdByteSizePlus } from "../dist/binding.cjs" + +console.log("testing :", getIdByteSizePlus(123)) diff --git a/packages/node-mimimi/test/tsconfig.json b/packages/node-mimimi/test/tsconfig.json new file mode 100644 index 000000000000..e3f614151189 --- /dev/null +++ b/packages/node-mimimi/test/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../../tsconfig_common.json", + "include": ["../../../types/*.d.ts"], + "files": ["Suite.ts"], + "compilerOptions": { + "outDir": "../build", + "declaration": false, + "noImplicitAny": false + }, + "references": [ + { + "path": "../../tutanota-test-utils/tsconfig.json" + } + ] +} diff --git a/packages/node-mimimi/tsconfig.json b/packages/node-mimimi/tsconfig.json new file mode 100644 index 000000000000..18c2b4c7c645 --- /dev/null +++ b/packages/node-mimimi/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "composite": true + } +} diff --git a/packages/tuta-imap/.gitignore b/packages/tuta-imap/.gitignore new file mode 100644 index 000000000000..658e291372c2 --- /dev/null +++ b/packages/tuta-imap/.gitignore @@ -0,0 +1,6 @@ +/target +Cargo.lock + +/java/.idea +/java/build +.gradle diff --git a/packages/tuta-imap/Cargo.toml b/packages/tuta-imap/Cargo.toml new file mode 100644 index 000000000000..145be30dce26 --- /dev/null +++ b/packages/tuta-imap/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "tuta-imap" +version = "0.1.0" +edition = "2021" + +[dependencies.imap-codec] +git = "https://github.com/duesee/imap-codec.git" +rev = "16a5c182285c7a895b06276f68a116caf2cb294f" +features = ["serde", "starttls", "ext_id", "ext_metadata"] + +[dependencies] +serde = { version = "1.0.210", features = ["derive"] } +log = { version = "0.4.22" } +rand = { version = "0.8.1" } +rustls = { version = "0.23.13", features = ["std"] } + +# see todo in `mod testing` in lib.rs +# [dev-dependencies] +j4rs = { version = "0.20.0" } +lazy_static = { version = "0.2.11" } \ No newline at end of file diff --git a/packages/tuta-imap/build.rs b/packages/tuta-imap/build.rs new file mode 100644 index 000000000000..b456aa59c157 --- /dev/null +++ b/packages/tuta-imap/build.rs @@ -0,0 +1,29 @@ +use std::process::Command; + +const GREENMAIL_TEST_SERVER_JAR: &str = concat!( +env!("CARGO_MANIFEST_DIR"), +"/java/build/libs/greenmail-test-server.jar" +); +const BUILD_WATCHLIST: &[&str] = &["/java/src/", "/java/build/libs/greenmail-test-server.jar"]; + +pub fn main() { + println!("cargo::rustc-env=GREENMAIL_TEST_SERVER_JAR={GREENMAIL_TEST_SERVER_JAR}", ); + for watch in BUILD_WATCHLIST { + println!("cargo::rerun-if-changed={watch}"); + } + + run_gradle_jar(); +} + +fn run_gradle_jar() { + Command::new("/opt/gradle-8.5/bin/gradle") + .current_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/java")) + .args(["jar"]) + .spawn() + .expect("Cannot spawn gradle command") + .wait() + .expect("Cannot wait for gradle command") + .success() + .then_some(()) + .expect("gradle exited with non-success status code"); +} diff --git a/packages/tuta-imap/java/build.gradle.kts b/packages/tuta-imap/java/build.gradle.kts new file mode 100644 index 000000000000..31ca8eb6be03 --- /dev/null +++ b/packages/tuta-imap/java/build.gradle.kts @@ -0,0 +1,24 @@ +repositories { + mavenLocal() + maven { + credentials(PasswordCredentials::class) + url = uri("https://next.tutao.de/nexus/content/groups/public/") + } +} + +plugins { + java +} + +dependencies { + implementation("com.icegreen:greenmail-standalone:2.0.1") +} + +tasks.jar { + val dependencies = configurations + .runtimeClasspath + .get() + .map(::zipTree) // OR .map { zipTree(it) } + from(dependencies) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} \ No newline at end of file diff --git a/packages/tuta-imap/java/settings.gradle.kts b/packages/tuta-imap/java/settings.gradle.kts new file mode 100644 index 000000000000..a9467e9f9833 --- /dev/null +++ b/packages/tuta-imap/java/settings.gradle.kts @@ -0,0 +1,24 @@ +rootProject.name = "greenmail-test-server" + +pluginManagement { + repositories { + mavenLocal() + maven { + url = uri("https://next.tutao.de/nexus/content/groups/public/") + credentials(PasswordCredentials::class) + } + } +} + +buildscript { + repositories { + mavenLocal() + maven { + credentials(PasswordCredentials::class) + url = uri("https://next.tutao.de/nexus/content/groups/public/") + } + } + dependencies { + classpath(group = "de.tutao.gradle", name = "devDefaults", version = "3.6.2") + } +} \ No newline at end of file diff --git a/packages/tuta-imap/java/src/main/java/greenmailserver/GreenMailServer.java b/packages/tuta-imap/java/src/main/java/greenmailserver/GreenMailServer.java new file mode 100644 index 000000000000..25f7e93d62c5 --- /dev/null +++ b/packages/tuta-imap/java/src/main/java/greenmailserver/GreenMailServer.java @@ -0,0 +1,63 @@ +package greenmailserver; + +import com.icegreen.greenmail.user.GreenMailUser; +import com.icegreen.greenmail.user.UserException; +import com.icegreen.greenmail.util.GreenMail; +import com.icegreen.greenmail.util.ServerSetup; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; + +import java.io.ByteArrayInputStream; + + +public class GreenMailServer { + public static final String imapsHost = "127.0.0.1"; + + private GreenMail greenMail; + + public GreenMailUser userMap; + public GreenMailUser userSug; + + public GreenMailServer(Integer p) { + setSystemClassLoaderForCurrentThreadContext(); + + ServerSetup defaultImapsProps = new ServerSetup(p, imapsHost, "imaps"); + try { + greenMail = new GreenMail(defaultImapsProps); + } catch (Exception e) { + e.printStackTrace(); + } + + greenMail.start(); + + try { + userMap = greenMail.getUserManager().createUser("map@example.org", "map@example.org", "map"); + userSug = greenMail.getUserManager().createUser("sug@example.org", "sug@example.org", "sug"); + } catch (UserException e) { + throw new RuntimeException(e); + } + } + + public void stop() { + greenMail.stop(); + } + + public void store_mail(String recipientAddress, String mimeMsg) throws MessagingException { + var recipient = greenMail.getUserManager().getUserByEmail(recipientAddress); + var mimeMessage = new MimeMessage(null, new ByteArrayInputStream(mimeMsg.getBytes())); + recipient.deliver(mimeMessage); + } + + + // ========= configuration required for (rust) jni interface ================== + + // For the class loaded by jni, .getCurrentThread().getContextClassLoader() will be null + // set it to systemClassLoader + public static void setSystemClassLoaderForCurrentThreadContext() { + if (Thread.currentThread().getContextClassLoader() == null) { + ClassLoader cl = ClassLoader.getSystemClassLoader(); + Thread.currentThread().setContextClassLoader(cl); + } + } + +} diff --git a/packages/tuta-imap/package.json b/packages/tuta-imap/package.json new file mode 100644 index 000000000000..7d9882cb2d78 --- /dev/null +++ b/packages/tuta-imap/package.json @@ -0,0 +1,15 @@ +{ + "name": "tuta-imap", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo test done", + "build": "echo done" + }, + "repository": { + "type": "git", + "url": "https://github.com/tutao/tutanota.git" + }, + "private": true +} diff --git a/packages/tuta-imap/rust-toolchain b/packages/tuta-imap/rust-toolchain new file mode 100644 index 000000000000..870bbe4e50e6 --- /dev/null +++ b/packages/tuta-imap/rust-toolchain @@ -0,0 +1 @@ +stable \ No newline at end of file diff --git a/packages/tuta-imap/src/client/mod.rs b/packages/tuta-imap/src/client/mod.rs new file mode 100644 index 000000000000..f99252bb87f1 --- /dev/null +++ b/packages/tuta-imap/src/client/mod.rs @@ -0,0 +1,511 @@ +use crate::client::tls_stream::TlsStream; +use crate::client::types::mail::ImapMail; +use imap_codec::decode::{Decoder, ResponseDecodeError}; +use imap_codec::encode::Encoder; +use imap_codec::imap_types::core::Tag; +use imap_codec::imap_types::mailbox::Mailbox; +use imap_codec::imap_types::response::{ + CommandContinuationRequest, Data, Response, Status, StatusBody, StatusKind, +}; +use imap_codec::imap_types::secret::Secret; +use imap_codec::imap_types::{response, ToStatic}; +use imap_codec::{imap_types, CommandCodec, ResponseCodec}; +use imap_types::state::State as ConnectionState; +use std::collections::HashMap; +use std::num::NonZeroU32; + +// todo: +// what is the intention for this commit: +// https://github.com/duesee/imap-codec/commit/998fa15456fb0b2006c88ba5d523b5e2e115ae86 +pub mod tls_stream; +pub mod types; + +// todo: make a PR for this type alias? +pub type CapabilitiesList<'a> = imap_types::core::Vec1>; + +/// Always return a pre-formatted/sanitised error to client and not the execution details +pub type ApiError = String; +pub type ApiResult = Result; + +pub struct TutanotaImapClient { + pub capabilities: Option>, + pub latest_search_results: Vec, + pub latest_mails: HashMap, + pub unreceived_status: HashMap, StatusBody<'static>>, + + connection_state: ConnectionState<'static>, + + command_codec: CommandCodec, + response_codec: ResponseCodec, + tls_stream: TlsStream, +} + +/// Implement the exposed api +impl TutanotaImapClient { + /// Construct a new client + /// as well as: + /// - listen ( &discard ) the greetings & SSL messages + /// - refresh capabilities once + /// - perform login + pub fn start_new_session(imaps_port: i32) -> Self { + let tls_stream = TlsStream::new("127.0.0.1", imaps_port as u16); + + let mut client = Self { + tls_stream, + capabilities: None, + latest_mails: HashMap::new(), + latest_search_results: Vec::new(), + command_codec: CommandCodec::new(), + response_codec: ResponseCodec::new(), + unreceived_status: HashMap::new(), + connection_state: ConnectionState::NotAuthenticated, + }; + + // start the tls handshake process + client.start_tls().unwrap(); + + // discard any tls & greetings messages + client.read_until_next_untagged_status().unwrap(); + client.connection_state = ConnectionState::Greeting; + + // refresh the capabilities + client.refresh_capabilities(); + + // return the updated client + client + } + + /// try to refresh the capability from server by executing CAPABILITIES command + pub fn refresh_capabilities(&mut self) -> response::StatusKind { + let capability_command = imap_types::command::Command { + tag: self.create_tag(), + body: imap_types::command::CommandBody::Capability, + }; + let capability_response = self + .execute_command_directly(capability_command) + .unwrap() + .unwrap(); + + capability_response.kind + } + + pub fn login(&mut self, username: &str, password: &str) -> response::StatusKind { + let login_command = imap_types::command::Command { + tag: self.create_tag(), + body: imap_types::command::CommandBody::Login { + username: username.try_into().unwrap(), + password: Secret::new(password.try_into().unwrap()), + }, + }; + let login_response = self + .execute_command_directly(login_command) + .unwrap() + .unwrap(); + let status_kind = login_response.kind.clone(); + + if status_kind == response::StatusKind::Ok { + self.connection_state = ConnectionState::Authenticated; + } + + status_kind + } + + /// List all the mailbox available + pub fn list_mailbox(&mut self) {} + + /// Select a mailbox + /// + /// Caller should already invoke `list_mailbox` function before calling this + pub fn select_mailbox(&mut self, mailbox: Mailbox) -> StatusKind { + assert_eq!( + ConnectionState::Authenticated, + self.connection_state, + "must be in authenticated state to select mailbox" + ); + let select_command = imap_types::command::Command { + tag: self.create_tag(), + body: imap_types::command::CommandBody::Select { mailbox }, + }; + + let select_response = self + .execute_command_directly(select_command) + .unwrap() + .unwrap(); + let status_kind = select_response.kind; + if status_kind == response::StatusKind::Ok { + self.connection_state = ConnectionState::Selected(Mailbox::Inbox) + } + status_kind + } + + /// perform a UID search command + pub fn search_all_uid(&mut self) -> StatusKind { + assert_eq!( + ConnectionState::Selected(Mailbox::Inbox), + self.connection_state, + "must be in selected state to search mailbox UIDs" + ); + let search_all_command = imap_types::command::Command { + tag: self.create_tag(), + body: imap_types::command::CommandBody::Search { + charset: None, + uid: true, + criteria: [imap_types::search::SearchKey::All].into(), + }, + }; + + let search_all_command = self + .execute_command_directly(search_all_command) + .unwrap() + .unwrap(); + let status_kind = search_all_command.kind; + status_kind + } + + /// fetch mail with given uid + // todo: + /// & given uidValidity + pub fn fetch_mail_by_uid(&mut self, uid: NonZeroU32) -> StatusKind { + assert_eq!( + ConnectionState::Selected(Mailbox::Inbox), + self.connection_state, + "must be in selected state to fetch mailbox UID" + ); + let fetch_command = imap_types::command::Command { + tag: self.create_tag(), + body: imap_types::command::CommandBody::Fetch { + uid: true, + sequence_set: imap_types::sequence::Sequence::Single(uid.into()).into(), + macro_or_item_names: imap_types::fetch::Macro::All.into(), + }, + }; + + let search_all_command = self + .execute_command_directly(fetch_command) + .unwrap() + .unwrap(); + let status_kind = search_all_command.kind; + status_kind + } +} + +/// Implement direct helper function divisions + +impl TutanotaImapClient { + fn create_tag(&mut self) -> Tag<'static> { + Tag::try_from("tag").unwrap() + } + + fn start_tls(&mut self) -> Result<(), ()> { + Ok(()) + } + + fn read_until_next_untagged_status(&mut self) -> Result { + loop { + let mut response_bytes = self.tls_stream.read_until_crlf().unwrap(); + + let response = self + .parse_response(&mut response_bytes) + .unwrap() + .to_static(); + + match &response { + Response::Status(Status::Untagged(status_body)) + | Response::Status(Status::Tagged(response::Tagged { + tag: _, + body: status_body, + })) => return Ok(status_body.to_owned()), + + Response::Status(_) + | Response::Data(_) + | Response::CommandContinuationRequest(_) => todo!(), + } + } + } + + // Use any untagged data response to update the state + fn process_data_response(&mut self, data_response: Data) { + match data_response { + Data::Capability(list) => self.capabilities = Some(list.to_static()), + Data::Search(list) => self.latest_search_results = list.to_static(), + Data::Fetch { seq, items } => { + self.latest_mails.insert(seq, ImapMail::new(items)); + } + + anything_else => { + log::warn!("Do not know yet how to handle: {anything_else:?}") + } + } + } + + // command continuation request + fn process_cmd_continutation_response( + &self, + cmd_continutation_response: CommandContinuationRequest, + ) -> Result<(), ()> { + Ok(()) + } + + /// Process any response parsed. + fn process_response(&mut self, response: Response) { + match response { + Response::Data(untagged_data) => { + self.process_data_response(untagged_data); + } + Response::Status(status) => match status { + Status::Untagged(untagged_status) => { + log::warn!("Received untagged status: {:?}", untagged_status); + } + Status::Tagged(response::Tagged { tag, body }) => { + self.unreceived_status + .insert(tag.to_static(), body.to_static()); + } + Status::Bye(response_bye) => { + log::warn!("Received bye from server. byeeeee."); + self.connection_state = ConnectionState::Logout; + } + }, + Response::CommandContinuationRequest(cmd_continuation) => { + self.process_cmd_continutation_response(cmd_continuation) + .unwrap(); + } + } + } + + /// returns if response bytes is incomplete + fn parse_response(&mut self, response_bytes: &mut Vec) -> Option { + if response_bytes.is_empty() { + None?; + } + + let response = self.response_codec.decode(response_bytes.as_ref()); + match response { + Ok((_left_over, response)) => { + log::info!("Got response to be: `{:?}`", response); + Some(response.to_static()) + } + + // if this is incomplete, + // save this might have to re-read again once we get remaining of response + Err(ResponseDecodeError::Incomplete) => { + log::warn!( + "Got an incomplete response from server. Saving it: `{}`", + String::from_utf8(response_bytes.to_vec()).unwrap() + ); + None + } + Err(ResponseDecodeError::Failed) => { + log::error!( + "Found a response: {}. But failed to decode. Ignoring...", + String::from_utf8(response_bytes.to_vec()).unwrap() + ); + None + } + Err(ResponseDecodeError::LiteralFound { length }) => { + log::warn!( + "Literal found for response: {}", + String::from_utf8(response_bytes.to_vec()).unwrap() + ); + + // read everything remaining + let mut remaining_literal = Vec::with_capacity(length as usize); + self.tls_stream.read_exact(&mut remaining_literal).unwrap(); + response_bytes.append(&mut remaining_literal); + + // try again + self.parse_response(response_bytes) + } + } + } + + // execute a command in imap + // + // this api is allowed to cache or delay the execution given command: + // example: some other non-overridden-able command is in progress + // example: LOGIN command is in progress. it's better to wait for such command to finish + // so that we can execute following command in correct state context + // + // this api is allowed to block the execution for so reason. If the waiting is not desired, + // todo: + // call another async function which will received the command and put it to queue, + // once the command is executed and the response with tag of this command is received, + // the response ( only the tagged one ) will be passed to the receiving channel + + fn execute_command_directly( + &mut self, + command: imap_types::command::Command, + ) -> Result, ()> { + // only check for logout state, + // calling function should make sure to check for other state + // if that action expects client to be in certain state + assert_ne!( + ConnectionState::Logout, + self.connection_state, + "Cannot execute command after being logged out" + ); + log::info!("Start Executing command: {command:?}"); + + // write the command + let encoded_command = self.command_codec.encode(&command); + self.tls_stream + .write_imap_command(encoded_command.dump().as_slice()) + .unwrap(); + + log::info!("Command written..."); + + loop { + // we assume we get at least one line of response with every command + // otherwise we will wait here forever, + // unless we get any other response ( which is still not ok because we check for this tag later on) + let mut next_line = self.tls_stream.read_until_crlf().unwrap(); + if next_line.is_empty() { + return Ok(None); + } + + let maybe_cmd_status = self.parse_response(&mut next_line).map(|r| r.to_static()); + match maybe_cmd_status { + // if it's the tagged response + // with the same tag as of command + Some(Response::Status(Status::Tagged(response::Tagged { tag, body }))) + if &tag == &command.tag => + { + return Ok(Some(body.to_owned())) + } + + // if response is something else, + // process the command and loop back + Some(response) => self.process_response(response), + + // response was literalFound? Incomplete? + None => { + log::warn!("Cannot get the tagged response from server for.. What to do now?") + } + } + } + } +} + +/// Credentials mechanism to use for authenticating the client +/// +/// LOGIN command will be available in all imap server, +/// but this is the least secure way to authenticate. and simplest. +/// +/// According to server capabilities, we can choose to perform login via any +/// SASL* (RFC 4422) authentication mechanism. +/// +/// Example: +/// Gmail IMAP server support OAUTH2, +/// and provides a custom `Authenticate` Command to do so. +/// this will require CommandContinuationRequests and hence is less +/// simple than LOGIN but this mechanism will be more "secured" +/// +/// todo: +/// For now only care for PLAIN mechanism. +pub enum CredentialsMechanism { + Plain, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::client::{CapabilitiesList, TutanotaImapClient}; + use crate::testing::utils::toNonZeroU32; + use crate::testing::GreenMailTestServer; + use imap_codec::imap_types::response::{Capability, StatusKind}; + + #[test] + fn can_refresh_capabilities() { + let greenmail = GreenMailTestServer::new(); + let mut import_client = TutanotaImapClient::start_new_session(greenmail.imaps_port); + + // refreshing multiple times should still result in same + for _ in 0..3 { + assert_eq!(StatusKind::Ok, import_client.refresh_capabilities()); + assert_eq!( + Some(vec![ + Capability::Imap4Rev1, + Capability::LiteralPlus, + Capability::UidPlus, + Capability::Sort(None), + Capability::Idle, + Capability::Move, + Capability::Quota, + ]), + import_client + .capabilities + .clone() + .map(CapabilitiesList::into_inner) + ); + } + } + + #[test] + fn can_login() { + let greenmail = GreenMailTestServer::new(); + let mut import_client = TutanotaImapClient::start_new_session(greenmail.imaps_port); + + // refreshing multiple times should still result in same + assert_eq!( + StatusKind::Ok, + import_client.login("sug@example.org", "sug") + ); + assert_eq!( + import_client.connection_state, + ConnectionState::Authenticated + ); + } + + #[test] + fn select_inbox() { + let greenmail = GreenMailTestServer::new(); + let mut import_client = TutanotaImapClient::start_new_session(greenmail.imaps_port); + + import_client.login("sug@example.org", "sug"); + + // refreshing multiple times should still result in same + assert_eq!(StatusKind::Ok, import_client.select_mailbox(Mailbox::Inbox)); + assert_eq!( + import_client.connection_state, + ConnectionState::Selected(Mailbox::Inbox) + ); + } + + #[test] + fn search_all_mail() { + let greenmail = GreenMailTestServer::new(); + let mut import_client = TutanotaImapClient::start_new_session(greenmail.imaps_port); + + // should find these two `sug` mails + greenmail.store_mail("sug@example.org", ""); + greenmail.store_mail("sug@example.org", ""); + // should not find this `map` mail + greenmail.store_mail("map@example.org", ""); + + import_client.login("sug@example.org", "sug"); + import_client.select_mailbox(Mailbox::Inbox); + assert_eq!(StatusKind::Ok, import_client.search_all_uid()); + assert_eq!(toNonZeroU32(&[1, 2]), import_client.latest_search_results); + } + + #[test] + fn fetch_mail() { + let greenmail = GreenMailTestServer::new(); + let mut import_client = TutanotaImapClient::start_new_session(greenmail.imaps_port); + + greenmail.store_mail("map@example.org", "Subject: =?UTF-8?B?bWEgdXRmLTgg4oKs?="); + greenmail.store_mail("map@example.org", "Subject: Find me if you can"); + + import_client.login("map@example.org", "map"); + import_client.select_mailbox(Mailbox::Inbox); + import_client.search_all_uid(); + + let message_id = NonZeroU32::new(1).unwrap(); + assert_eq!(StatusKind::Ok, import_client.fetch_mail_by_uid(message_id)); + assert_eq!( + &ImapMail { + subject: "=?UTF-8?B?bWEgdXRmLTgg4oKs?=".to_string() + }, + import_client.latest_mails.get(&message_id).unwrap(), + ); + } +} diff --git a/packages/tuta-imap/src/client/tls_stream.rs b/packages/tuta-imap/src/client/tls_stream.rs new file mode 100644 index 000000000000..e980cf22138a --- /dev/null +++ b/packages/tuta-imap/src/client/tls_stream.rs @@ -0,0 +1,111 @@ +use crate::utils::BufReadExtension; +use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; +use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; +use rustls::{ClientConfig, ClientConnection, DigitallySignedStruct, Error, SignatureScheme}; +use std::io::{BufReader, Read, Write}; +use std::net::{SocketAddr, SocketAddrV4, TcpStream}; +use std::str::FromStr; +use std::sync::Arc; +use std::time::Duration; + +pub type SecuredStream = rustls::StreamOwned; + +pub struct TlsStream { + buffer_controller: BufReader, +} + +impl TlsStream { + pub fn new(address: &str, port: u16) -> Self { + let tcp_address = SocketAddr::V4(SocketAddrV4::new( + std::net::Ipv4Addr::from_str(address).unwrap(), + port, + )); + + let dangerous_config = ClientConfig::builder() + .dangerous() + .with_custom_certificate_verifier(Arc::new(MockSsl)) + .with_no_client_auth(); + + let tcp_stream = TcpStream::connect_timeout(&tcp_address, Duration::from_secs(10)).unwrap(); + let client_connection = rustls::ClientConnection::new( + Arc::new(dangerous_config), + "localhost".try_into().unwrap(), + ) + .unwrap(); + + let buffer_controller = BufReader::new(SecuredStream::new(client_connection, tcp_stream)); + + TlsStream { buffer_controller } + } + + pub fn write_imap_command(&mut self, encoded_command: &[u8]) -> std::io::Result { + let writer = self.buffer_controller.get_mut(); + let written = writer.write(encoded_command)?; + writer.flush()?; + Ok(written) + } + + pub fn read_until_crlf(&mut self) -> std::io::Result> { + let mut line_until_crlf = Vec::new(); + self.buffer_controller + .read_until_slice(b"\r\n", &mut line_until_crlf)?; + + Ok(line_until_crlf) + } + + pub fn read_exact(&mut self, target: &mut Vec) -> std::io::Result<()> { + self.buffer_controller.read_exact(target) + } +} + +#[derive(Debug)] +pub struct MockSsl; + +impl ServerCertVerifier for MockSsl { + fn verify_server_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + server_name: &ServerName<'_>, + ocsp_response: &[u8], + now: UnixTime, + ) -> Result { + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![ + SignatureScheme::RSA_PKCS1_SHA1, + SignatureScheme::ECDSA_SHA1_Legacy, + SignatureScheme::RSA_PKCS1_SHA256, + SignatureScheme::ECDSA_NISTP256_SHA256, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::ECDSA_NISTP384_SHA384, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::ECDSA_NISTP521_SHA512, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA512, + SignatureScheme::ED25519, + SignatureScheme::ED448, + ] + } +} diff --git a/packages/tuta-imap/src/client/types/mail.rs b/packages/tuta-imap/src/client/types/mail.rs new file mode 100644 index 000000000000..23f8fd177074 --- /dev/null +++ b/packages/tuta-imap/src/client/types/mail.rs @@ -0,0 +1,28 @@ +use imap_codec::imap_types::core::Vec1; +use imap_codec::imap_types::fetch::MessageDataItem; + +#[derive(Eq, PartialEq, Debug)] +pub struct ImapMail { + pub subject: String, +} + +impl ImapMail { + pub fn new(items: Vec1) -> Self { + let mut imap_mail = ImapMail { + subject: String::new(), + }; + + for item in items { + match item { + MessageDataItem::Envelope(envelope) => { + imap_mail.subject = + String::from_utf8(envelope.subject.0.unwrap().into_inner().to_vec()) + .unwrap() + } + + _ => (), + } + } + imap_mail + } +} diff --git a/packages/tuta-imap/src/client/types/mod.rs b/packages/tuta-imap/src/client/types/mod.rs new file mode 100644 index 000000000000..996a5d04085f --- /dev/null +++ b/packages/tuta-imap/src/client/types/mod.rs @@ -0,0 +1,6 @@ +pub mod mail; + +pub mod reexports { + pub use imap_codec::imap_types::mailbox::Mailbox; + pub use imap_codec::imap_types::response::StatusKind; +} diff --git a/packages/tuta-imap/src/lib.rs b/packages/tuta-imap/src/lib.rs new file mode 100644 index 000000000000..2c8f944b89b2 --- /dev/null +++ b/packages/tuta-imap/src/lib.rs @@ -0,0 +1,9 @@ +// todo: +// somehow have to guard against another feature flag ( #[cfg(test)] ) will not be forwarded to +// dependency hence, currently cannot use this module with #[cfg(test)] in node-mimimi test +// #[cfg(test)] +pub mod testing; + +pub mod client; + +pub mod utils; diff --git a/packages/tuta-imap/src/testing/jvm_singeleton.rs b/packages/tuta-imap/src/testing/jvm_singeleton.rs new file mode 100644 index 000000000000..bed996435137 --- /dev/null +++ b/packages/tuta-imap/src/testing/jvm_singeleton.rs @@ -0,0 +1,21 @@ +use crate::testing::GREENMAIL_TEST_SERVER_JAR; +use j4rs::{ClasspathEntry, JvmBuilder}; + +static mut START_JVM_INVOCATION_COUNTER: i32 = 0; + +pub fn start_or_attach_to_jvm() -> i32 { + /// todo: SAFETY??? + unsafe { + if START_JVM_INVOCATION_COUNTER == 0 { + // create exactly one jvm and attach to it whenever we create a new IMAP test server + + JvmBuilder::new() + .classpath_entry(ClasspathEntry::new(GREENMAIL_TEST_SERVER_JAR)) + .with_default_classloader() + .build() + .expect("Cannot start jvm"); + } + START_JVM_INVOCATION_COUNTER += 1; + START_JVM_INVOCATION_COUNTER + } +} diff --git a/packages/tuta-imap/src/testing/mod.rs b/packages/tuta-imap/src/testing/mod.rs new file mode 100644 index 000000000000..51a0cf57b6c7 --- /dev/null +++ b/packages/tuta-imap/src/testing/mod.rs @@ -0,0 +1,161 @@ +use j4rs::{Instance, InvocationArg, Jvm}; +use std::collections::HashMap; + +pub mod jvm_singeleton; +pub mod utils; + +pub const GREENMAIL_TEST_SERVER_JAR: &str = env!("GREENMAIL_TEST_SERVER_JAR"); +pub const IMAPS_STARTING_PORT: i32 = 3993; + +pub struct GreenMailTestServer { + pub jvm: Jvm, + pub server: Instance, + + pub imaps_address: (String, u32), + + pub users: HashMap<&'static str, Instance>, + pub imaps_port: i32, +} + +impl GreenMailTestServer { + pub fn new() -> Self { + let this_jvm_id = jvm_singeleton::start_or_attach_to_jvm(); + let imaps_port = this_jvm_id + IMAPS_STARTING_PORT; + let jvm = Jvm::attach_thread().unwrap(); + + let imaps_host = jvm + .static_class_field("greenmailserver.GreenMailServer", "imapsHost") + .map(|v| jvm.to_rust(v)) + .unwrap() + .unwrap(); + let imaps_address = (imaps_host, imaps_port as u32); + + let server = jvm + .create_instance( + "greenmailserver.GreenMailServer", + &[InvocationArg::try_from(imaps_port).unwrap()], + ) + .unwrap(); + + let mut users = HashMap::new(); + users.insert("map", jvm.field(&server, "userMap").unwrap()); + users.insert("sug", jvm.field(&server, "userSug").unwrap()); + + Self { + users, + jvm, + server, + imaps_address, + imaps_port, + } + } + + pub fn stop(self) { + self.stop_greenmail_server(); + } + + fn stop_greenmail_server(&self) { + self.jvm + .invoke(&self.server, "stop", InvocationArg::empty()) + .unwrap(); + } + + pub fn store_mail(&self, receiver: &str, mime_message: &str) { + self.jvm + .invoke( + &self.server, + "store_mail", + &[ + &InvocationArg::try_from(receiver).unwrap(), + &mime_message.try_into().unwrap(), + ], + ) + .unwrap(); + } +} + +impl Drop for GreenMailTestServer { + fn drop(&mut self) { + self.stop_greenmail_server() + } +} + +#[cfg(test)] +pub mod greenmail_interaction { + use super::*; + use std::process::Command; + + #[test] + pub fn ensure_imap_server_running() { + let test_server = GreenMailTestServer::new(); + let (imaps_host, imaps_port) = &test_server.imaps_address; + + let output = Command::new("curl") + .args(&[ + format!("imaps://{imaps_host}:{imaps_port}").as_str(), + "--request", + "CAPABILITY", + "-k", + ]) + .output() + .unwrap(); + + assert!(output.status.success()); + assert_eq!( + b"* CAPABILITY IMAP4rev1 LITERAL+ UIDPLUS SORT IDLE MOVE QUOTA\r\n", + output.stdout.as_slice() + ); + } + + #[test] + pub fn ensure_can_store_mail() { + let test_server = GreenMailTestServer::new(); + let (imaps_host, imaps_port) = &test_server.imaps_address; + + test_server.store_mail( + "sug@example.org", + r#"From: Some One +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="XXXXboundary text" + +This is a multipart message in MIME format. + +--XXXXboundary text +Content-Type: text/plain + +this is the body text + +--XXXXboundary text +Content-Type: text/plain; +Content-Disposition: attachment; + filename="test.txt" + +this is the attachment text + +--XXXXboundary text--"#, + ); + + let output = Command::new("curl") + .args(&[ + format!("imaps://{imaps_host}:{imaps_port}/INBOX").as_str(), + "--request", + "LIST \"\" *", + "--user", + "sug@example.org:sug", + "--request", + "FETCH 1 BODY[HEADER]", + "-k", + ]) + .output() + .unwrap(); + + assert!(output.status.success()); + assert_eq!( + b"* 1 FETCH (FLAGS (\\Seen) BODY[HEADER] {127}\r\n", + output.stdout.as_slice(), + "{}", + String::from_utf8(output.stdout.to_vec()).unwrap() + ); + } +} diff --git a/packages/tuta-imap/src/testing/utils.rs b/packages/tuta-imap/src/testing/utils.rs new file mode 100644 index 000000000000..cde623ffe7b5 --- /dev/null +++ b/packages/tuta-imap/src/testing/utils.rs @@ -0,0 +1,5 @@ +use std::num::NonZeroU32; + +pub fn toNonZeroU32(slice: &[u32]) -> Vec { + slice.iter().map(|s| NonZeroU32::new(*s).unwrap()).collect() +} diff --git a/packages/tuta-imap/src/utils/mod.rs b/packages/tuta-imap/src/utils/mod.rs new file mode 100644 index 000000000000..65011c851b43 --- /dev/null +++ b/packages/tuta-imap/src/utils/mod.rs @@ -0,0 +1,28 @@ +use std::io::BufRead; + +pub trait BufReadExtension { + /// Same as `std::io::BufRead::read_until` + /// instead of accepting a single byte, accept a slice + fn read_until_slice(&mut self, delimiter: &[u8], buf: &mut Vec) -> std::io::Result; +} + +// implement for everything that have BufRead +impl BufReadExtension for T +where + T: BufRead, +{ + fn read_until_slice(&mut self, delimeter: &[u8], buf: &mut Vec) -> std::io::Result { + let mut read_count = 0; + + loop { + let mut one_byte = [0]; + self.read_exact(&mut one_byte)?; + buf.push(one_byte[0]); + + read_count += 1; + if buf.ends_with(delimeter) { + break Ok(read_count); + } + } + } +} diff --git a/src/common/api/worker/rest/DefaultEntityRestCache.ts b/src/common/api/worker/rest/DefaultEntityRestCache.ts index d43dd5e28bb1..be838da17e3b 100644 --- a/src/common/api/worker/rest/DefaultEntityRestCache.ts +++ b/src/common/api/worker/rest/DefaultEntityRestCache.ts @@ -882,7 +882,14 @@ export class DefaultEntityRestCache implements EntityRestCache { if (oldFolder != null && oldFolder.isMailSet) return const updatedFolder = await this.entityRestClient.load(MailFolderTypeRef, [update.instanceListId, update.instanceId]) if (!updatedFolder.isMailSet) return + let mailsInOffline = await this.storage.getIdsInRange(MailTypeRef, updatedFolder.mails) await this.storage.deleteWholeList(MailTypeRef, updatedFolder.mails) + + await this.storage.lockRangesDbAccess(updatedFolder.mails) + console.log("Loading mails: ", mailsInOffline) + await this.entityRestClient.loadMultiple(MailTypeRef, updatedFolder.mails, mailsInOffline) + await this.storage.unlockRangesDbAccess(updatedFolder.mails) + await this.storage.put(updatedFolder) } } diff --git a/src/common/desktop/DesktopMain.ts b/src/common/desktop/DesktopMain.ts index 916007217ebb..77f957b34976 100644 --- a/src/common/desktop/DesktopMain.ts +++ b/src/common/desktop/DesktopMain.ts @@ -71,6 +71,10 @@ import { DelayedImpls, exposeLocalDelayed } from "../api/common/WorkerProxy.js" import { DefaultDateProvider } from "../calendar/date/CalendarUtils.js" import { AlarmScheduler } from "../calendar/date/AlarmScheduler.js" import { DesktopExternalCalendarFacade } from "./ipc/DesktopExternalCalendarFacade.js" +import { ImapCredentials, ImportCredentials, TutaCredentials, TutaCredentialType } from "@tutao/node-mimimi" +import { DesktopImapImportSystemFacade } from "./imapimport/DesktopImapImportSystemFacade.js" + +mp() /** * Should be injected during build time. @@ -86,7 +90,6 @@ setupAssetProtocol(electron) const TAG = "[DesktopMain]" -mp() type Components = { readonly wm: WindowManager readonly tfs: TempFs @@ -270,6 +273,7 @@ async function createComponents(): Promise { new DesktopExportFacade(tfs, conf, window, dragIcons), new DesktopExternalCalendarFacade(), new DesktopFileFacade(window, conf, dateProvider, desktopNet, electron, tfs, fs), + new DesktopImapImportSystemFacade(window), new DesktopInterWindowEventFacade(window, wm), nativeCredentialsFacade, desktopCrypto, @@ -317,7 +321,7 @@ async function startupInstance(components: Components) { const { wm, sse, tfs } = components if (!(await desktopUtils.cleanupOldInstance())) return sse.connect().catch((e) => log.warn("unable to start sse client", e)) - // The second-instance event fires when we call app.requestSingleInstanceLock inside of DesktopUtils.makeSingleInstance + // The second-instance event fires when we call app.requestSingleInstanceLock inside DesktopUtils.makeSingleInstance app.on("second-instance", async (_ev, args) => desktopUtils.handleSecondInstance(wm, args)) app.on("open-url", (e, url) => { // MacOS mailto handling diff --git a/src/common/desktop/imapimport/DesktopImapImportSystemFacade.ts b/src/common/desktop/imapimport/DesktopImapImportSystemFacade.ts new file mode 100644 index 000000000000..47308aee1633 --- /dev/null +++ b/src/common/desktop/imapimport/DesktopImapImportSystemFacade.ts @@ -0,0 +1,50 @@ +import { ImapCredentials, ImportCredentials, TutaCredentials, TutaCredentialType } from "@tutao/node-mimimi" +import { ImapImportSystemFacade } from "../../native/common/generatedipc/ImapImportSystemFacade.js" +import { ApplicationWindow } from "../ApplicationWindow.js" +import { locator } from "../../api/main/CommonLocator.js" +import { CredentialType } from "../../misc/credentials/CredentialType.js" +import { uint8ArrayToBitArray } from "@tutao/tutanota-crypto" + +export class DesktopImapImportSystemFacade implements ImapImportSystemFacade { + constructor(private readonly win: ApplicationWindow) {} + + async setup(imapCredentials: ImapCredentials): Promise { + const userId = locator.logins.getUserController().userId + const unencryptedCredentials = await locator.credentialsProvider.getDecryptedCredentialsByUserId(userId) + + if (unencryptedCredentials) { + try { + const tutaCredentials: TutaCredentials = { + accessToken: unencryptedCredentials?.accessToken, + credentialType: + unencryptedCredentials.credentialInfo.type == CredentialType.Internal ? TutaCredentialType.Internal : TutaCredentialType.External, + encryptedPassphraseKey: unencryptedCredentials.encryptedPassphraseKey + ? uint8ArrayToBitArray(unencryptedCredentials.encryptedPassphraseKey) + : [], + login: unencryptedCredentials.credentialInfo.login, + userId: unencryptedCredentials.credentialInfo.userId, + sseOrigin: "https://api.test.tuta.com", + clientVersion: "", + } + const importCredentials = ImportCredentials.setup(tutaCredentials, imapCredentials) + const importerObj = await importCredentials.login() + + console.log(importerObj) + } catch (e) { + console.log(e) + } + } else { + console.error(`could not load credentials for user with userId ${userId}`) + } + + Promise.resolve() + } + + startImport(): Promise { + throw new Error("Method not implemented.") + } + + stopImport(): Promise { + throw new Error("Method not implemented.") + } +} diff --git a/src/common/desktop/sse/SseClient.ts b/src/common/desktop/sse/SseClient.ts index 93d84c511347..01f8de4089a7 100644 --- a/src/common/desktop/sse/SseClient.ts +++ b/src/common/desktop/sse/SseClient.ts @@ -79,7 +79,7 @@ export class SseClient { constructor(private readonly net: DesktopNetworkClient, private readonly delay: SseDelay, private readonly scheduler: Scheduler) {} async connect(options: SseConnectOptions) { - log.debug("connect") + log.debug("connect", options) switch (this.state.state) { case ConnectionState.delayedReconnect: this.scheduler.unscheduleTimeout(this.state.timeout) diff --git a/src/common/login/PostLoginActions.ts b/src/common/login/PostLoginActions.ts index 0803532b206d..57ef1c4f1c13 100644 --- a/src/common/login/PostLoginActions.ts +++ b/src/common/login/PostLoginActions.ts @@ -35,6 +35,8 @@ import { CustomerFacade } from "../api/worker/facades/lazy/CustomerFacade.js" import { deviceConfig } from "../misc/DeviceConfig.js" import { ThemeController } from "../gui/ThemeController.js" import { EntityUpdateData, isUpdateForTypeRef } from "../api/common/utils/EntityUpdateUtils.js" +import { ImapCredentials } from "../../../packages/node-mimimi/dist/binding.js" +import { mailLocator } from "../../mail-app/mailLocator.js" /** * This is a collection of all things that need to be initialized/global state to be set after a user has logged in successfully. @@ -195,6 +197,18 @@ export class PostLoginActions implements PostLoginAction { // Needs to be called after UsageTestModel.init() if the UsageOptInNews is live! (its isShown() requires an initialized UsageTestModel) await locator.newsModel.loadNewsIds() + // FIXME + // setup imap import + if (isDesktop()) { + const imapCredentials: ImapCredentials = { + password: "imap-password", + username: "imap-user", + host: "mail.gmail.com", + port: "123", + } + await mailLocator.imapImportSystemFacade.setup(imapCredentials) + } + // Redraw to render usage tests and news, among other things that may have changed. m.redraw() } diff --git a/src/common/native/common/generatedipc/DesktopGlobalDispatcher.ts b/src/common/native/common/generatedipc/DesktopGlobalDispatcher.ts index c6473c215ea0..f532745c5517 100644 --- a/src/common/native/common/generatedipc/DesktopGlobalDispatcher.ts +++ b/src/common/native/common/generatedipc/DesktopGlobalDispatcher.ts @@ -10,6 +10,8 @@ import { ExternalCalendarFacade } from "./ExternalCalendarFacade.js" import { ExternalCalendarFacadeReceiveDispatcher } from "./ExternalCalendarFacadeReceiveDispatcher.js" import { FileFacade } from "./FileFacade.js" import { FileFacadeReceiveDispatcher } from "./FileFacadeReceiveDispatcher.js" +import { ImapImportSystemFacade } from "./ImapImportSystemFacade.js" +import { ImapImportSystemFacadeReceiveDispatcher } from "./ImapImportSystemFacadeReceiveDispatcher.js" import { InterWindowEventFacade } from "./InterWindowEventFacade.js" import { InterWindowEventFacadeReceiveDispatcher } from "./InterWindowEventFacadeReceiveDispatcher.js" import { NativeCredentialsFacade } from "./NativeCredentialsFacade.js" @@ -35,6 +37,7 @@ export class DesktopGlobalDispatcher { private readonly exportFacade: ExportFacadeReceiveDispatcher private readonly externalCalendarFacade: ExternalCalendarFacadeReceiveDispatcher private readonly fileFacade: FileFacadeReceiveDispatcher + private readonly imapImportSystemFacade: ImapImportSystemFacadeReceiveDispatcher private readonly interWindowEventFacade: InterWindowEventFacadeReceiveDispatcher private readonly nativeCredentialsFacade: NativeCredentialsFacadeReceiveDispatcher private readonly nativeCryptoFacade: NativeCryptoFacadeReceiveDispatcher @@ -50,6 +53,7 @@ export class DesktopGlobalDispatcher { exportFacade: ExportFacade, externalCalendarFacade: ExternalCalendarFacade, fileFacade: FileFacade, + imapImportSystemFacade: ImapImportSystemFacade, interWindowEventFacade: InterWindowEventFacade, nativeCredentialsFacade: NativeCredentialsFacade, nativeCryptoFacade: NativeCryptoFacade, @@ -65,6 +69,7 @@ export class DesktopGlobalDispatcher { this.exportFacade = new ExportFacadeReceiveDispatcher(exportFacade) this.externalCalendarFacade = new ExternalCalendarFacadeReceiveDispatcher(externalCalendarFacade) this.fileFacade = new FileFacadeReceiveDispatcher(fileFacade) + this.imapImportSystemFacade = new ImapImportSystemFacadeReceiveDispatcher(imapImportSystemFacade) this.interWindowEventFacade = new InterWindowEventFacadeReceiveDispatcher(interWindowEventFacade) this.nativeCredentialsFacade = new NativeCredentialsFacadeReceiveDispatcher(nativeCredentialsFacade) this.nativeCryptoFacade = new NativeCryptoFacadeReceiveDispatcher(nativeCryptoFacade) @@ -88,6 +93,8 @@ export class DesktopGlobalDispatcher { return this.externalCalendarFacade.dispatch(methodName, args) case "FileFacade": return this.fileFacade.dispatch(methodName, args) + case "ImapImportSystemFacade": + return this.imapImportSystemFacade.dispatch(methodName, args) case "InterWindowEventFacade": return this.interWindowEventFacade.dispatch(methodName, args) case "NativeCredentialsFacade": diff --git a/src/common/native/common/generatedipc/ImapCredentials.ts b/src/common/native/common/generatedipc/ImapCredentials.ts new file mode 100644 index 000000000000..373b86d9e1c2 --- /dev/null +++ b/src/common/native/common/generatedipc/ImapCredentials.ts @@ -0,0 +1,3 @@ +/* generated file, don't edit. */ + +export { ImapCredentials } from "../../../../../packages/node-mimimi/dist/binding.js" diff --git a/src/common/native/common/generatedipc/ImapImportSystemFacade.ts b/src/common/native/common/generatedipc/ImapImportSystemFacade.ts new file mode 100644 index 000000000000..5569491dfbc0 --- /dev/null +++ b/src/common/native/common/generatedipc/ImapImportSystemFacade.ts @@ -0,0 +1,22 @@ +/* generated file, don't edit. */ + +import { ImapCredentials } from "./ImapCredentials.js" +/** + * Facade implemented by the native desktop client starting and stopping an IMAP import. + */ +export interface ImapImportSystemFacade { + /** + * Initializing the IMAP import. + */ + setup(imapCredentials: ImapCredentials): Promise + + /** + * Start the IMAP import. + */ + startImport(): Promise + + /** + * Stop a running IMAP import. + */ + stopImport(): Promise +} diff --git a/src/common/native/common/generatedipc/ImapImportSystemFacadeReceiveDispatcher.ts b/src/common/native/common/generatedipc/ImapImportSystemFacadeReceiveDispatcher.ts new file mode 100644 index 000000000000..bfe0545097c4 --- /dev/null +++ b/src/common/native/common/generatedipc/ImapImportSystemFacadeReceiveDispatcher.ts @@ -0,0 +1,22 @@ +/* generated file, don't edit. */ + +import { ImapCredentials } from "./ImapCredentials.js" +import { ImapImportSystemFacade } from "./ImapImportSystemFacade.js" + +export class ImapImportSystemFacadeReceiveDispatcher { + constructor(private readonly facade: ImapImportSystemFacade) {} + async dispatch(method: string, arg: Array): Promise { + switch (method) { + case "setup": { + const imapCredentials: ImapCredentials = arg[0] + return this.facade.setup(imapCredentials) + } + case "startImport": { + return this.facade.startImport() + } + case "stopImport": { + return this.facade.stopImport() + } + } + } +} diff --git a/src/common/native/common/generatedipc/ImapImportSystemFacadeSendDispatcher.ts b/src/common/native/common/generatedipc/ImapImportSystemFacadeSendDispatcher.ts new file mode 100644 index 000000000000..867b7a5fb019 --- /dev/null +++ b/src/common/native/common/generatedipc/ImapImportSystemFacadeSendDispatcher.ts @@ -0,0 +1,19 @@ +/* generated file, don't edit. */ + +import { ImapImportSystemFacade } from "./ImapImportSystemFacade.js" + +interface NativeInterface { + invokeNative(requestType: string, args: unknown[]): Promise +} +export class ImapImportSystemFacadeSendDispatcher implements ImapImportSystemFacade { + constructor(private readonly transport: NativeInterface) {} + async setup(...args: Parameters) { + return this.transport.invokeNative("ipc", ["ImapImportSystemFacade", "setup", ...args]) + } + async startImport(...args: Parameters) { + return this.transport.invokeNative("ipc", ["ImapImportSystemFacade", "startImport", ...args]) + } + async stopImport(...args: Parameters) { + return this.transport.invokeNative("ipc", ["ImapImportSystemFacade", "stopImport", ...args]) + } +} diff --git a/src/common/native/common/generatedipc/NativePushFacade.ts b/src/common/native/common/generatedipc/NativePushFacade.ts index dfc13eba0b66..ac0e761bb9ab 100644 --- a/src/common/native/common/generatedipc/NativePushFacade.ts +++ b/src/common/native/common/generatedipc/NativePushFacade.ts @@ -2,7 +2,6 @@ import { EncryptedAlarmNotification } from "./EncryptedAlarmNotification.js" import { ExtendedNotificationMode } from "./ExtendedNotificationMode.js" - /** * Push notifications and alarms operations */ diff --git a/src/common/native/common/generatedipc/NativePushFacadeReceiveDispatcher.ts b/src/common/native/common/generatedipc/NativePushFacadeReceiveDispatcher.ts index b8a452c19172..e6aa61494699 100644 --- a/src/common/native/common/generatedipc/NativePushFacadeReceiveDispatcher.ts +++ b/src/common/native/common/generatedipc/NativePushFacadeReceiveDispatcher.ts @@ -6,7 +6,6 @@ import { NativePushFacade } from "./NativePushFacade.js" export class NativePushFacadeReceiveDispatcher { constructor(private readonly facade: NativePushFacade) {} - async dispatch(method: string, arg: Array): Promise { switch (method) { case "getPushIdentifier": { diff --git a/src/common/native/main/NativeInterfaceFactory.ts b/src/common/native/main/NativeInterfaceFactory.ts index 976b876428c0..f4e46770277d 100644 --- a/src/common/native/main/NativeInterfaceFactory.ts +++ b/src/common/native/main/NativeInterfaceFactory.ts @@ -38,6 +38,8 @@ import { MobilePaymentsFacadeSendDispatcher } from "../common/generatedipc/Mobil import { AppType } from "../../misc/ClientConstants.js" import { ExternalCalendarFacade } from "../common/generatedipc/ExternalCalendarFacade.js" import { ExternalCalendarFacadeSendDispatcher } from "../common/generatedipc/ExternalCalendarFacadeSendDispatcher.js" +import { ImapImportSystemFacadeSendDispatcher } from "../common/generatedipc/ImapImportSystemFacadeSendDispatcher.js" +import { ImapImportSystemFacade } from "../common/generatedipc/ImapImportSystemFacade.js" export type NativeInterfaces = { native: NativeInterfaceMain @@ -56,6 +58,7 @@ export type DesktopInterfaces = { searchTextFacade: SearchTextInAppFacade desktopSettingsFacade: SettingsFacadeSendDispatcher desktopSystemFacade: DesktopSystemFacade + imapImportSystemFacade: ImapImportSystemFacade interWindowEventSender: InterWindowEventFacadeSendDispatcher } @@ -113,6 +116,7 @@ export function createDesktopInterfaces(native: NativeInterfaceMain): DesktopInt searchTextFacade: new SearchTextInAppFacadeSendDispatcher(native), desktopSettingsFacade: new SettingsFacadeSendDispatcher(native), desktopSystemFacade: new DesktopSystemFacadeSendDispatcher(native), + imapImportSystemFacade: new ImapImportSystemFacadeSendDispatcher(native), interWindowEventSender: new InterWindowEventFacadeSendDispatcher(native), } } diff --git a/src/mail-app/mailLocator.ts b/src/mail-app/mailLocator.ts index 04f6a8c7a509..87e1e3b0dc4e 100644 --- a/src/mail-app/mailLocator.ts +++ b/src/mail-app/mailLocator.ts @@ -127,6 +127,7 @@ import type { ContactImporter } from "./contacts/ContactImporter.js" import { ExternalCalendarFacade } from "../common/native/common/generatedipc/ExternalCalendarFacade.js" import { AppType } from "../common/misc/ClientConstants.js" import { ParsedEvent } from "../common/calendar/import/CalendarImporter.js" +import { ImapImportSystemFacade } from "../common/native/common/generatedipc/ImapImportSystemFacade.js" assertMainOrNode() @@ -171,6 +172,7 @@ class MailLocator { searchTextFacade!: SearchTextInAppFacade desktopSettingsFacade!: SettingsFacade desktopSystemFacade!: DesktopSystemFacade + imapImportSystemFacade!: ImapImportSystemFacade webMobileFacade!: WebMobileFacade systemPermissionHandler!: SystemPermissionHandler interWindowEventSender!: InterWindowEventFacadeSendDispatcher @@ -805,6 +807,7 @@ class MailLocator { if (isDesktop()) { this.desktopSettingsFacade = desktopInterfaces.desktopSettingsFacade this.desktopSystemFacade = desktopInterfaces.desktopSystemFacade + this.imapImportSystemFacade = desktopInterfaces.imapImportSystemFacade } } else if (isAndroidApp() || isIOSApp()) { const { SystemPermissionHandler } = await import("../common/native/main/SystemPermissionHandler.js") diff --git a/test/tests/api/worker/crypto/InstanceMapperTest.ts b/test/tests/api/worker/crypto/InstanceMapperTest.ts index dc4ec9e7e4a2..bd78c7bdf13e 100644 --- a/test/tests/api/worker/crypto/InstanceMapperTest.ts +++ b/test/tests/api/worker/crypto/InstanceMapperTest.ts @@ -295,6 +295,7 @@ o.spec("InstanceMapper", function () { o(encryptValue("_id", vt, null, null)).equals(null) o(encryptValue("_permissions", vt, null, null)).equals(null) }) + o("throw error on ONE null values (enc String)", makeTestForErrorOnNull(ValueType.String)) o("throw error on ONE null values (enc Date)", makeTestForErrorOnNull(ValueType.Date)) o("throw error on ONE null values (enc Bytes)", makeTestForErrorOnNull(ValueType.Bytes)) diff --git a/test/types/test.d.ts b/test/types/test.d.ts index eb92ac06b6f8..0a153385478c 100644 --- a/test/types/test.d.ts +++ b/test/types/test.d.ts @@ -26,4 +26,5 @@ declare function node(f: F): F */ declare const buildOptions: { readonly sqliteNativePath: string + readonly mimimiNativePath: string } diff --git a/tsconfig.json b/tsconfig.json index 91bb648f77f8..ca3efda30908 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,6 +25,9 @@ }, { "path": "./packages/tuta-wasm-loader" + }, + { + "path": "./packages/node-mimimi" } ] } diff --git a/tuta-sdk/rust/Cargo.lock b/tuta-sdk/rust/Cargo.lock index c51bb7d64892..02524458254b 100644 --- a/tuta-sdk/rust/Cargo.lock +++ b/tuta-sdk/rust/Cargo.lock @@ -307,13 +307,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.98" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "jobserver", "libc", - "once_cell", + "shlex", ] [[package]] @@ -1294,9 +1294,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.202" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -1312,9 +1312,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -1343,6 +1343,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signature" version = "2.2.0" @@ -1428,9 +1434,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.65" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -1454,18 +1460,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", diff --git a/tuta-sdk/rust/sdk/Cargo.toml b/tuta-sdk/rust/sdk/Cargo.toml index a46378d60b7a..af4807b07d72 100644 --- a/tuta-sdk/rust/sdk/Cargo.toml +++ b/tuta-sdk/rust/sdk/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" async-trait = "0.1.77" serde = { version = "1.0.201", features = ["derive"] } serde_json = "1.0.117" -minicbor = { version = "0.24.2" , features = ["std", "alloc"]} +minicbor = { version = "0.24.2", features = ["std", "alloc"] } thiserror = "1.0.60" base64 = "0.22.1" aes = { version = "0.8.4", features = ["zeroize"] } diff --git a/tuta-sdk/rust/sdk/src/crypto/aes.rs b/tuta-sdk/rust/sdk/src/crypto/aes.rs index 13d5965d123f..a7e1bac6046a 100644 --- a/tuta-sdk/rust/sdk/src/crypto/aes.rs +++ b/tuta-sdk/rust/sdk/src/crypto/aes.rs @@ -142,7 +142,6 @@ trait AesKey: Clone { /// An initialisation vector for AES encryption pub struct Iv([u8; IV_BYTE_SIZE]); -#[cfg(test)] impl Clone for Iv { /// Clone the initialization vector /// @@ -166,6 +165,10 @@ impl Iv { fn from_slice(slice: &[u8]) -> Option { Self::from_bytes(slice).ok() } + + pub fn get_inner(&self) -> &[u8; IV_BYTE_SIZE] { + &self.0 + } } #[derive(thiserror::Error, Debug)] diff --git a/tuta-sdk/rust/sdk/src/crypto/crypto_facade.rs b/tuta-sdk/rust/sdk/src/crypto/crypto_facade.rs index bd5ffc473fa3..311e28fc463b 100644 --- a/tuta-sdk/rust/sdk/src/crypto/crypto_facade.rs +++ b/tuta-sdk/rust/sdk/src/crypto/crypto_facade.rs @@ -39,6 +39,7 @@ pub struct CryptoFacade { /// Session key that encrypts an entity and the same key encrypted with the owner group. /// owner_enc_session_key is stored on entities to avoid public key encryption on subsequent loads. +#[derive(Clone)] pub struct ResolvedSessionKey { pub session_key: GenericAesKey, pub owner_enc_session_key: Vec, @@ -310,14 +311,14 @@ impl<'a> EntityOwnerKeyData<'a> { entity: &'a ParsedEntity, ) -> Result, SessionKeyResolutionError> { macro_rules! get_nullable_field { - ($entity:expr, $field:expr, $type:tt) => { - match $entity.get($field) { - Some(ElementValue::$type(q)) => Ok(Some(q)), - None | Some(ElementValue::Null) => Ok(None), // none = not present on type, null = present on type but null - Some(actual) => Err(SessionKeyResolutionError { reason: format!("field `{}` is not the expected type, got {} instead", $field, actual.type_variant_name()) }) - } - }; - } + ($entity:expr, $field:expr, $type:tt) => { + match $entity.get($field) { + Some(ElementValue::$type(q)) => Ok(Some(q)), + None | Some(ElementValue::Null) => Ok(None), // none = not present on type, null = present on type but null + Some(actual) => Err(SessionKeyResolutionError { reason: format!("field `{}` is not the expected type, got {} instead", $field, actual.type_variant_name()) }), + } + }; + } let owner_enc_session_key = get_nullable_field!(entity, OWNER_ENC_SESSION_FIELD, Bytes)?; let owner_key_version = @@ -332,7 +333,7 @@ impl<'a> EntityOwnerKeyData<'a> { } } -#[derive(thiserror::Error, Debug)] +#[derive(thiserror::Error, Debug, Clone)] #[error("Session key resolution failure: {reason}")] pub struct SessionKeyResolutionError { reason: String, diff --git a/tuta-sdk/rust/sdk/src/crypto/mod.rs b/tuta-sdk/rust/sdk/src/crypto/mod.rs index 17b5337d809d..71004047f0dd 100644 --- a/tuta-sdk/rust/sdk/src/crypto/mod.rs +++ b/tuta-sdk/rust/sdk/src/crypto/mod.rs @@ -4,7 +4,6 @@ #[allow(unused_imports)] pub use aes::Aes128Key; -#[cfg(test)] pub use aes::Iv; pub use aes::PlaintextAndIv; #[allow(unused_imports)] @@ -15,7 +14,7 @@ pub use sha::sha256; #[allow(unused_imports)] pub use tuta_crypt::PQKeyPairs; -mod aes; +pub mod aes; mod sha; diff --git a/tuta-sdk/rust/sdk/src/custom_id.rs b/tuta-sdk/rust/sdk/src/custom_id.rs index 2bee02076092..6f324e1eeec4 100644 --- a/tuta-sdk/rust/sdk/src/custom_id.rs +++ b/tuta-sdk/rust/sdk/src/custom_id.rs @@ -6,7 +6,7 @@ use std::fmt::{Debug, Display, Formatter}; pub const CUSTOM_ID_STRUCT_NAME: &str = "CustomId"; /// An ID that uses arbitrary data encoded in base64 -#[derive(Clone, Default, PartialEq, PartialOrd)] +#[derive(Clone, Default, Eq, PartialEq, PartialOrd)] #[repr(transparent)] pub struct CustomId(pub String); diff --git a/tuta-sdk/rust/sdk/src/element_value.rs b/tuta-sdk/rust/sdk/src/element_value.rs index bb173a05f276..88832e31fb0b 100644 --- a/tuta-sdk/rust/sdk/src/element_value.rs +++ b/tuta-sdk/rust/sdk/src/element_value.rs @@ -8,119 +8,199 @@ use std::collections::HashMap; /// Primitive value types used by entity/instance types #[derive(uniffi::Enum, Debug, Serialize, Deserialize, PartialEq, Clone)] pub enum ElementValue { - Null, - String(String), - Number(i64), - Bytes(Vec), - Date(DateTime), - Bool(bool), - // Names are prefixed with 'Id' to avoid name collision in Kotlin - IdGeneratedId(GeneratedId), - IdCustomId(CustomId), - IdTupleId(IdTuple), - Dict(HashMap), - Array(Vec), + Null, + String(String), + Number(i64), + Bytes(Vec), + Date(DateTime), + Bool(bool), + // Names are prefixed with 'Id' to avoid name collision in Kotlin + IdGeneratedId(GeneratedId), + IdCustomId(CustomId), + IdTupleId(IdTuple), + Dict(HashMap), + Array(Vec), } pub type ParsedEntity = HashMap; impl ElementValue { - pub fn assert_number(&self) -> i64 { - match self { - ElementValue::Number(number) => *number, - _ => panic!("Invalid type"), - } - } - - pub fn assert_string(&self) -> String { - self.assert_str().to_string() - } - - pub fn assert_array(&self) -> Vec { - match self { - ElementValue::Array(value) => value.clone(), - _ => panic!("Invalid type"), - } - } - - pub fn assert_bytes(&self) -> Vec { - match self { - ElementValue::Bytes(value) => value.clone(), - _ => panic!( - "Invalid type, expected bytes, got: {}", - self.type_variant_name() - ), - } - } - - pub fn assert_dict(&self) -> HashMap { - match self { - ElementValue::Dict(value) => value.clone(), - _ => panic!("Invalid type"), - } - } - - pub fn assert_array_ref(&self) -> &Vec { - match self { - ElementValue::Array(value) => value, - _ => panic!("Invalid type"), - } - } - - pub fn assert_dict_ref(&self) -> &HashMap { - match self { - ElementValue::Dict(value) => value, - _ => panic!("Invalid type"), - } - } - - pub fn assert_str(&self) -> &str { - match self { - ElementValue::String(value) => value, - _ => panic!("Invalid type"), - } - } - - pub fn assert_generated_id(&self) -> &GeneratedId { - match self { - ElementValue::IdGeneratedId(value) => value, - _ => panic!("Invalid type"), - } - } - pub fn assert_tuple_id(&self) -> &IdTuple { - match self { - ElementValue::IdTupleId(value) => value, - _ => panic!("Invalid type"), - } - } - - pub fn assert_date(&self) -> &DateTime { - match self { - ElementValue::Date(value) => value, - _ => panic!("Invalid type"), - } - } - - pub fn assert_bool(&self) -> bool { - match self { - ElementValue::Bool(value) => *value, - _ => panic!("Invalid type"), - } - } - - pub(crate) fn type_variant_name(&self) -> &'static str { - match self { - Self::Null => "Null", - Self::String(_) => "String", - Self::Number(_) => "Number", - Self::Bytes(_) => "Bytes", - Self::Date(_) => "Date", - Self::Bool(_) => "Bool", - Self::IdGeneratedId(_) => "IdGeneratedId", - Self::IdCustomId(_) => "IdCustomId", - Self::IdTupleId(_) => "IdTupleId", - Self::Dict(_) => "Dict", - Self::Array(_) => "Array", - } - } + pub fn assert_number(&self) -> i64 { + match self { + ElementValue::Number(number) => *number, + _ => panic!("Invalid type"), + } + } + + pub fn assert_string(&self) -> String { + self.assert_str().to_string() + } + + pub fn assert_array(&self) -> Vec { + match self { + ElementValue::Array(value) => value.clone(), + _ => panic!("Invalid type"), + } + } + + pub fn assert_bytes(&self) -> Vec { + match self { + ElementValue::Bytes(value) => value.clone(), + _ => panic!( + "Invalid type, expected bytes, got: {}", + self.type_variant_name() + ), + } + } + + pub fn assert_dict(&self) -> HashMap { + match self { + ElementValue::Dict(value) => value.clone(), + _ => panic!("Invalid type"), + } + } + + pub fn assert_array_ref(&self) -> &Vec { + match self { + ElementValue::Array(value) => value, + _ => panic!("Invalid type"), + } + } + + pub fn assert_dict_ref(&self) -> &HashMap { + match self { + ElementValue::Dict(value) => value, + _ => panic!("Invalid type"), + } + } + + pub fn assert_dict_mut_ref(&mut self) -> &mut HashMap { + match self { + ElementValue::Dict(value) => value, + _ => panic!("Invalid type"), + } + } + + pub fn assert_str(&self) -> &str { + match self { + ElementValue::String(value) => value, + _ => panic!("Invalid type"), + } + } + + pub fn assert_generated_id(&self) -> &GeneratedId { + match self { + ElementValue::IdGeneratedId(value) => value, + _ => panic!("Invalid type"), + } + } + pub fn assert_tuple_id(&self) -> &IdTuple { + match self { + ElementValue::IdTupleId(value) => value, + _ => panic!("Invalid type"), + } + } + + pub fn assert_custom_id(&self) -> &CustomId { + match self { + ElementValue::IdCustomId(value) => value, + _ => panic!("Invalid type"), + } + } + + pub fn assert_date(&self) -> &DateTime { + match self { + ElementValue::Date(value) => value, + _ => panic!("Invalid type"), + } + } + + pub fn assert_bool(&self) -> bool { + match self { + ElementValue::Bool(value) => *value, + _ => panic!("Invalid type"), + } + } + + pub(crate) fn type_variant_name(&self) -> &'static str { + match self { + Self::Null => "Null", + Self::String(_) => "String", + Self::Number(_) => "Number", + Self::Bytes(_) => "Bytes", + Self::Date(_) => "Date", + Self::Bool(_) => "Bool", + Self::IdGeneratedId(_) => "IdGeneratedId", + Self::IdCustomId(_) => "IdCustomId", + Self::IdTupleId(_) => "IdTupleId", + Self::Dict(_) => "Dict", + Self::Array(_) => "Array", + } + } +} + +impl From<()> for ElementValue { + fn from(value: ()) -> Self { + Self::Null + } +} + +impl From for ElementValue { + fn from(value: String) -> Self { + Self::String(value) + } } + +impl From for ElementValue { + fn from(value: i64) -> Self { + Self::Number(value) + } +} + +impl From> for ElementValue { + fn from(value: Vec) -> Self { + Self::Bytes(value) + } +} + +impl From for ElementValue { + fn from(value: DateTime) -> Self { + Self::Date(value) + } +} + +impl From for ElementValue { + fn from(value: bool) -> Self { + Self::Bool(value) + } +} + +impl From for ElementValue { + fn from(value: GeneratedId) -> Self { + Self::IdGeneratedId(value) + } +} + +impl From for ElementValue { + fn from(value: CustomId) -> Self { + Self::IdCustomId(value) + } +} + +impl From for ElementValue { + fn from(value: IdTuple) -> Self { + Self::IdTupleId(value) + } +} + +impl From> for ElementValue { + fn from(value: HashMap) -> Self { + Self::Dict(value) + } +} + +impl From> for ElementValue { + fn from(value: Vec) -> Self { + Self::Array(value) + } +} \ No newline at end of file diff --git a/tuta-sdk/rust/sdk/src/entities/accounting.rs b/tuta-sdk/rust/sdk/src/entities/accounting.rs index 86f51edd5099..b4aae76feea1 100644 --- a/tuta-sdk/rust/sdk/src/entities/accounting.rs +++ b/tuta-sdk/rust/sdk/src/entities/accounting.rs @@ -2,7 +2,7 @@ use super::*; use serde::{Deserialize, Serialize}; -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CustomerAccountPosting { pub _id: CustomId, pub amount: i64, @@ -12,7 +12,6 @@ pub struct CustomerAccountPosting { pub valueDate: DateTime, pub _finalIvs: HashMap, } - impl Entity for CustomerAccountPosting { fn type_ref() -> TypeRef { TypeRef { @@ -22,7 +21,10 @@ impl Entity for CustomerAccountPosting { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CustomerAccountReturn { pub _format: i64, pub _ownerGroup: Option, @@ -35,7 +37,6 @@ pub struct CustomerAccountReturn { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for CustomerAccountReturn { fn type_ref() -> TypeRef { TypeRef { @@ -44,3 +45,6 @@ impl Entity for CustomerAccountReturn { } } } + + + diff --git a/tuta-sdk/rust/sdk/src/entities/base.rs b/tuta-sdk/rust/sdk/src/entities/base.rs index 34da086a9db9..1a58f2ef4c60 100644 --- a/tuta-sdk/rust/sdk/src/entities/base.rs +++ b/tuta-sdk/rust/sdk/src/entities/base.rs @@ -2,13 +2,12 @@ use super::*; use serde::{Deserialize, Serialize}; -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PersistenceResourcePostReturn { pub _format: i64, pub generatedId: Option, pub permissionListId: GeneratedId, } - impl Entity for PersistenceResourcePostReturn { fn type_ref() -> TypeRef { TypeRef { @@ -17,3 +16,6 @@ impl Entity for PersistenceResourcePostReturn { } } } + + + diff --git a/tuta-sdk/rust/sdk/src/entities/entity_facade.rs b/tuta-sdk/rust/sdk/src/entities/entity_facade.rs index ee60b475aa98..22dd7607e1a0 100644 --- a/tuta-sdk/rust/sdk/src/entities/entity_facade.rs +++ b/tuta-sdk/rust/sdk/src/entities/entity_facade.rs @@ -1,21 +1,23 @@ -#![allow(unused)] // TODO: Remove this when implementing the crypto entity client -use std::borrow::Borrow; -use std::collections::HashMap; -use std::sync::Arc; - +#![allow(unused)] use crate::crypto::crypto_facade::ResolvedSessionKey; use crate::crypto::key::GenericAesKey; -use crate::crypto::PlaintextAndIv; +use crate::crypto::randomizer_facade::RandomizerFacade; +use crate::crypto::{Iv, PlaintextAndIv, IV_BYTE_SIZE}; use crate::date::DateTime; -use crate::element_value::ElementValue::Bool; use crate::element_value::{ElementValue, ParsedEntity}; use crate::entities::Errors; use crate::metamodel::{ - AssociationType, Cardinality, ModelAssociation, ModelValue, TypeModel, ValueType, + AssociationType, Cardinality, ElementType, ModelAssociation, ModelValue, TypeModel, ValueType, }; use crate::type_model_provider::TypeModelProvider; -use crate::util::{array_cast_slice, resolve_default_value}; +use crate::util::array_cast_slice; use crate::ApiCallError; +use base64::prelude::{BASE64_STANDARD, BASE64_URL_SAFE_NO_PAD}; +use base64::Engine; +use minicbor::Encode; +use std::borrow::Borrow; +use std::collections::HashMap; +use std::sync::Arc; /// Provides high level functions to handle encryption/decryption of entities #[derive(uniffi::Object)] @@ -33,10 +35,30 @@ struct MappedValue { error: Option, } +#[derive(Clone)] +enum IvProvider { + Fixed(Iv), + Random, +} +impl IvProvider { + fn provide_iv(&self) -> Iv { + match self { + IvProvider::Random => Iv::from_bytes( + &RandomizerFacade::from_core(rand_core::OsRng) + .generate_random_array::() + .to_vec(), + ) + .unwrap(), + + IvProvider::Fixed(iv) => iv.clone(), + } + } +} + #[cfg_attr(test, mockall::automock)] impl EntityFacade { pub fn new(type_model_provider: Arc) -> Self { - EntityFacade { + Self { type_model_provider, } } @@ -55,6 +77,243 @@ impl EntityFacade { Ok(mapped_decrypted) } + fn should_restore_default_value( + model_value: &ModelValue, + value: &ElementValue, + instance: &ParsedEntity, + key: &str, + ) -> bool { + if model_value.encrypted { + if let Some(final_ivs) = Self::get_final_iv_for_key(instance, key) { + return final_ivs.assert_bytes().is_empty() + && value == &ValueType::get_default(&model_value.value_type); + } + } + false + } + + fn encrypt_value( + value_name: &str, + model_value: &ModelValue, + instance_value: &ElementValue, + session_key: &Option, + iv_provider: IvProvider, + ) -> Result { + let value_type = &model_value.value_type; + + if value_name == "_id" || value_name == "_permissions" { + Ok(instance_value.clone()) + } else if (>::is_nil(instance_value)) { + if model_value.cardinality == Cardinality::ZeroOrOne { + Ok(ElementValue::Null) + } else { + Err(ApiCallError::internal(format!( + "Value {value_name} with cardinality ONE can not be null" + ))) + } + } else if model_value.encrypted { + let iv = iv_provider.provide_iv(); + let bytes = Self::map_value_to_binary(value_type, instance_value) + .expect(&format!("invalid encrypted value {:?}", instance_value)); + let encrypted_data = session_key + .as_ref() + .ok_or(ApiCallError::internal( + "session_key can't be null for encrypted value".to_string(), + ))? + .encrypt_data(bytes.as_slice(), iv) + .expect("Cannot encrypt data"); + Ok(ElementValue::Bytes(encrypted_data)) + } else { + Ok(instance_value.clone()) + } + } + + fn get_final_iv_for_key(instance: &ParsedEntity, key: &str) -> Option { + if let Some(final_ivs) = instance.get("_finalIvs") { + if let Some(array) = final_ivs.assert_dict_ref().get(key) { + return Some(ElementValue::Bytes(array.assert_bytes())); + }; + }; + None + } + + fn map_value_to_db_type(value_type: &ValueType, value: &ElementValue) -> ElementValue { + match value { + ElementValue::Date(d) => ElementValue::String(d.as_millis().to_string()), + ElementValue::Bool(b) => ElementValue::String(if *b { + String::from("1") + } else { + String::from("0") + }), + ElementValue::Number(n) => ElementValue::String(n.to_string()), + other => other.clone(), + } + } + + fn map_value_to_binary(value_type: &ValueType, value: &ElementValue) -> Option> { + if >::is_nil(value) { + return None; + } + match value_type { + ValueType::Bytes => Some(value.assert_bytes()), + ValueType::String => Some(value.assert_string().as_bytes().to_vec()), + ValueType::Number => Some(value.assert_number().to_string().as_bytes().to_vec()), + ValueType::Date => Some( + value + .assert_date() + .as_millis() + .to_string() + .as_bytes() + .to_vec(), + ), + ValueType::Boolean => Some(if value.assert_bool() { b"1" } else { b"0" }.to_vec()), + ValueType::GeneratedId => Some(value.assert_generated_id().0.as_bytes().to_vec()), + ValueType::CustomId => Some(value.assert_custom_id().0.as_bytes().to_vec()), + ValueType::CompressedString => unimplemented!("compressed string"), + } + } + + pub fn encrypt_and_map_to_literal( + &self, + type_model: &TypeModel, + instance: &ParsedEntity, + sk: Option, + ) -> Result { + self.encrypt_and_map_to_literal_with_iv(type_model, instance, sk, IvProvider::Random) + } + + fn encrypt_and_map_to_literal_with_iv( + &self, + type_model: &TypeModel, + instance: &ParsedEntity, + sk: Option, + random_iv_provider: IvProvider, + ) -> Result { + if (type_model.marked_encrypted() && sk == None) { + return Err(ApiCallError::InternalSdkError { + error_message: format!( + "Encrypting {}/{} requires a session key!", + type_model.app, type_model.name + ), + }); + } + let mut encrypted = ParsedEntity::new(); + + for (key, model_value) in type_model.values.iter() { + let instance_value = instance + .get(&key.to_string()) + .expect(format!("Can not find key: {key} in instance: {instance:?}").as_str()); + + let encrypted_value: ElementValue; + + if (Self::should_restore_default_value(&model_value, instance_value, &instance, &key)) { + // restore the default encrypted value because it has not changed + // note: this branch must be checked *before* the one which reuses IVs as this one checks + // the length. + encrypted_value = ElementValue::String("".to_string()); + } else if (model_value.encrypted + && model_value.is_final + && Self::get_final_iv_for_key(instance, key).is_some()) + { + let final_iv = Iv::from_bytes( + Self::get_final_iv_for_key(instance, key) + .unwrap() + .assert_bytes() + .as_slice(), + ) + .map_err(|err| ApiCallError::internal(format!("iv of illegal size {:?}", err)))?; + encrypted_value = Self::encrypt_value( + key, + model_value, + instance_value, + &sk, + IvProvider::Fixed(final_iv), + )? + } else { + encrypted_value = Self::encrypt_value( + key, + model_value, + instance_value, + &sk, + random_iv_provider.clone(), + )? + } + encrypted.insert(key.to_string(), encrypted_value); + } + + if (type_model.element_type == ElementType::Aggregated && encrypted.get("_id").is_none()) { + let randomizer = RandomizerFacade::from_core(rand_core::OsRng); + let new_id = randomizer.generate_random_array::<4>(); + + encrypted.insert( + String::from("_id"), + ElementValue::String( + BASE64_URL_SAFE_NO_PAD.encode(BASE64_STANDARD.encode(&new_id)), + ), + ); + } + + for (association_name, association) in type_model.associations.iter() { + if let AssociationType::Aggregation = association.association_type { + let dependency = association.dependency.unwrap_or(type_model.app); + let aggregated_type_model = self + .type_model_provider + .get_type_model(dependency, association.ref_type) + .expect(&format!( + "Cannot find type model for: {:?}", + (dependency, association.ref_type) + )); + let aggregation = association; + let instance_association = instance.get(&association_name.to_string()).unwrap(); + if aggregation.cardinality == Cardinality::ZeroOrOne + && instance_association == &ElementValue::Null + { + encrypted.insert(association_name.to_string(), ElementValue::Null); + } else if instance_association == &ElementValue::Null { + panic!("Undefined attribute {}:{association_name}", type_model.name); + } else if aggregation.cardinality == Cardinality::Any { + let aggregates = instance_association.assert_array(); + let mut encrypted_aggregates = Vec::with_capacity(aggregates.len()); + for aggregate in aggregates.iter() { + let parsed_entity = self.encrypt_and_map_to_literal_with_iv( + aggregated_type_model, + &aggregate.assert_dict(), + sk.clone(), + random_iv_provider.clone(), + )?; + encrypted_aggregates.push(ElementValue::Dict(parsed_entity)); + } + + encrypted.insert( + association_name.to_string(), + ElementValue::Array(encrypted_aggregates), + ); + } else { + let parsed_entity = self.encrypt_and_map_to_literal_with_iv( + aggregated_type_model, + &instance_association.assert_dict(), + sk.clone(), + random_iv_provider.clone(), + )?; + let encrypted_aggregate = ElementValue::Dict(parsed_entity); + encrypted.insert(association_name.to_string(), encrypted_aggregate); + } + } else { + encrypted.insert( + association_name.to_string(), + instance.get(&association_name.to_string()).cloned().ok_or( + ApiCallError::internal(format!( + "could not find association {association_name} on type {}", + type_model.name + )), + )?, + ); + } + } + + Ok(encrypted) + } + fn decrypt_and_map_inner( &self, type_model: &TypeModel, @@ -226,7 +485,7 @@ impl EntityFacade { // If the value is default-encrypted (empty string) then return default value and // empty IV. When re-encrypting we should put the empty value back to not increase // used storage. - let value = resolve_default_value(&model_value.value_type); + let value = model_value.value_type.get_default(); Ok(MappedValue { value, iv: Some(Vec::new()), @@ -269,7 +528,7 @@ impl EntityFacade { }) }, Err(err) => Ok(MappedValue { - value: resolve_default_value(&model_value.value_type), + value: model_value.value_type.get_default(), iv: None, error: Some(format!("Failed to decrypt {key}. {err}")), }), @@ -329,7 +588,7 @@ impl EntityFacade { }) }, }; - Ok(Bool(value)) + Ok(ElementValue::Bool(value)) }, ValueType::CompressedString => unimplemented!("compressed string"), v => unreachable!("Can't parse {v:?} into ElementValue"), @@ -339,38 +598,41 @@ impl EntityFacade { #[cfg(test)] mod tests { - use std::sync::Arc; - - use rand::random; - - use crate::collection; use crate::crypto::crypto_facade::ResolvedSessionKey; use crate::crypto::key::GenericAesKey; + use crate::crypto::randomizer_facade::RandomizerFacade; use crate::crypto::{Aes256Key, Iv}; use crate::date::DateTime; - use crate::element_value::ParsedEntity; - use crate::entities::entity_facade::EntityFacade; + use crate::element_value::{ElementValue, ParsedEntity}; + use crate::entities::entity_facade::{EntityFacade, IvProvider}; + use crate::entities::sys::CustomerAccountTerminationRequest; use crate::entities::tutanota::Mail; use crate::entities::Entity; use crate::instance_mapper::InstanceMapper; use crate::json_element::{JsonElement, RawEntity}; use crate::json_serializer::JsonSerializer; + use crate::metamodel::{Cardinality, ModelValue, ValueType}; use crate::type_model_provider::init_type_model_provider; + use crate::util::entity_test_utils::generate_email_entity; + use crate::{collection, ApiCallError}; + use std::collections::{BTreeMap, HashMap}; + use std::sync::Arc; + use std::time::SystemTime; + + const KNOWN_SK: [u8; 32] = [ + 83, 168, 168, 203, 48, 91, 246, 102, 175, 252, 39, 110, 36, 141, 4, 216, 135, 201, 226, + 134, 182, 175, 15, 152, 117, 216, 81, 1, 120, 134, 116, 143, + ]; + + fn no_need_for_iv() -> Option { + panic!("IV should not have been used") + } #[test] fn test_decrypt_mail() { - let sk = GenericAesKey::Aes256( - Aes256Key::from_bytes( - vec![ - 83, 168, 168, 203, 48, 91, 246, 102, 175, 252, 39, 110, 36, 141, 4, 216, 135, - 201, 226, 134, 182, 175, 15, 152, 117, 216, 81, 1, 120, 134, 116, 143, - ] - .as_slice(), - ) - .unwrap(), - ); + let sk = GenericAesKey::Aes256(Aes256Key::from_bytes(KNOWN_SK.as_slice()).unwrap()); let owner_enc_session_key = vec![0, 1, 2]; - let iv = Iv::from_bytes(&random::<[u8; 16]>()).unwrap(); + let iv = Iv::from_bytes(&rand::random::<[u8; 16]>()).unwrap(); let type_model_provider = Arc::new(init_type_model_provider()); let raw_entity: RawEntity = make_json_entity(); let json_serializer = JsonSerializer::new(type_model_provider.clone()); @@ -459,6 +721,631 @@ mod tests { ); } + #[test] + fn encrypt_value_string() { + let model_value = create_model_value(ValueType::String, true, Cardinality::One); + let sk = GenericAesKey::from_bytes(&[rand::random(); 32]).ok(); + let iv = Iv::generate(&RandomizerFacade::from_core(rand_core::OsRng)); + let value = ElementValue::String("this is a string value".to_string()); + + let encrypted_value = EntityFacade::encrypt_value( + "test", + &model_value, + &value, + &sk, + IvProvider::Fixed(iv.clone()), + ); + + let expected = sk + .unwrap() + .encrypt_data(value.assert_string().as_bytes(), iv) + .unwrap(); + + assert_eq!(expected, encrypted_value.unwrap().assert_bytes().to_vec()) + } + + #[test] + fn encrypt_value_bool() { + let model_value = create_model_value(ValueType::Boolean, true, Cardinality::One); + let sk = GenericAesKey::from_bytes(&[rand::random(); 32]).ok(); + let iv = Iv::generate(&RandomizerFacade::from_core(rand_core::OsRng)); + + { + let value = ElementValue::Bool(true); + + let encrypted_value = EntityFacade::encrypt_value( + "test", + &model_value, + &value, + &sk, + IvProvider::Fixed(iv.clone()), + ); + + let expected = sk + .clone() + .unwrap() + .encrypt_data("1".as_bytes(), iv.clone()) + .unwrap(); + assert_eq!(expected, encrypted_value.unwrap().assert_bytes().to_vec()) + } + + { + let value = ElementValue::Bool(false); + let encrypted_value = EntityFacade::encrypt_value( + "test", + &model_value, + &value, + &sk, + IvProvider::Fixed(iv.clone()), + ); + + let expected = sk + .clone() + .unwrap() + .encrypt_data("0".as_bytes(), iv.clone()) + .unwrap(); + assert_eq!(expected, encrypted_value.unwrap().assert_bytes().to_vec()) + } + } + + #[test] + fn encrypt_value_date() { + let model_value = create_model_value(ValueType::Date, true, Cardinality::One); + let sk = GenericAesKey::from_bytes(&[rand::random(); 32]).ok(); + let iv = Iv::generate(&RandomizerFacade::from_core(rand_core::OsRng)); + let value = ElementValue::Date(DateTime::from_system_time(SystemTime::now())); + + let encrypted_value = EntityFacade::encrypt_value( + "test", + &model_value, + &value, + &sk, + IvProvider::Fixed(iv.clone()), + ); + + let expected = sk + .unwrap() + .encrypt_data(value.assert_date().as_millis().to_string().as_bytes(), iv) + .unwrap(); + + assert_eq!(expected, encrypted_value.unwrap().assert_bytes().to_vec()); + } + + #[test] + fn encrypt_value_bytes() { + let model_value = create_model_value(ValueType::Bytes, true, Cardinality::One); + let sk = GenericAesKey::from_bytes(&[rand::random(); 32]).ok(); + let randomizer_facade = &RandomizerFacade::from_core(rand_core::OsRng); + let iv = Iv::generate(randomizer_facade); + let value = ElementValue::Bytes(randomizer_facade.generate_random_array::<5>().to_vec()); + + let encrypted_value = EntityFacade::encrypt_value( + "test", + &model_value, + &value, + &sk, + IvProvider::Fixed(iv.clone()), + ); + + let expected = sk + .unwrap() + .encrypt_data(value.assert_bytes().as_slice(), iv) + .unwrap(); + + assert_eq!(expected, encrypted_value.unwrap().assert_bytes().to_vec()); + } + + #[test] + fn encrypt_value_null() { + let sk = GenericAesKey::from_bytes(&[rand::random(); 32]).ok(); + + assert_eq!( + ElementValue::Null, + EntityFacade::encrypt_value( + "test", + &create_model_value(ValueType::Bytes, true, Cardinality::ZeroOrOne), + &ElementValue::Null, + &sk, + IvProvider::Random, + ) + .unwrap() + ); + assert_eq!( + ElementValue::Null, + EntityFacade::encrypt_value( + "test", + &create_model_value(ValueType::Date, true, Cardinality::ZeroOrOne), + &ElementValue::Null, + &sk, + IvProvider::Random, + ) + .unwrap() + ); + assert_eq!( + ElementValue::Null, + EntityFacade::encrypt_value( + "test", + &create_model_value(ValueType::String, true, Cardinality::ZeroOrOne), + &ElementValue::Null, + &sk, + IvProvider::Random, + ) + .unwrap() + ); + assert_eq!( + ElementValue::Null, + EntityFacade::encrypt_value( + "test", + &create_model_value(ValueType::Boolean, true, Cardinality::ZeroOrOne), + &ElementValue::Null, + &sk, + IvProvider::Random, + ) + .unwrap() + ); + assert_eq!( + ElementValue::Null, + EntityFacade::encrypt_value( + "test", + &create_model_value(ValueType::Date, true, Cardinality::ZeroOrOne), + &ElementValue::Null, + &sk, + IvProvider::Random, + ) + .unwrap() + ); + assert_eq!( + ElementValue::Null, + EntityFacade::encrypt_value( + "test", + &create_model_value(ValueType::GeneratedId, true, Cardinality::ZeroOrOne), + &ElementValue::Null, + &sk, + IvProvider::Random, + ) + .unwrap() + ); + assert_eq!( + ElementValue::Null, + EntityFacade::encrypt_value( + "test", + &create_model_value(ValueType::CustomId, true, Cardinality::ZeroOrOne), + &ElementValue::Null, + &sk, + IvProvider::Random, + ) + .unwrap() + ); + assert_eq!( + ElementValue::Null, + EntityFacade::encrypt_value( + "test", + &create_model_value(ValueType::CompressedString, true, Cardinality::ZeroOrOne), + &ElementValue::Null, + &sk, + IvProvider::Random, + ) + .unwrap() + ); + assert_eq!( + ElementValue::Null, + EntityFacade::encrypt_value( + "test", + &create_model_value(ValueType::Number, true, Cardinality::ZeroOrOne), + &ElementValue::Null, + &sk, + IvProvider::Random, + ) + .unwrap() + ); + } + + #[test] + fn encrypt_value_accept_null_id_and_permission() { + let sk = GenericAesKey::from_bytes(&[rand::random(); 32]).ok(); + + assert_eq!( + ElementValue::Null, + EntityFacade::encrypt_value( + "_id", + &create_model_value(ValueType::GeneratedId, true, Cardinality::One), + &ElementValue::Null, + &sk, + IvProvider::Random, + ) + .unwrap() + ); + + assert_eq!( + ElementValue::Null, + EntityFacade::encrypt_value( + "_permissions", + &create_model_value(ValueType::CustomId, true, Cardinality::One), + &ElementValue::Null, + &sk, + IvProvider::Random, + ) + .unwrap() + ); + } + + #[test] + fn encrypt_value_do_not_accept_null() { + let sk = GenericAesKey::from_bytes(&[rand::random(); 32]).ok(); + + let value_types = [ + ValueType::Bytes, + ValueType::Boolean, + ValueType::CompressedString, + ValueType::String, + ValueType::Date, + ValueType::GeneratedId, + ValueType::Number, + ValueType::CustomId, + ]; + + for value_type in value_types { + assert_eq!( + Err(ApiCallError::internal( + "Value test with cardinality ONE can not be null".to_string() + )), + EntityFacade::encrypt_value( + "test", + &create_model_value(value_type, true, Cardinality::One), + &ElementValue::Null, + &sk, + IvProvider::Random, + ) + ); + } + } + + #[test] + fn encrypt_value_convert_unencrypted_date_to_db_type() { + let model_value = create_model_value(ValueType::Date, false, Cardinality::One); + let value = ElementValue::Date(DateTime::from_system_time(SystemTime::now())); + + let mapped_value = + EntityFacade::encrypt_value("test", &model_value, &value, &None, IvProvider::Random); + + assert_eq!(value, mapped_value.unwrap()); + } + + #[test] + fn encrypt_value_convert_unencrypted_bytes_to_db_type() { + let model_value = create_model_value(ValueType::Bytes, false, Cardinality::One); + let value = ElementValue::Bytes(b"test".to_vec()); + + let mapped_value = + EntityFacade::encrypt_value("test", &model_value, &value, &None, IvProvider::Random); + + assert_eq!(value, mapped_value.unwrap()); + } + + #[test] + fn encrypt_value_convert_unencrypted_boolean_to_db_type() { + let model_value = create_model_value(ValueType::Boolean, false, Cardinality::One); + + let true_value = ElementValue::Bool(true); + assert_eq!( + ElementValue::Bool(true), + EntityFacade::encrypt_value( + "test", + &model_value, + &true_value, + &None, + IvProvider::Random, + ) + .unwrap() + ); + + let false_value = ElementValue::Bool(false); + assert_eq!( + false_value, + EntityFacade::encrypt_value( + "test", + &model_value, + &false_value, + &None, + IvProvider::Random, + ) + .unwrap() + ); + } + + #[test] + fn encrypt_value_convert_unencrypted_string_to_db_type() { + let model_value = create_model_value(ValueType::String, false, Cardinality::One); + let randomizer_facade = &RandomizerFacade::from_core(rand_core::OsRng); + let value = ElementValue::String("hello".to_string()); + + let mapped_value = + EntityFacade::encrypt_value("test", &model_value, &value, &None, IvProvider::Random); + assert_eq!(value, mapped_value.unwrap()); + } + + #[test] + fn encrypt_value_convert_unencrypted_number_to_db_type() { + let model_value = create_model_value(ValueType::Number, false, Cardinality::One); + let randomizer_facade = &RandomizerFacade::from_core(rand_core::OsRng); + let value = ElementValue::Number(100); + + let mapped_value = + EntityFacade::encrypt_value("test", &model_value, &value, &None, IvProvider::Random); + assert_eq!(value, mapped_value.unwrap()); + } + + #[test] + fn encrypt_value_convert_unencrypted_compressed_string_to_db_type() { + let model_value = create_model_value(ValueType::CompressedString, false, Cardinality::One); + let randomizer_facade = &RandomizerFacade::from_core(rand_core::OsRng); + let value = ElementValue::String("tutanota".to_string()); + + let mapped_value = + EntityFacade::encrypt_value("test", &model_value, &value, &None, IvProvider::Random); + assert_eq!(value, mapped_value.unwrap()); + } + + #[test] + fn encrypt_instance() { + let sk = GenericAesKey::Aes256(Aes256Key::from_bytes(KNOWN_SK.as_slice()).unwrap()); + let owner_enc_session_key = [0, 1, 2]; + let iv = Iv::from_bytes(&rand::random::<[u8; 16]>()).unwrap(); + let type_model_provider = Arc::new(init_type_model_provider()); + + let json_serializer = JsonSerializer::new(type_model_provider.clone()); + let type_ref = Mail::type_ref(); + let type_model = type_model_provider + .get_type_model(type_ref.app, type_ref.type_) + .unwrap(); + + let entity_facade = EntityFacade::new(Arc::clone(&type_model_provider)); + + let (mut expected_encrypted_mail, raw_mail) = generate_email_entity( + &sk, + &iv, + true, + String::from("Hello, world!"), + String::from("Hanover"), + String::from("Munich"), + ); + + // remove finalIvs for easy comparision + { + expected_encrypted_mail + .remove(&"_finalIvs".to_string()) + .unwrap(); + expected_encrypted_mail + .get_mut("sender") + .unwrap() + .assert_dict_mut_ref() + .remove("_finalIvs") + .unwrap(); + expected_encrypted_mail + .get_mut("firstRecipient") + .unwrap() + .assert_dict_mut_ref() + .remove("_finalIvs") + .unwrap(); + } + + let mut encrypted_mail = entity_facade.encrypt_and_map_to_literal_with_iv( + type_model, + &raw_mail, + Some(sk.clone()), + IvProvider::Fixed(iv.clone()), + ); + + assert_eq!(Ok(expected_encrypted_mail), encrypted_mail); + + // verify every data is preserved as is after decryption + { + let mut original_mail = raw_mail; + let mut encrypted_mail = encrypted_mail.unwrap(); + let instance_mapper = InstanceMapper::new(); + + let mut decrypted_mail = entity_facade + .decrypt_and_map( + type_model, + encrypted_mail.clone(), + ResolvedSessionKey { + session_key: sk.clone(), + owner_enc_session_key: owner_enc_session_key.to_vec(), + }, + ) + .unwrap(); + + // compare all the _finalIvs are initialised with expectedIV + // for simplicity in comparison remove them as well( original_mail don't have _finalIvs ) + verify_final_ivs_and_clear(&iv, &mut decrypted_mail); + + assert_eq!( + Some(&ElementValue::Bytes(owner_enc_session_key.to_vec())), + decrypted_mail.get(&"_ownerEncSessionKey".to_string()), + ); + decrypted_mail.insert("_ownerEncSessionKey".to_string(), ElementValue::Null); + + assert_eq!( + Some(ElementValue::Dict(HashMap::new())), + decrypted_mail.remove("_errors") + ); + + // comparison with sorted fields. only for easy for debugging + assert_eq!( + map_to_string(&original_mail), + map_to_string(&decrypted_mail) + ); + assert_eq!(original_mail, decrypted_mail); + } + } + + #[test] + fn encrypt_unencrypted_to_db_literal() { + let type_model_provider = Arc::new(init_type_model_provider()); + let json_serializer = JsonSerializer::new(type_model_provider.clone()); + let entity_facade = EntityFacade::new(Arc::clone(&type_model_provider)); + let type_ref = CustomerAccountTerminationRequest::type_ref(); + let type_model = type_model_provider + .get_type_model(type_ref.app, type_ref.type_) + .unwrap(); + let sk = GenericAesKey::from_bytes(rand::random::<[u8; 32]>().as_slice()).unwrap(); + + let dummy_date = DateTime::from_system_time(SystemTime::now()); + let instance: RawEntity = collection! { + "_format" => JsonElement::String("0".to_string()), + "_id" => JsonElement::Array(vec![JsonElement::String("O1RT2Dj--3-0".to_string()); 2]), + "_ownerGroup" => JsonElement::Null, + "_permissions" => JsonElement::String("O2TT2Aj--2-1".to_string()), + "terminationDate" => JsonElement::String(dummy_date.as_millis().to_string()), + "terminationRequestDate" => JsonElement::String(dummy_date.as_millis().to_string()), + "customer" => JsonElement::String("customId".to_string()), + }; + let instance = json_serializer.parse(&type_ref, instance).unwrap(); + + let encrypted_instance = + entity_facade.encrypt_and_map_to_literal(&type_model, &instance, Some(sk.clone())); + + // unencrypted value should be kept as-is + assert_eq!(Ok(instance), encrypted_instance); + } + + #[test] + fn encryption_final_ivs_will_be_reused() { + let type_model_provider = Arc::new(init_type_model_provider()); + let json_serializer = JsonSerializer::new(type_model_provider.clone()); + let entity_facade = EntityFacade::new(Arc::clone(&type_model_provider)); + let type_ref = Mail::type_ref(); + let type_model = type_model_provider + .get_type_model(type_ref.app, type_ref.type_) + .unwrap(); + let sk = GenericAesKey::from_bytes(rand::random::<[u8; 32]>().as_slice()).unwrap(); + let original_iv = Iv::from_bytes(&rand::random::<[u8; 16]>()).unwrap(); + let new_iv = Iv::from_bytes(&rand::random::<[u8; 16]>()).unwrap(); + + // use two seperate iv + assert_ne!(original_iv.get_inner(), new_iv.get_inner()); + + let (mut encrypted_mail, mut unencrypted_mail) = generate_email_entity( + &sk, + &original_iv, + true, + String::from("Hello, world!"), + String::from("Hanover"), + String::from("Munich"), + ); + + // set separate finalIv for some field + let final_iv_for_subject = [( + "subject".to_string(), + ElementValue::Bytes(new_iv.get_inner().to_vec()), + )] + .into_iter() + .collect::>(); + + unencrypted_mail.insert( + "_finalIvs".to_string(), + ElementValue::Dict(final_iv_for_subject), + ); + + let mut encrypted_mail = entity_facade + .encrypt_and_map_to_literal_with_iv( + type_model, + &unencrypted_mail, + Some(sk.clone()), + IvProvider::Fixed(original_iv.clone()), + ) + .unwrap(); + + let encrypted_subject = encrypted_mail.get("subject").unwrap(); + let subject_and_iv = sk + .decrypt_data_and_iv(&encrypted_subject.assert_bytes()) + .unwrap(); + + assert_eq!( + Ok("Hello, world!".to_string()), + String::from_utf8(subject_and_iv.data) + ); + assert_eq!(new_iv.get_inner(), &subject_and_iv.iv); + + // other fields should be encrypted with origin_iv + let encrypted_recipient_name = encrypted_mail + .get("firstRecipient") + .unwrap() + .assert_dict() + .get("name") + .unwrap() + .assert_bytes(); + let recipient_and_iv = sk.decrypt_data_and_iv(&encrypted_recipient_name).unwrap(); + assert_eq!(original_iv.get_inner().to_vec(), recipient_and_iv.iv) + } + + #[test] + #[ignore = "todo: Right now we will anyway try to encrypt the default value even for final fields.\ + This is however not intended. We skip the implementation because we did not need it for service call?"] + fn empty_final_iv_and_default_value_should_be_preserved() { + let type_model_provider = Arc::new(init_type_model_provider()); + let json_serializer = JsonSerializer::new(type_model_provider.clone()); + let entity_facade = EntityFacade::new(Arc::clone(&type_model_provider)); + let type_ref = Mail::type_ref(); + let type_model = type_model_provider + .get_type_model(type_ref.app, type_ref.type_) + .unwrap(); + let sk = GenericAesKey::from_bytes(rand::random::<[u8; 32]>().as_slice()).unwrap(); + let iv = Iv::from_bytes(&rand::random::<[u8; 16]>()).unwrap(); + + let default_subject = String::from(""); + let (mut encrypted_mail, mut unencrypted_mail) = generate_email_entity( + &sk, + &iv, + true, + default_subject.clone(), + String::from("Hanover"), + String::from("Munich"), + ); + + let mut encrypted_mail = entity_facade + .encrypt_and_map_to_literal_with_iv( + type_model, + &unencrypted_mail, + Some(sk.clone()), + IvProvider::Fixed(iv.clone()), + ) + .unwrap(); + + let encrypted_subject = encrypted_mail.get("subject").unwrap().assert_bytes(); + assert_eq!(default_subject.as_bytes(), encrypted_subject.as_slice()); + } + + fn map_to_string(map: &HashMap) -> String { + let mut out = String::new(); + let sorted_map: BTreeMap = map.clone().into_iter().collect(); + for (key, value) in sorted_map.iter() { + match value { + ElementValue::Dict(aggregate) => { + out.push_str(&format!("{}: {}\n", key, map_to_string(aggregate))) + }, + _ => out.push_str(&format!("{}: {:?}\n", key, value)), + } + } + out + } + + fn verify_final_ivs_and_clear(iv: &Iv, instance: &mut ParsedEntity) { + for (name, value) in instance.iter_mut() { + match value { + ElementValue::Dict(value_map) if name == "_finalIvs" => { + for (_n, actual_iv) in value_map.iter() { + assert_eq!(iv.get_inner(), actual_iv.assert_bytes().as_slice()); + } + value_map.clear(); + }, + + ElementValue::Dict(value_map) => verify_final_ivs_and_clear(iv, value_map), + _ => {}, + } + } + } + fn make_json_entity() -> RawEntity { collection! { "sentDate"=> JsonElement::Null, @@ -585,4 +1472,18 @@ mod tests { ), "sets"=> JsonElement::Array(vec![]),} } + + fn create_model_value( + value_type: ValueType, + encrypted: bool, + cardinality: Cardinality, + ) -> ModelValue { + ModelValue { + id: 426, + value_type, + cardinality, + is_final: true, + encrypted, + } + } } diff --git a/tuta-sdk/rust/sdk/src/entities/mod.rs b/tuta-sdk/rust/sdk/src/entities/mod.rs index 1d6e17180250..fd9392988f2f 100644 --- a/tuta-sdk/rust/sdk/src/entities/mod.rs +++ b/tuta-sdk/rust/sdk/src/entities/mod.rs @@ -9,15 +9,15 @@ use crate::generated_id::GeneratedId; pub use crate::IdTuple; use crate::TypeRef; -pub(crate) mod accounting; -pub(crate) mod base; -pub(crate) mod entity_facade; -pub(crate) mod gossip; -pub(crate) mod monitor; -pub(crate) mod storage; -pub(crate) mod sys; -pub(crate) mod tutanota; -pub(crate) mod usage; +pub mod accounting; +pub mod base; +pub mod entity_facade; +pub mod gossip; +pub mod monitor; +pub mod storage; +pub mod sys; +pub mod tutanota; +pub mod usage; /// `'static` on trait bound is fine here because Entity does not contain any non-static references. /// See https://doc.rust-lang.org/rust-by-example/scope/lifetime/static_lifetime.html#trait-bound @@ -30,7 +30,7 @@ pub trait Entity: 'static { /// to the same exact value. For that we need to use the same initialization /// vector. /// FinalIv holds such an IV for a specific field. -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] #[serde(transparent)] pub struct FinalIv(#[serde(with = "serde_bytes")] Vec); diff --git a/tuta-sdk/rust/sdk/src/entities/monitor.rs b/tuta-sdk/rust/sdk/src/entities/monitor.rs index bdb789086b51..fcb946d0ad9a 100644 --- a/tuta-sdk/rust/sdk/src/entities/monitor.rs +++ b/tuta-sdk/rust/sdk/src/entities/monitor.rs @@ -2,7 +2,7 @@ use super::*; use serde::{Deserialize, Serialize}; -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ApprovalMail { pub _format: i64, pub _id: IdTuple, @@ -13,7 +13,6 @@ pub struct ApprovalMail { pub text: String, pub customer: Option, } - impl Entity for ApprovalMail { fn type_ref() -> TypeRef { TypeRef { @@ -23,13 +22,15 @@ impl Entity for ApprovalMail { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CounterValue { pub _id: CustomId, pub counterId: GeneratedId, pub value: i64, } - impl Entity for CounterValue { fn type_ref() -> TypeRef { TypeRef { @@ -39,7 +40,10 @@ impl Entity for CounterValue { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ErrorReportData { pub _id: CustomId, pub additionalInfo: String, @@ -52,7 +56,6 @@ pub struct ErrorReportData { pub userId: Option, pub userMessage: Option, } - impl Entity for ErrorReportData { fn type_ref() -> TypeRef { TypeRef { @@ -62,13 +65,15 @@ impl Entity for ErrorReportData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ErrorReportFile { pub _id: CustomId, pub content: String, pub name: String, } - impl Entity for ErrorReportFile { fn type_ref() -> TypeRef { TypeRef { @@ -78,14 +83,16 @@ impl Entity for ErrorReportFile { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ReadCounterData { pub _format: i64, pub columnName: Option, pub counterType: i64, pub rowName: String, } - impl Entity for ReadCounterData { fn type_ref() -> TypeRef { TypeRef { @@ -95,13 +102,15 @@ impl Entity for ReadCounterData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ReadCounterReturn { pub _format: i64, pub value: Option, pub counterValues: Vec, } - impl Entity for ReadCounterReturn { fn type_ref() -> TypeRef { TypeRef { @@ -111,13 +120,15 @@ impl Entity for ReadCounterReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ReportErrorIn { pub _format: i64, pub data: ErrorReportData, pub files: Vec, } - impl Entity for ReportErrorIn { fn type_ref() -> TypeRef { TypeRef { @@ -127,7 +138,10 @@ impl Entity for ReportErrorIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct WriteCounterData { pub _format: i64, pub column: GeneratedId, @@ -135,7 +149,6 @@ pub struct WriteCounterData { pub row: String, pub value: i64, } - impl Entity for WriteCounterData { fn type_ref() -> TypeRef { TypeRef { @@ -144,3 +157,6 @@ impl Entity for WriteCounterData { } } } + + + diff --git a/tuta-sdk/rust/sdk/src/entities/storage.rs b/tuta-sdk/rust/sdk/src/entities/storage.rs index 718a2295cd78..358d4020042d 100644 --- a/tuta-sdk/rust/sdk/src/entities/storage.rs +++ b/tuta-sdk/rust/sdk/src/entities/storage.rs @@ -2,14 +2,13 @@ use super::*; use serde::{Deserialize, Serialize}; -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BlobAccessTokenPostIn { pub _format: i64, pub archiveDataType: Option, pub read: Option, pub write: Option, } - impl Entity for BlobAccessTokenPostIn { fn type_ref() -> TypeRef { TypeRef { @@ -19,12 +18,14 @@ impl Entity for BlobAccessTokenPostIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BlobAccessTokenPostOut { pub _format: i64, pub blobAccessInfo: BlobServerAccessInfo, } - impl Entity for BlobAccessTokenPostOut { fn type_ref() -> TypeRef { TypeRef { @@ -34,7 +35,10 @@ impl Entity for BlobAccessTokenPostOut { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BlobArchiveRef { pub _format: i64, pub _id: IdTuple, @@ -42,7 +46,6 @@ pub struct BlobArchiveRef { pub _permissions: GeneratedId, pub archive: GeneratedId, } - impl Entity for BlobArchiveRef { fn type_ref() -> TypeRef { TypeRef { @@ -52,14 +55,16 @@ impl Entity for BlobArchiveRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BlobGetIn { pub _format: i64, pub archiveId: GeneratedId, pub blobId: Option, pub blobIds: Vec, } - impl Entity for BlobGetIn { fn type_ref() -> TypeRef { TypeRef { @@ -69,12 +74,14 @@ impl Entity for BlobGetIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BlobId { pub _id: CustomId, pub blobId: GeneratedId, } - impl Entity for BlobId { fn type_ref() -> TypeRef { TypeRef { @@ -84,12 +91,14 @@ impl Entity for BlobId { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BlobPostOut { pub _format: i64, pub blobReferenceToken: String, } - impl Entity for BlobPostOut { fn type_ref() -> TypeRef { TypeRef { @@ -99,14 +108,16 @@ impl Entity for BlobPostOut { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BlobReadData { pub _id: CustomId, pub archiveId: GeneratedId, pub instanceListId: Option, pub instanceIds: Vec, } - impl Entity for BlobReadData { fn type_ref() -> TypeRef { TypeRef { @@ -116,7 +127,10 @@ impl Entity for BlobReadData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BlobReferenceDeleteIn { pub _format: i64, pub archiveDataType: i64, @@ -124,7 +138,6 @@ pub struct BlobReferenceDeleteIn { pub instanceListId: Option, pub blobs: Vec, } - impl Entity for BlobReferenceDeleteIn { fn type_ref() -> TypeRef { TypeRef { @@ -134,7 +147,10 @@ impl Entity for BlobReferenceDeleteIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BlobReferencePutIn { pub _format: i64, pub archiveDataType: i64, @@ -142,7 +158,6 @@ pub struct BlobReferencePutIn { pub instanceListId: Option, pub referenceTokens: Vec, } - impl Entity for BlobReferencePutIn { fn type_ref() -> TypeRef { TypeRef { @@ -152,14 +167,16 @@ impl Entity for BlobReferencePutIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BlobServerAccessInfo { pub _id: CustomId, pub blobAccessToken: String, pub expires: DateTime, pub servers: Vec, } - impl Entity for BlobServerAccessInfo { fn type_ref() -> TypeRef { TypeRef { @@ -169,12 +186,14 @@ impl Entity for BlobServerAccessInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BlobServerUrl { pub _id: CustomId, pub url: String, } - impl Entity for BlobServerUrl { fn type_ref() -> TypeRef { TypeRef { @@ -184,12 +203,14 @@ impl Entity for BlobServerUrl { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BlobWriteData { pub _id: CustomId, pub archiveOwnerGroup: GeneratedId, } - impl Entity for BlobWriteData { fn type_ref() -> TypeRef { TypeRef { @@ -199,12 +220,14 @@ impl Entity for BlobWriteData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct InstanceId { pub _id: CustomId, pub instanceId: Option, } - impl Entity for InstanceId { fn type_ref() -> TypeRef { TypeRef { @@ -213,3 +236,6 @@ impl Entity for InstanceId { } } } + + + diff --git a/tuta-sdk/rust/sdk/src/entities/sys.rs b/tuta-sdk/rust/sdk/src/entities/sys.rs index aa37018a7564..e125ad7a01fd 100644 --- a/tuta-sdk/rust/sdk/src/entities/sys.rs +++ b/tuta-sdk/rust/sdk/src/entities/sys.rs @@ -2,7 +2,7 @@ use super::*; use serde::{Deserialize, Serialize}; -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AccountingInfo { pub _format: i64, pub _id: GeneratedId, @@ -30,7 +30,6 @@ pub struct AccountingInfo { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for AccountingInfo { fn type_ref() -> TypeRef { TypeRef { @@ -40,7 +39,10 @@ impl Entity for AccountingInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AdminGroupKeyAuthenticationData { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -48,7 +50,6 @@ pub struct AdminGroupKeyAuthenticationData { pub version: i64, pub userGroup: GeneratedId, } - impl Entity for AdminGroupKeyAuthenticationData { fn type_ref() -> TypeRef { TypeRef { @@ -58,14 +59,16 @@ impl Entity for AdminGroupKeyAuthenticationData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AdminGroupKeyRotationPostIn { pub _format: i64, pub adminGroupKeyAuthenticationDataList: Vec, pub adminGroupKeyData: GroupKeyRotationData, pub userGroupKeyData: UserGroupKeyRotationData, } - impl Entity for AdminGroupKeyRotationPostIn { fn type_ref() -> TypeRef { TypeRef { @@ -75,12 +78,14 @@ impl Entity for AdminGroupKeyRotationPostIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AdministratedGroupsRef { pub _id: CustomId, pub items: GeneratedId, } - impl Entity for AdministratedGroupsRef { fn type_ref() -> TypeRef { TypeRef { @@ -90,7 +95,10 @@ impl Entity for AdministratedGroupsRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AffiliatePartnerKpiMonthSummary { pub _id: CustomId, pub commission: i64, @@ -100,7 +108,6 @@ pub struct AffiliatePartnerKpiMonthSummary { pub totalFree: i64, pub totalPaid: i64, } - impl Entity for AffiliatePartnerKpiMonthSummary { fn type_ref() -> TypeRef { TypeRef { @@ -110,7 +117,10 @@ impl Entity for AffiliatePartnerKpiMonthSummary { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AffiliatePartnerKpiServiceGetOut { pub _format: i64, pub accumulatedCommission: i64, @@ -118,7 +128,6 @@ pub struct AffiliatePartnerKpiServiceGetOut { pub promotionId: String, pub kpis: Vec, } - impl Entity for AffiliatePartnerKpiServiceGetOut { fn type_ref() -> TypeRef { TypeRef { @@ -128,7 +137,10 @@ impl Entity for AffiliatePartnerKpiServiceGetOut { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AlarmInfo { pub _id: CustomId, pub alarmIdentifier: String, @@ -136,7 +148,6 @@ pub struct AlarmInfo { pub calendarRef: CalendarEventRef, pub _finalIvs: HashMap, } - impl Entity for AlarmInfo { fn type_ref() -> TypeRef { TypeRef { @@ -146,7 +157,10 @@ impl Entity for AlarmInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AlarmNotification { pub _id: CustomId, pub eventEnd: DateTime, @@ -159,7 +173,6 @@ pub struct AlarmNotification { pub user: GeneratedId, pub _finalIvs: HashMap, } - impl Entity for AlarmNotification { fn type_ref() -> TypeRef { TypeRef { @@ -169,14 +182,16 @@ impl Entity for AlarmNotification { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AlarmServicePost { pub _format: i64, pub alarmNotifications: Vec, pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for AlarmServicePost { fn type_ref() -> TypeRef { TypeRef { @@ -186,12 +201,14 @@ impl Entity for AlarmServicePost { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ArchiveRef { pub _id: CustomId, pub archiveId: GeneratedId, } - impl Entity for ArchiveRef { fn type_ref() -> TypeRef { TypeRef { @@ -201,7 +218,10 @@ impl Entity for ArchiveRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ArchiveType { pub _id: CustomId, pub active: ArchiveRef, @@ -209,7 +229,6 @@ pub struct ArchiveType { #[serde(rename = "type")] pub r#type: TypeInfo, } - impl Entity for ArchiveType { fn type_ref() -> TypeRef { TypeRef { @@ -219,7 +238,10 @@ impl Entity for ArchiveType { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AuditLogEntry { pub _format: i64, pub _id: IdTuple, @@ -238,7 +260,6 @@ pub struct AuditLogEntry { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for AuditLogEntry { fn type_ref() -> TypeRef { TypeRef { @@ -248,12 +269,14 @@ impl Entity for AuditLogEntry { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AuditLogRef { pub _id: CustomId, pub items: GeneratedId, } - impl Entity for AuditLogRef { fn type_ref() -> TypeRef { TypeRef { @@ -263,7 +286,10 @@ impl Entity for AuditLogRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AuthenticatedDevice { pub _id: CustomId, pub authType: i64, @@ -271,7 +297,6 @@ pub struct AuthenticatedDevice { pub deviceKey: Vec, pub deviceToken: String, } - impl Entity for AuthenticatedDevice { fn type_ref() -> TypeRef { TypeRef { @@ -281,7 +306,10 @@ impl Entity for AuthenticatedDevice { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Authentication { pub _id: CustomId, pub accessToken: Option, @@ -289,7 +317,6 @@ pub struct Authentication { pub externalAuthToken: Option, pub userId: GeneratedId, } - impl Entity for Authentication { fn type_ref() -> TypeRef { TypeRef { @@ -299,12 +326,14 @@ impl Entity for Authentication { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AutoLoginDataDelete { pub _format: i64, pub deviceToken: String, } - impl Entity for AutoLoginDataDelete { fn type_ref() -> TypeRef { TypeRef { @@ -314,13 +343,15 @@ impl Entity for AutoLoginDataDelete { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AutoLoginDataGet { pub _format: i64, pub deviceToken: String, pub userId: GeneratedId, } - impl Entity for AutoLoginDataGet { fn type_ref() -> TypeRef { TypeRef { @@ -330,13 +361,15 @@ impl Entity for AutoLoginDataGet { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AutoLoginDataReturn { pub _format: i64, #[serde(with = "serde_bytes")] pub deviceKey: Vec, } - impl Entity for AutoLoginDataReturn { fn type_ref() -> TypeRef { TypeRef { @@ -346,12 +379,14 @@ impl Entity for AutoLoginDataReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AutoLoginPostReturn { pub _format: i64, pub deviceToken: String, } - impl Entity for AutoLoginPostReturn { fn type_ref() -> TypeRef { TypeRef { @@ -361,14 +396,16 @@ impl Entity for AutoLoginPostReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Blob { pub _id: CustomId, pub archiveId: GeneratedId, pub blobId: GeneratedId, pub size: i64, } - impl Entity for Blob { fn type_ref() -> TypeRef { TypeRef { @@ -378,12 +415,14 @@ impl Entity for Blob { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BlobReferenceTokenWrapper { pub _id: CustomId, pub blobReferenceToken: String, } - impl Entity for BlobReferenceTokenWrapper { fn type_ref() -> TypeRef { TypeRef { @@ -393,7 +432,10 @@ impl Entity for BlobReferenceTokenWrapper { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Booking { pub _area: i64, pub _format: i64, @@ -408,7 +450,6 @@ pub struct Booking { pub paymentMonths: i64, pub items: Vec, } - impl Entity for Booking { fn type_ref() -> TypeRef { TypeRef { @@ -418,7 +459,10 @@ impl Entity for Booking { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BookingItem { pub _id: CustomId, pub currentCount: i64, @@ -429,7 +473,6 @@ pub struct BookingItem { pub priceType: i64, pub totalInvoicedCount: i64, } - impl Entity for BookingItem { fn type_ref() -> TypeRef { TypeRef { @@ -439,12 +482,14 @@ impl Entity for BookingItem { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BookingsRef { pub _id: CustomId, pub items: GeneratedId, } - impl Entity for BookingsRef { fn type_ref() -> TypeRef { TypeRef { @@ -454,12 +499,14 @@ impl Entity for BookingsRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BootstrapFeature { pub _id: CustomId, pub feature: i64, } - impl Entity for BootstrapFeature { fn type_ref() -> TypeRef { TypeRef { @@ -469,14 +516,16 @@ impl Entity for BootstrapFeature { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Braintree3ds2Request { pub _id: CustomId, pub bin: String, pub clientToken: String, pub nonce: String, } - impl Entity for Braintree3ds2Request { fn type_ref() -> TypeRef { TypeRef { @@ -486,13 +535,15 @@ impl Entity for Braintree3ds2Request { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Braintree3ds2Response { pub _id: CustomId, pub clientToken: String, pub nonce: String, } - impl Entity for Braintree3ds2Response { fn type_ref() -> TypeRef { TypeRef { @@ -502,7 +553,10 @@ impl Entity for Braintree3ds2Response { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BrandingDomainData { pub _format: i64, pub domain: String, @@ -515,7 +569,6 @@ pub struct BrandingDomainData { pub systemAdminPubKeyVersion: i64, pub systemAdminPublicProtocolVersion: i64, } - impl Entity for BrandingDomainData { fn type_ref() -> TypeRef { TypeRef { @@ -525,12 +578,14 @@ impl Entity for BrandingDomainData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BrandingDomainDeleteData { pub _format: i64, pub domain: String, } - impl Entity for BrandingDomainDeleteData { fn type_ref() -> TypeRef { TypeRef { @@ -540,12 +595,14 @@ impl Entity for BrandingDomainDeleteData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BrandingDomainGetReturn { pub _format: i64, pub certificateInfo: Option, } - impl Entity for BrandingDomainGetReturn { fn type_ref() -> TypeRef { TypeRef { @@ -555,12 +612,14 @@ impl Entity for BrandingDomainGetReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Bucket { pub _id: CustomId, pub bucketPermissions: GeneratedId, } - impl Entity for Bucket { fn type_ref() -> TypeRef { TypeRef { @@ -570,7 +629,10 @@ impl Entity for Bucket { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BucketKey { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -583,7 +645,6 @@ pub struct BucketKey { pub bucketEncSessionKeys: Vec, pub keyGroup: Option, } - impl Entity for BucketKey { fn type_ref() -> TypeRef { TypeRef { @@ -593,7 +654,10 @@ impl Entity for BucketKey { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct BucketPermission { pub _format: i64, pub _id: IdTuple, @@ -614,7 +678,6 @@ pub struct BucketPermission { pub r#type: i64, pub group: GeneratedId, } - impl Entity for BucketPermission { fn type_ref() -> TypeRef { TypeRef { @@ -624,13 +687,15 @@ impl Entity for BucketPermission { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CalendarEventRef { pub _id: CustomId, pub elementId: CustomId, pub listId: GeneratedId, } - impl Entity for CalendarEventRef { fn type_ref() -> TypeRef { TypeRef { @@ -640,7 +705,10 @@ impl Entity for CalendarEventRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CertificateInfo { pub _id: CustomId, pub expiryDate: Option, @@ -649,7 +717,6 @@ pub struct CertificateInfo { pub r#type: i64, pub certificate: Option, } - impl Entity for CertificateInfo { fn type_ref() -> TypeRef { TypeRef { @@ -659,7 +726,10 @@ impl Entity for CertificateInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Challenge { pub _id: CustomId, #[serde(rename = "type")] @@ -667,7 +737,6 @@ pub struct Challenge { pub otp: Option, pub u2f: Option, } - impl Entity for Challenge { fn type_ref() -> TypeRef { TypeRef { @@ -677,7 +746,10 @@ impl Entity for Challenge { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ChangeKdfPostIn { pub _format: i64, pub kdfVersion: i64, @@ -691,7 +763,6 @@ pub struct ChangeKdfPostIn { #[serde(with = "serde_bytes")] pub verifier: Vec, } - impl Entity for ChangeKdfPostIn { fn type_ref() -> TypeRef { TypeRef { @@ -701,7 +772,10 @@ impl Entity for ChangeKdfPostIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ChangePasswordPostIn { pub _format: i64, pub code: Option, @@ -718,7 +792,6 @@ pub struct ChangePasswordPostIn { #[serde(with = "serde_bytes")] pub verifier: Vec, } - impl Entity for ChangePasswordPostIn { fn type_ref() -> TypeRef { TypeRef { @@ -728,14 +801,16 @@ impl Entity for ChangePasswordPostIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Chat { pub _id: CustomId, pub recipient: GeneratedId, pub sender: GeneratedId, pub text: String, } - impl Entity for Chat { fn type_ref() -> TypeRef { TypeRef { @@ -745,13 +820,15 @@ impl Entity for Chat { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CloseSessionServicePost { pub _format: i64, pub accessToken: String, pub sessionId: IdTuple, } - impl Entity for CloseSessionServicePost { fn type_ref() -> TypeRef { TypeRef { @@ -761,14 +838,16 @@ impl Entity for CloseSessionServicePost { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CreateCustomerServerPropertiesData { pub _format: i64, #[serde(with = "serde_bytes")] pub adminGroupEncSessionKey: Vec, pub adminGroupKeyVersion: i64, } - impl Entity for CreateCustomerServerPropertiesData { fn type_ref() -> TypeRef { TypeRef { @@ -778,12 +857,14 @@ impl Entity for CreateCustomerServerPropertiesData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CreateCustomerServerPropertiesReturn { pub _format: i64, pub id: GeneratedId, } - impl Entity for CreateCustomerServerPropertiesReturn { fn type_ref() -> TypeRef { TypeRef { @@ -793,7 +874,10 @@ impl Entity for CreateCustomerServerPropertiesReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CreateSessionData { pub _format: i64, #[serde(with = "serde_bytes")] @@ -805,7 +889,6 @@ pub struct CreateSessionData { pub recoverCodeVerifier: Option, pub user: Option, } - impl Entity for CreateSessionData { fn type_ref() -> TypeRef { TypeRef { @@ -815,14 +898,16 @@ impl Entity for CreateSessionData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CreateSessionReturn { pub _format: i64, pub accessToken: String, pub challenges: Vec, pub user: GeneratedId, } - impl Entity for CreateSessionReturn { fn type_ref() -> TypeRef { TypeRef { @@ -832,7 +917,10 @@ impl Entity for CreateSessionReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CreditCard { pub _id: CustomId, pub cardHolderName: String, @@ -842,7 +930,6 @@ pub struct CreditCard { pub number: String, pub _finalIvs: HashMap, } - impl Entity for CreditCard { fn type_ref() -> TypeRef { TypeRef { @@ -852,13 +939,15 @@ impl Entity for CreditCard { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CustomDomainCheckGetIn { pub _format: i64, pub domain: String, pub customer: Option, } - impl Entity for CustomDomainCheckGetIn { fn type_ref() -> TypeRef { TypeRef { @@ -868,7 +957,10 @@ impl Entity for CustomDomainCheckGetIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CustomDomainCheckGetOut { pub _format: i64, pub checkResult: i64, @@ -876,7 +968,6 @@ pub struct CustomDomainCheckGetOut { pub missingRecords: Vec, pub requiredRecords: Vec, } - impl Entity for CustomDomainCheckGetOut { fn type_ref() -> TypeRef { TypeRef { @@ -886,13 +977,15 @@ impl Entity for CustomDomainCheckGetOut { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CustomDomainData { pub _format: i64, pub domain: String, pub catchAllMailGroup: Option, } - impl Entity for CustomDomainData { fn type_ref() -> TypeRef { TypeRef { @@ -902,13 +995,15 @@ impl Entity for CustomDomainData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CustomDomainReturn { pub _format: i64, pub validationResult: i64, pub invalidDnsRecords: Vec, } - impl Entity for CustomDomainReturn { fn type_ref() -> TypeRef { TypeRef { @@ -918,7 +1013,10 @@ impl Entity for CustomDomainReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Customer { pub _format: i64, pub _id: GeneratedId, @@ -947,7 +1045,6 @@ pub struct Customer { pub whitelabelChildren: Option, pub whitelabelParent: Option, } - impl Entity for Customer { fn type_ref() -> TypeRef { TypeRef { @@ -957,13 +1054,15 @@ impl Entity for Customer { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CustomerAccountTerminationPostIn { pub _format: i64, pub terminationDate: Option, pub surveyData: Option, } - impl Entity for CustomerAccountTerminationPostIn { fn type_ref() -> TypeRef { TypeRef { @@ -973,12 +1072,14 @@ impl Entity for CustomerAccountTerminationPostIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CustomerAccountTerminationPostOut { pub _format: i64, pub terminationRequest: IdTuple, } - impl Entity for CustomerAccountTerminationPostOut { fn type_ref() -> TypeRef { TypeRef { @@ -988,7 +1089,10 @@ impl Entity for CustomerAccountTerminationPostOut { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CustomerAccountTerminationRequest { pub _format: i64, pub _id: IdTuple, @@ -998,7 +1102,6 @@ pub struct CustomerAccountTerminationRequest { pub terminationRequestDate: DateTime, pub customer: GeneratedId, } - impl Entity for CustomerAccountTerminationRequest { fn type_ref() -> TypeRef { TypeRef { @@ -1008,7 +1111,10 @@ impl Entity for CustomerAccountTerminationRequest { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CustomerInfo { pub _format: i64, pub _id: IdTuple, @@ -1043,7 +1149,6 @@ pub struct CustomerInfo { pub takeoverCustomer: Option, pub terminationRequest: Option, } - impl Entity for CustomerInfo { fn type_ref() -> TypeRef { TypeRef { @@ -1053,7 +1158,10 @@ impl Entity for CustomerInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CustomerProperties { pub _format: i64, pub _id: GeneratedId, @@ -1066,7 +1174,6 @@ pub struct CustomerProperties { pub notificationMailTemplates: Vec, pub smallLogo: Option, } - impl Entity for CustomerProperties { fn type_ref() -> TypeRef { TypeRef { @@ -1076,7 +1183,10 @@ impl Entity for CustomerProperties { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CustomerServerProperties { pub _format: i64, pub _id: GeneratedId, @@ -1094,7 +1204,6 @@ pub struct CustomerServerProperties { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for CustomerServerProperties { fn type_ref() -> TypeRef { TypeRef { @@ -1104,13 +1213,15 @@ impl Entity for CustomerServerProperties { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DateWrapper { pub _id: CustomId, pub date: DateTime, pub _finalIvs: HashMap, } - impl Entity for DateWrapper { fn type_ref() -> TypeRef { TypeRef { @@ -1120,12 +1231,14 @@ impl Entity for DateWrapper { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DebitServicePutData { pub _format: i64, pub invoice: Option, } - impl Entity for DebitServicePutData { fn type_ref() -> TypeRef { TypeRef { @@ -1135,7 +1248,10 @@ impl Entity for DebitServicePutData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DeleteCustomerData { pub _format: i64, #[serde(with = "serde_bytes")] @@ -1146,7 +1262,6 @@ pub struct DeleteCustomerData { pub customer: GeneratedId, pub surveyData: Option, } - impl Entity for DeleteCustomerData { fn type_ref() -> TypeRef { TypeRef { @@ -1156,7 +1271,10 @@ impl Entity for DeleteCustomerData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DnsRecord { pub _id: CustomId, pub subdomain: Option, @@ -1164,7 +1282,6 @@ pub struct DnsRecord { pub r#type: i64, pub value: String, } - impl Entity for DnsRecord { fn type_ref() -> TypeRef { TypeRef { @@ -1174,7 +1291,10 @@ impl Entity for DnsRecord { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DomainInfo { pub _id: CustomId, pub domain: String, @@ -1182,7 +1302,6 @@ pub struct DomainInfo { pub catchAllMailGroup: Option, pub whitelabelConfig: Option, } - impl Entity for DomainInfo { fn type_ref() -> TypeRef { TypeRef { @@ -1192,12 +1311,14 @@ impl Entity for DomainInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DomainMailAddressAvailabilityData { pub _format: i64, pub mailAddress: String, } - impl Entity for DomainMailAddressAvailabilityData { fn type_ref() -> TypeRef { TypeRef { @@ -1207,12 +1328,14 @@ impl Entity for DomainMailAddressAvailabilityData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DomainMailAddressAvailabilityReturn { pub _format: i64, pub available: bool, } - impl Entity for DomainMailAddressAvailabilityReturn { fn type_ref() -> TypeRef { TypeRef { @@ -1222,12 +1345,14 @@ impl Entity for DomainMailAddressAvailabilityReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DomainsRef { pub _id: CustomId, pub items: GeneratedId, } - impl Entity for DomainsRef { fn type_ref() -> TypeRef { TypeRef { @@ -1237,7 +1362,10 @@ impl Entity for DomainsRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct EmailSenderListElement { pub _id: CustomId, pub field: i64, @@ -1247,7 +1375,6 @@ pub struct EmailSenderListElement { pub value: String, pub _finalIvs: HashMap, } - impl Entity for EmailSenderListElement { fn type_ref() -> TypeRef { TypeRef { @@ -1257,7 +1384,10 @@ impl Entity for EmailSenderListElement { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct EntityEventBatch { pub _format: i64, pub _id: IdTuple, @@ -1265,7 +1395,6 @@ pub struct EntityEventBatch { pub _permissions: GeneratedId, pub events: Vec, } - impl Entity for EntityEventBatch { fn type_ref() -> TypeRef { TypeRef { @@ -1275,7 +1404,10 @@ impl Entity for EntityEventBatch { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct EntityUpdate { pub _id: CustomId, pub application: String, @@ -1285,7 +1417,6 @@ pub struct EntityUpdate { #[serde(rename = "type")] pub r#type: String, } - impl Entity for EntityUpdate { fn type_ref() -> TypeRef { TypeRef { @@ -1295,14 +1426,16 @@ impl Entity for EntityUpdate { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SysException { pub _id: CustomId, pub msg: String, #[serde(rename = "type")] pub r#type: String, } - impl Entity for SysException { fn type_ref() -> TypeRef { TypeRef { @@ -1312,7 +1445,10 @@ impl Entity for SysException { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ExternalPropertiesReturn { pub _format: i64, pub accountType: i64, @@ -1320,7 +1456,6 @@ pub struct ExternalPropertiesReturn { pub bigLogo: Option, pub smallLogo: Option, } - impl Entity for ExternalPropertiesReturn { fn type_ref() -> TypeRef { TypeRef { @@ -1330,7 +1465,10 @@ impl Entity for ExternalPropertiesReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ExternalUserReference { pub _format: i64, pub _id: IdTuple, @@ -1339,7 +1477,6 @@ pub struct ExternalUserReference { pub user: GeneratedId, pub userGroup: GeneratedId, } - impl Entity for ExternalUserReference { fn type_ref() -> TypeRef { TypeRef { @@ -1349,12 +1486,14 @@ impl Entity for ExternalUserReference { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Feature { pub _id: CustomId, pub feature: i64, } - impl Entity for Feature { fn type_ref() -> TypeRef { TypeRef { @@ -1364,7 +1503,10 @@ impl Entity for Feature { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct File { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -1372,7 +1514,6 @@ pub struct File { pub mimeType: String, pub name: String, } - impl Entity for File { fn type_ref() -> TypeRef { TypeRef { @@ -1382,12 +1523,14 @@ impl Entity for File { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GeneratedIdWrapper { pub _id: CustomId, pub value: GeneratedId, } - impl Entity for GeneratedIdWrapper { fn type_ref() -> TypeRef { TypeRef { @@ -1397,7 +1540,10 @@ impl Entity for GeneratedIdWrapper { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GiftCard { pub _format: i64, pub _id: IdTuple, @@ -1414,7 +1560,6 @@ pub struct GiftCard { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for GiftCard { fn type_ref() -> TypeRef { TypeRef { @@ -1424,7 +1569,10 @@ impl Entity for GiftCard { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GiftCardCreateData { pub _format: i64, #[serde(with = "serde_bytes")] @@ -1437,7 +1585,6 @@ pub struct GiftCardCreateData { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for GiftCardCreateData { fn type_ref() -> TypeRef { TypeRef { @@ -1447,12 +1594,14 @@ impl Entity for GiftCardCreateData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GiftCardCreateReturn { pub _format: i64, pub giftCard: IdTuple, } - impl Entity for GiftCardCreateReturn { fn type_ref() -> TypeRef { TypeRef { @@ -1462,12 +1611,14 @@ impl Entity for GiftCardCreateReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GiftCardDeleteData { pub _format: i64, pub giftCard: IdTuple, } - impl Entity for GiftCardDeleteData { fn type_ref() -> TypeRef { TypeRef { @@ -1477,14 +1628,16 @@ impl Entity for GiftCardDeleteData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GiftCardGetReturn { pub _format: i64, pub maxPerPeriod: i64, pub period: i64, pub options: Vec, } - impl Entity for GiftCardGetReturn { fn type_ref() -> TypeRef { TypeRef { @@ -1494,12 +1647,14 @@ impl Entity for GiftCardGetReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GiftCardOption { pub _id: CustomId, pub value: i64, } - impl Entity for GiftCardOption { fn type_ref() -> TypeRef { TypeRef { @@ -1509,7 +1664,10 @@ impl Entity for GiftCardOption { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GiftCardRedeemData { pub _format: i64, pub countryCode: String, @@ -1517,7 +1675,6 @@ pub struct GiftCardRedeemData { pub keyHash: Vec, pub giftCardInfo: GeneratedId, } - impl Entity for GiftCardRedeemData { fn type_ref() -> TypeRef { TypeRef { @@ -1527,7 +1684,10 @@ impl Entity for GiftCardRedeemData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GiftCardRedeemGetReturn { pub _format: i64, pub message: String, @@ -1536,7 +1696,6 @@ pub struct GiftCardRedeemGetReturn { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for GiftCardRedeemGetReturn { fn type_ref() -> TypeRef { TypeRef { @@ -1546,12 +1705,14 @@ impl Entity for GiftCardRedeemGetReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GiftCardsRef { pub _id: CustomId, pub items: GeneratedId, } - impl Entity for GiftCardsRef { fn type_ref() -> TypeRef { TypeRef { @@ -1561,7 +1722,10 @@ impl Entity for GiftCardsRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Group { pub _format: i64, pub _id: GeneratedId, @@ -1588,7 +1752,6 @@ pub struct Group { pub storageCounter: Option, pub user: Option, } - impl Entity for Group { fn type_ref() -> TypeRef { TypeRef { @@ -1598,7 +1761,10 @@ impl Entity for Group { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupInfo { pub _format: i64, pub _id: IdTuple, @@ -1620,7 +1786,6 @@ pub struct GroupInfo { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for GroupInfo { fn type_ref() -> TypeRef { TypeRef { @@ -1630,7 +1795,10 @@ impl Entity for GroupInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupKey { pub _format: i64, pub _id: IdTuple, @@ -1645,7 +1813,6 @@ pub struct GroupKey { pub keyPair: Option, pub pubAdminGroupEncGKey: Option, } - impl Entity for GroupKey { fn type_ref() -> TypeRef { TypeRef { @@ -1655,7 +1822,10 @@ impl Entity for GroupKey { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupKeyRotationData { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -1669,7 +1839,6 @@ pub struct GroupKeyRotationData { pub groupMembershipUpdateData: Vec, pub keyPair: Option, } - impl Entity for GroupKeyRotationData { fn type_ref() -> TypeRef { TypeRef { @@ -1679,13 +1848,15 @@ impl Entity for GroupKeyRotationData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupKeyRotationInfoGetOut { pub _format: i64, pub userOrAdminGroupKeyRotationScheduled: bool, pub groupKeyUpdates: Vec, } - impl Entity for GroupKeyRotationInfoGetOut { fn type_ref() -> TypeRef { TypeRef { @@ -1695,12 +1866,14 @@ impl Entity for GroupKeyRotationInfoGetOut { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupKeyRotationPostIn { pub _format: i64, pub groupKeyUpdates: Vec, } - impl Entity for GroupKeyRotationPostIn { fn type_ref() -> TypeRef { TypeRef { @@ -1710,7 +1883,10 @@ impl Entity for GroupKeyRotationPostIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupKeyUpdate { pub _format: i64, pub _id: IdTuple, @@ -1726,7 +1902,6 @@ pub struct GroupKeyUpdate { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for GroupKeyUpdate { fn type_ref() -> TypeRef { TypeRef { @@ -1736,7 +1911,10 @@ impl Entity for GroupKeyUpdate { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupKeyUpdateData { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -1746,7 +1924,6 @@ pub struct GroupKeyUpdateData { pub sessionKeyEncGroupKeyVersion: i64, pub pubEncBucketKeyData: PubEncKeyData, } - impl Entity for GroupKeyUpdateData { fn type_ref() -> TypeRef { TypeRef { @@ -1756,12 +1933,14 @@ impl Entity for GroupKeyUpdateData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupKeyUpdatesRef { pub _id: CustomId, pub list: GeneratedId, } - impl Entity for GroupKeyUpdatesRef { fn type_ref() -> TypeRef { TypeRef { @@ -1771,12 +1950,14 @@ impl Entity for GroupKeyUpdatesRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupKeysRef { pub _id: CustomId, pub list: GeneratedId, } - impl Entity for GroupKeysRef { fn type_ref() -> TypeRef { TypeRef { @@ -1786,7 +1967,10 @@ impl Entity for GroupKeysRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupMember { pub _format: i64, pub _id: IdTuple, @@ -1797,7 +1981,6 @@ pub struct GroupMember { pub user: GeneratedId, pub userGroupInfo: IdTuple, } - impl Entity for GroupMember { fn type_ref() -> TypeRef { TypeRef { @@ -1807,7 +1990,10 @@ impl Entity for GroupMember { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupMembership { pub _id: CustomId, pub admin: bool, @@ -1821,7 +2007,6 @@ pub struct GroupMembership { pub groupInfo: IdTuple, pub groupMember: IdTuple, } - impl Entity for GroupMembership { fn type_ref() -> TypeRef { TypeRef { @@ -1831,7 +2016,10 @@ impl Entity for GroupMembership { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupMembershipKeyData { pub _id: CustomId, pub groupKeyVersion: i64, @@ -1840,7 +2028,6 @@ pub struct GroupMembershipKeyData { pub symKeyVersion: i64, pub group: GeneratedId, } - impl Entity for GroupMembershipKeyData { fn type_ref() -> TypeRef { TypeRef { @@ -1850,7 +2037,10 @@ impl Entity for GroupMembershipKeyData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupMembershipUpdateData { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -1858,7 +2048,6 @@ pub struct GroupMembershipUpdateData { pub userKeyVersion: i64, pub userId: GeneratedId, } - impl Entity for GroupMembershipUpdateData { fn type_ref() -> TypeRef { TypeRef { @@ -1868,7 +2057,10 @@ impl Entity for GroupMembershipUpdateData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupRoot { pub _format: i64, pub _id: GeneratedId, @@ -1878,7 +2070,6 @@ pub struct GroupRoot { pub externalUserAreaGroupInfos: Option, pub externalUserReferences: GeneratedId, } - impl Entity for GroupRoot { fn type_ref() -> TypeRef { TypeRef { @@ -1888,13 +2079,15 @@ impl Entity for GroupRoot { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct IdTupleWrapper { pub _id: CustomId, pub listElementId: GeneratedId, pub listId: GeneratedId, } - impl Entity for IdTupleWrapper { fn type_ref() -> TypeRef { TypeRef { @@ -1904,7 +2097,10 @@ impl Entity for IdTupleWrapper { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct InstanceSessionKey { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -1916,7 +2112,6 @@ pub struct InstanceSessionKey { pub symKeyVersion: i64, pub typeInfo: TypeInfo, } - impl Entity for InstanceSessionKey { fn type_ref() -> TypeRef { TypeRef { @@ -1926,7 +2121,10 @@ impl Entity for InstanceSessionKey { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Invoice { pub _format: i64, pub _id: GeneratedId, @@ -1955,7 +2153,6 @@ pub struct Invoice { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for Invoice { fn type_ref() -> TypeRef { TypeRef { @@ -1965,12 +2162,14 @@ impl Entity for Invoice { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct InvoiceDataGetIn { pub _format: i64, pub invoiceNumber: String, } - impl Entity for InvoiceDataGetIn { fn type_ref() -> TypeRef { TypeRef { @@ -1980,7 +2179,10 @@ impl Entity for InvoiceDataGetIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct InvoiceDataGetOut { pub _format: i64, pub address: String, @@ -1997,7 +2199,6 @@ pub struct InvoiceDataGetOut { pub vatType: i64, pub items: Vec, } - impl Entity for InvoiceDataGetOut { fn type_ref() -> TypeRef { TypeRef { @@ -2007,7 +2208,10 @@ impl Entity for InvoiceDataGetOut { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct InvoiceDataItem { pub _id: CustomId, pub amount: i64, @@ -2017,7 +2221,6 @@ pub struct InvoiceDataItem { pub startDate: Option, pub totalPrice: i64, } - impl Entity for InvoiceDataItem { fn type_ref() -> TypeRef { TypeRef { @@ -2027,7 +2230,10 @@ impl Entity for InvoiceDataItem { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct InvoiceInfo { pub _format: i64, pub _id: GeneratedId, @@ -2048,7 +2254,6 @@ pub struct InvoiceInfo { pub invoices: GeneratedId, pub paymentErrorInfo: Option, } - impl Entity for InvoiceInfo { fn type_ref() -> TypeRef { TypeRef { @@ -2058,7 +2263,10 @@ impl Entity for InvoiceInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct InvoiceItem { pub _id: CustomId, pub amount: i64, @@ -2071,7 +2279,6 @@ pub struct InvoiceItem { pub r#type: i64, pub _finalIvs: HashMap, } - impl Entity for InvoiceItem { fn type_ref() -> TypeRef { TypeRef { @@ -2081,7 +2288,10 @@ impl Entity for InvoiceItem { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct KeyPair { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -2097,7 +2307,6 @@ pub struct KeyPair { #[serde(with = "serde_bytes")] pub symEncPrivRsaKey: Option>, } - impl Entity for KeyPair { fn type_ref() -> TypeRef { TypeRef { @@ -2107,7 +2316,10 @@ impl Entity for KeyPair { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct KeyRotation { pub _format: i64, pub _id: IdTuple, @@ -2117,7 +2329,6 @@ pub struct KeyRotation { pub targetKeyVersion: i64, pub adminGroupKeyAuthenticationData: Option, } - impl Entity for KeyRotation { fn type_ref() -> TypeRef { TypeRef { @@ -2127,12 +2338,14 @@ impl Entity for KeyRotation { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct KeyRotationsRef { pub _id: CustomId, pub list: GeneratedId, } - impl Entity for KeyRotationsRef { fn type_ref() -> TypeRef { TypeRef { @@ -2142,12 +2355,14 @@ impl Entity for KeyRotationsRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct LocationServiceGetReturn { pub _format: i64, pub country: String, } - impl Entity for LocationServiceGetReturn { fn type_ref() -> TypeRef { TypeRef { @@ -2157,7 +2372,10 @@ impl Entity for LocationServiceGetReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Login { pub _format: i64, pub _id: IdTuple, @@ -2165,7 +2383,6 @@ pub struct Login { pub _permissions: GeneratedId, pub time: DateTime, } - impl Entity for Login { fn type_ref() -> TypeRef { TypeRef { @@ -2175,13 +2392,15 @@ impl Entity for Login { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailAddressAlias { pub _id: CustomId, pub enabled: bool, pub mailAddress: String, } - impl Entity for MailAddressAlias { fn type_ref() -> TypeRef { TypeRef { @@ -2191,12 +2410,14 @@ impl Entity for MailAddressAlias { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailAddressAliasGetIn { pub _format: i64, pub targetGroup: GeneratedId, } - impl Entity for MailAddressAliasGetIn { fn type_ref() -> TypeRef { TypeRef { @@ -2206,13 +2427,15 @@ impl Entity for MailAddressAliasGetIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailAddressAliasServiceData { pub _format: i64, pub mailAddress: String, pub group: GeneratedId, } - impl Entity for MailAddressAliasServiceData { fn type_ref() -> TypeRef { TypeRef { @@ -2222,14 +2445,16 @@ impl Entity for MailAddressAliasServiceData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailAddressAliasServiceDataDelete { pub _format: i64, pub mailAddress: String, pub restore: bool, pub group: GeneratedId, } - impl Entity for MailAddressAliasServiceDataDelete { fn type_ref() -> TypeRef { TypeRef { @@ -2239,7 +2464,10 @@ impl Entity for MailAddressAliasServiceDataDelete { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailAddressAliasServiceReturn { pub _format: i64, pub enabledAliases: i64, @@ -2247,7 +2475,6 @@ pub struct MailAddressAliasServiceReturn { pub totalAliases: i64, pub usedAliases: i64, } - impl Entity for MailAddressAliasServiceReturn { fn type_ref() -> TypeRef { TypeRef { @@ -2257,13 +2484,15 @@ impl Entity for MailAddressAliasServiceReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailAddressAvailability { pub _id: CustomId, pub available: bool, pub mailAddress: String, } - impl Entity for MailAddressAvailability { fn type_ref() -> TypeRef { TypeRef { @@ -2273,7 +2502,10 @@ impl Entity for MailAddressAvailability { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailAddressToGroup { pub _format: i64, pub _id: CustomId, @@ -2281,7 +2513,6 @@ pub struct MailAddressToGroup { pub _permissions: GeneratedId, pub internalGroup: Option, } - impl Entity for MailAddressToGroup { fn type_ref() -> TypeRef { TypeRef { @@ -2291,7 +2522,10 @@ impl Entity for MailAddressToGroup { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MembershipAddData { pub _format: i64, pub groupKeyVersion: i64, @@ -2301,7 +2535,6 @@ pub struct MembershipAddData { pub group: GeneratedId, pub user: GeneratedId, } - impl Entity for MembershipAddData { fn type_ref() -> TypeRef { TypeRef { @@ -2311,12 +2544,14 @@ impl Entity for MembershipAddData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MembershipPutIn { pub _format: i64, pub groupKeyUpdates: Vec, } - impl Entity for MembershipPutIn { fn type_ref() -> TypeRef { TypeRef { @@ -2326,13 +2561,15 @@ impl Entity for MembershipPutIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MembershipRemoveData { pub _format: i64, pub group: GeneratedId, pub user: GeneratedId, } - impl Entity for MembershipRemoveData { fn type_ref() -> TypeRef { TypeRef { @@ -2342,7 +2579,10 @@ impl Entity for MembershipRemoveData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MissedNotification { pub _format: i64, pub _id: CustomId, @@ -2359,7 +2599,6 @@ pub struct MissedNotification { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for MissedNotification { fn type_ref() -> TypeRef { TypeRef { @@ -2369,12 +2608,14 @@ impl Entity for MissedNotification { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MultipleMailAddressAvailabilityData { pub _format: i64, pub mailAddresses: Vec, } - impl Entity for MultipleMailAddressAvailabilityData { fn type_ref() -> TypeRef { TypeRef { @@ -2384,12 +2625,14 @@ impl Entity for MultipleMailAddressAvailabilityData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MultipleMailAddressAvailabilityReturn { pub _format: i64, pub availabilities: Vec, } - impl Entity for MultipleMailAddressAvailabilityReturn { fn type_ref() -> TypeRef { TypeRef { @@ -2399,14 +2642,16 @@ impl Entity for MultipleMailAddressAvailabilityReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct NotificationInfo { pub _id: CustomId, pub mailAddress: String, pub userId: GeneratedId, pub mailId: Option, } - impl Entity for NotificationInfo { fn type_ref() -> TypeRef { TypeRef { @@ -2416,14 +2661,16 @@ impl Entity for NotificationInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct NotificationMailTemplate { pub _id: CustomId, pub body: String, pub language: String, pub subject: String, } - impl Entity for NotificationMailTemplate { fn type_ref() -> TypeRef { TypeRef { @@ -2433,14 +2680,16 @@ impl Entity for NotificationMailTemplate { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct NotificationSessionKey { pub _id: CustomId, #[serde(with = "serde_bytes")] pub pushIdentifierSessionEncSessionKey: Vec, pub pushIdentifier: IdTuple, } - impl Entity for NotificationSessionKey { fn type_ref() -> TypeRef { TypeRef { @@ -2450,7 +2699,10 @@ impl Entity for NotificationSessionKey { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct OrderProcessingAgreement { pub _format: i64, pub _id: IdTuple, @@ -2467,7 +2719,6 @@ pub struct OrderProcessingAgreement { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for OrderProcessingAgreement { fn type_ref() -> TypeRef { TypeRef { @@ -2477,12 +2728,14 @@ impl Entity for OrderProcessingAgreement { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct OtpChallenge { pub _id: CustomId, pub secondFactors: Vec, } - impl Entity for OtpChallenge { fn type_ref() -> TypeRef { TypeRef { @@ -2492,12 +2745,14 @@ impl Entity for OtpChallenge { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PaymentDataServiceGetData { pub _format: i64, pub clientType: Option, } - impl Entity for PaymentDataServiceGetData { fn type_ref() -> TypeRef { TypeRef { @@ -2507,12 +2762,14 @@ impl Entity for PaymentDataServiceGetData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PaymentDataServiceGetReturn { pub _format: i64, pub loginUrl: String, } - impl Entity for PaymentDataServiceGetReturn { fn type_ref() -> TypeRef { TypeRef { @@ -2522,12 +2779,14 @@ impl Entity for PaymentDataServiceGetReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PaymentDataServicePostData { pub _format: i64, pub braintree3dsResponse: Braintree3ds2Response, } - impl Entity for PaymentDataServicePostData { fn type_ref() -> TypeRef { TypeRef { @@ -2537,7 +2796,10 @@ impl Entity for PaymentDataServicePostData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PaymentDataServicePutData { pub _format: i64, pub confirmedCountry: Option, @@ -2553,7 +2815,6 @@ pub struct PaymentDataServicePutData { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for PaymentDataServicePutData { fn type_ref() -> TypeRef { TypeRef { @@ -2563,13 +2824,15 @@ impl Entity for PaymentDataServicePutData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PaymentDataServicePutReturn { pub _format: i64, pub result: i64, pub braintree3dsRequest: Option, } - impl Entity for PaymentDataServicePutReturn { fn type_ref() -> TypeRef { TypeRef { @@ -2579,14 +2842,16 @@ impl Entity for PaymentDataServicePutReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PaymentErrorInfo { pub _id: CustomId, pub errorCode: String, pub errorTime: DateTime, pub thirdPartyErrorId: String, } - impl Entity for PaymentErrorInfo { fn type_ref() -> TypeRef { TypeRef { @@ -2596,7 +2861,10 @@ impl Entity for PaymentErrorInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Permission { pub _format: i64, pub _id: IdTuple, @@ -2618,7 +2886,6 @@ pub struct Permission { pub bucket: Option, pub group: Option, } - impl Entity for Permission { fn type_ref() -> TypeRef { TypeRef { @@ -2628,7 +2895,10 @@ impl Entity for Permission { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PlanConfiguration { pub _id: CustomId, pub autoResponder: bool, @@ -2642,7 +2912,6 @@ pub struct PlanConfiguration { pub templates: bool, pub whitelabel: bool, } - impl Entity for PlanConfiguration { fn type_ref() -> TypeRef { TypeRef { @@ -2652,7 +2921,10 @@ impl Entity for PlanConfiguration { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PlanPrices { pub _id: CustomId, pub additionalUserPriceMonthly: i64, @@ -2669,7 +2941,6 @@ pub struct PlanPrices { pub whitelabel: bool, pub planConfiguration: PlanConfiguration, } - impl Entity for PlanPrices { fn type_ref() -> TypeRef { TypeRef { @@ -2679,12 +2950,14 @@ impl Entity for PlanPrices { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PlanServiceGetOut { pub _format: i64, pub config: PlanConfiguration, } - impl Entity for PlanServiceGetOut { fn type_ref() -> TypeRef { TypeRef { @@ -2694,7 +2967,10 @@ impl Entity for PlanServiceGetOut { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PriceData { pub _id: CustomId, pub paymentInterval: i64, @@ -2702,7 +2978,6 @@ pub struct PriceData { pub taxIncluded: bool, pub items: Vec, } - impl Entity for PriceData { fn type_ref() -> TypeRef { TypeRef { @@ -2712,7 +2987,10 @@ impl Entity for PriceData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PriceItemData { pub _id: CustomId, pub count: i64, @@ -2720,7 +2998,6 @@ pub struct PriceItemData { pub price: i64, pub singleType: bool, } - impl Entity for PriceItemData { fn type_ref() -> TypeRef { TypeRef { @@ -2730,7 +3007,10 @@ impl Entity for PriceItemData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PriceRequestData { pub _id: CustomId, pub accountType: Option, @@ -2740,7 +3020,6 @@ pub struct PriceRequestData { pub paymentInterval: Option, pub reactivate: bool, } - impl Entity for PriceRequestData { fn type_ref() -> TypeRef { TypeRef { @@ -2750,13 +3029,15 @@ impl Entity for PriceRequestData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PriceServiceData { pub _format: i64, pub date: Option, pub priceRequest: Option, } - impl Entity for PriceServiceData { fn type_ref() -> TypeRef { TypeRef { @@ -2766,7 +3047,10 @@ impl Entity for PriceServiceData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PriceServiceReturn { pub _format: i64, pub currentPeriodAddedPrice: Option, @@ -2775,7 +3059,6 @@ pub struct PriceServiceReturn { pub currentPriceThisPeriod: Option, pub futurePriceNextPeriod: Option, } - impl Entity for PriceServiceReturn { fn type_ref() -> TypeRef { TypeRef { @@ -2785,7 +3068,10 @@ impl Entity for PriceServiceReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PubEncKeyData { pub _id: CustomId, pub protocolVersion: i64, @@ -2796,7 +3082,6 @@ pub struct PubEncKeyData { pub recipientKeyVersion: i64, pub senderKeyVersion: Option, } - impl Entity for PubEncKeyData { fn type_ref() -> TypeRef { TypeRef { @@ -2806,14 +3091,16 @@ impl Entity for PubEncKeyData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PublicKeyGetIn { pub _format: i64, pub identifier: String, pub identifierType: i64, pub version: Option, } - impl Entity for PublicKeyGetIn { fn type_ref() -> TypeRef { TypeRef { @@ -2823,7 +3110,10 @@ impl Entity for PublicKeyGetIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PublicKeyGetOut { pub _format: i64, #[serde(with = "serde_bytes")] @@ -2834,7 +3124,6 @@ pub struct PublicKeyGetOut { #[serde(with = "serde_bytes")] pub pubRsaKey: Option>, } - impl Entity for PublicKeyGetOut { fn type_ref() -> TypeRef { TypeRef { @@ -2844,7 +3133,10 @@ impl Entity for PublicKeyGetOut { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PublicKeyPutIn { pub _format: i64, #[serde(with = "serde_bytes")] @@ -2853,7 +3145,6 @@ pub struct PublicKeyPutIn { pub symEncPrivEccKey: Vec, pub keyGroup: GeneratedId, } - impl Entity for PublicKeyPutIn { fn type_ref() -> TypeRef { TypeRef { @@ -2863,7 +3154,10 @@ impl Entity for PublicKeyPutIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PushIdentifier { pub _area: i64, pub _format: i64, @@ -2885,7 +3179,6 @@ pub struct PushIdentifier { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for PushIdentifier { fn type_ref() -> TypeRef { TypeRef { @@ -2895,12 +3188,14 @@ impl Entity for PushIdentifier { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PushIdentifierList { pub _id: CustomId, pub list: GeneratedId, } - impl Entity for PushIdentifierList { fn type_ref() -> TypeRef { TypeRef { @@ -2910,7 +3205,10 @@ impl Entity for PushIdentifierList { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ReceivedGroupInvitation { pub _format: i64, pub _id: IdTuple, @@ -2933,7 +3231,6 @@ pub struct ReceivedGroupInvitation { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for ReceivedGroupInvitation { fn type_ref() -> TypeRef { TypeRef { @@ -2943,7 +3240,10 @@ impl Entity for ReceivedGroupInvitation { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct RecoverCode { pub _format: i64, pub _id: GeneratedId, @@ -2957,7 +3257,6 @@ pub struct RecoverCode { #[serde(with = "serde_bytes")] pub verifier: Vec, } - impl Entity for RecoverCode { fn type_ref() -> TypeRef { TypeRef { @@ -2967,7 +3266,10 @@ impl Entity for RecoverCode { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct RecoverCodeData { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -2978,7 +3280,6 @@ pub struct RecoverCodeData { pub userEncRecoveryCode: Vec, pub userKeyVersion: i64, } - impl Entity for RecoverCodeData { fn type_ref() -> TypeRef { TypeRef { @@ -2988,12 +3289,14 @@ impl Entity for RecoverCodeData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ReferralCodeGetIn { pub _format: i64, pub referralCode: GeneratedId, } - impl Entity for ReferralCodeGetIn { fn type_ref() -> TypeRef { TypeRef { @@ -3003,11 +3306,13 @@ impl Entity for ReferralCodeGetIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ReferralCodePostIn { pub _format: i64, } - impl Entity for ReferralCodePostIn { fn type_ref() -> TypeRef { TypeRef { @@ -3017,12 +3322,14 @@ impl Entity for ReferralCodePostIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ReferralCodePostOut { pub _format: i64, pub referralCode: GeneratedId, } - impl Entity for ReferralCodePostOut { fn type_ref() -> TypeRef { TypeRef { @@ -3032,13 +3339,15 @@ impl Entity for ReferralCodePostOut { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct RegistrationCaptchaServiceData { pub _format: i64, pub response: String, pub token: String, } - impl Entity for RegistrationCaptchaServiceData { fn type_ref() -> TypeRef { TypeRef { @@ -3048,7 +3357,10 @@ impl Entity for RegistrationCaptchaServiceData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct RegistrationCaptchaServiceGetData { pub _format: i64, pub businessUseSelected: bool, @@ -3057,7 +3369,6 @@ pub struct RegistrationCaptchaServiceGetData { pub signupToken: Option, pub token: Option, } - impl Entity for RegistrationCaptchaServiceGetData { fn type_ref() -> TypeRef { TypeRef { @@ -3067,14 +3378,16 @@ impl Entity for RegistrationCaptchaServiceGetData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct RegistrationCaptchaServiceReturn { pub _format: i64, #[serde(with = "serde_bytes")] pub challenge: Option>, pub token: String, } - impl Entity for RegistrationCaptchaServiceReturn { fn type_ref() -> TypeRef { TypeRef { @@ -3084,12 +3397,14 @@ impl Entity for RegistrationCaptchaServiceReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct RegistrationReturn { pub _format: i64, pub authToken: String, } - impl Entity for RegistrationReturn { fn type_ref() -> TypeRef { TypeRef { @@ -3099,14 +3414,16 @@ impl Entity for RegistrationReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct RegistrationServiceData { pub _format: i64, pub source: Option, pub starterDomain: String, pub state: i64, } - impl Entity for RegistrationServiceData { fn type_ref() -> TypeRef { TypeRef { @@ -3116,7 +3433,10 @@ impl Entity for RegistrationServiceData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct RejectedSender { pub _format: i64, pub _id: IdTuple, @@ -3128,7 +3448,6 @@ pub struct RejectedSender { pub senderIp: String, pub senderMailAddress: String, } - impl Entity for RejectedSender { fn type_ref() -> TypeRef { TypeRef { @@ -3138,12 +3457,14 @@ impl Entity for RejectedSender { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct RejectedSendersRef { pub _id: CustomId, pub items: GeneratedId, } - impl Entity for RejectedSendersRef { fn type_ref() -> TypeRef { TypeRef { @@ -3153,7 +3474,10 @@ impl Entity for RejectedSendersRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct RepeatRule { pub _id: CustomId, pub endType: i64, @@ -3164,7 +3488,6 @@ pub struct RepeatRule { pub excludedDates: Vec, pub _finalIvs: HashMap, } - impl Entity for RepeatRule { fn type_ref() -> TypeRef { TypeRef { @@ -3174,14 +3497,16 @@ impl Entity for RepeatRule { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ResetFactorsDeleteData { pub _format: i64, pub authVerifier: String, pub mailAddress: String, pub recoverCodeVerifier: String, } - impl Entity for ResetFactorsDeleteData { fn type_ref() -> TypeRef { TypeRef { @@ -3191,7 +3516,10 @@ impl Entity for ResetFactorsDeleteData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ResetPasswordPostIn { pub _format: i64, pub kdfVersion: i64, @@ -3204,7 +3532,6 @@ pub struct ResetPasswordPostIn { pub verifier: Vec, pub user: GeneratedId, } - impl Entity for ResetPasswordPostIn { fn type_ref() -> TypeRef { TypeRef { @@ -3214,7 +3541,10 @@ impl Entity for ResetPasswordPostIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct RootInstance { pub _format: i64, pub _id: IdTuple, @@ -3222,7 +3552,6 @@ pub struct RootInstance { pub _permissions: GeneratedId, pub reference: GeneratedId, } - impl Entity for RootInstance { fn type_ref() -> TypeRef { TypeRef { @@ -3232,12 +3561,14 @@ impl Entity for RootInstance { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SaltData { pub _format: i64, pub mailAddress: String, } - impl Entity for SaltData { fn type_ref() -> TypeRef { TypeRef { @@ -3247,14 +3578,16 @@ impl Entity for SaltData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SaltReturn { pub _format: i64, pub kdfVersion: i64, #[serde(with = "serde_bytes")] pub salt: Vec, } - impl Entity for SaltReturn { fn type_ref() -> TypeRef { TypeRef { @@ -3264,7 +3597,10 @@ impl Entity for SaltReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SecondFactor { pub _format: i64, pub _id: IdTuple, @@ -3277,7 +3613,6 @@ pub struct SecondFactor { pub r#type: i64, pub u2f: Option, } - impl Entity for SecondFactor { fn type_ref() -> TypeRef { TypeRef { @@ -3287,12 +3622,14 @@ impl Entity for SecondFactor { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SecondFactorAuthAllowedReturn { pub _format: i64, pub allowed: bool, } - impl Entity for SecondFactorAuthAllowedReturn { fn type_ref() -> TypeRef { TypeRef { @@ -3302,7 +3639,10 @@ impl Entity for SecondFactorAuthAllowedReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SecondFactorAuthData { pub _format: i64, pub otpCode: Option, @@ -3312,7 +3652,6 @@ pub struct SecondFactorAuthData { pub u2f: Option, pub webauthn: Option, } - impl Entity for SecondFactorAuthData { fn type_ref() -> TypeRef { TypeRef { @@ -3322,12 +3661,14 @@ impl Entity for SecondFactorAuthData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SecondFactorAuthDeleteData { pub _format: i64, pub session: IdTuple, } - impl Entity for SecondFactorAuthDeleteData { fn type_ref() -> TypeRef { TypeRef { @@ -3337,12 +3678,14 @@ impl Entity for SecondFactorAuthDeleteData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SecondFactorAuthGetData { pub _format: i64, pub accessToken: String, } - impl Entity for SecondFactorAuthGetData { fn type_ref() -> TypeRef { TypeRef { @@ -3352,12 +3695,14 @@ impl Entity for SecondFactorAuthGetData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SecondFactorAuthGetReturn { pub _format: i64, pub secondFactorPending: bool, } - impl Entity for SecondFactorAuthGetReturn { fn type_ref() -> TypeRef { TypeRef { @@ -3367,7 +3712,10 @@ impl Entity for SecondFactorAuthGetReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SecondFactorAuthentication { pub _format: i64, pub _id: IdTuple, @@ -3378,7 +3726,6 @@ pub struct SecondFactorAuthentication { pub service: String, pub verifyCount: i64, } - impl Entity for SecondFactorAuthentication { fn type_ref() -> TypeRef { TypeRef { @@ -3388,7 +3735,10 @@ impl Entity for SecondFactorAuthentication { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SendRegistrationCodeData { pub _format: i64, pub accountType: i64, @@ -3396,7 +3746,6 @@ pub struct SendRegistrationCodeData { pub language: String, pub mobilePhoneNumber: String, } - impl Entity for SendRegistrationCodeData { fn type_ref() -> TypeRef { TypeRef { @@ -3406,12 +3755,14 @@ impl Entity for SendRegistrationCodeData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SendRegistrationCodeReturn { pub _format: i64, pub authToken: String, } - impl Entity for SendRegistrationCodeReturn { fn type_ref() -> TypeRef { TypeRef { @@ -3421,7 +3772,10 @@ impl Entity for SendRegistrationCodeReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SentGroupInvitation { pub _format: i64, pub _id: IdTuple, @@ -3432,7 +3786,6 @@ pub struct SentGroupInvitation { pub receivedInvitation: Option, pub sharedGroup: GeneratedId, } - impl Entity for SentGroupInvitation { fn type_ref() -> TypeRef { TypeRef { @@ -3442,7 +3795,10 @@ impl Entity for SentGroupInvitation { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Session { pub _format: i64, pub _id: IdTuple, @@ -3463,7 +3819,6 @@ pub struct Session { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for Session { fn type_ref() -> TypeRef { TypeRef { @@ -3473,13 +3828,15 @@ impl Entity for Session { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SignOrderProcessingAgreementData { pub _format: i64, pub customerAddress: String, pub version: String, } - impl Entity for SignOrderProcessingAgreementData { fn type_ref() -> TypeRef { TypeRef { @@ -3489,13 +3846,15 @@ impl Entity for SignOrderProcessingAgreementData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SseConnectData { pub _format: i64, pub identifier: String, pub userIds: Vec, } - impl Entity for SseConnectData { fn type_ref() -> TypeRef { TypeRef { @@ -3505,13 +3864,15 @@ impl Entity for SseConnectData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct StringConfigValue { pub _id: CustomId, pub name: String, pub value: String, } - impl Entity for StringConfigValue { fn type_ref() -> TypeRef { TypeRef { @@ -3521,12 +3882,14 @@ impl Entity for StringConfigValue { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct StringWrapper { pub _id: CustomId, pub value: String, } - impl Entity for StringWrapper { fn type_ref() -> TypeRef { TypeRef { @@ -3536,7 +3899,10 @@ impl Entity for StringWrapper { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SurveyData { pub _id: CustomId, pub category: i64, @@ -3544,7 +3910,6 @@ pub struct SurveyData { pub reason: i64, pub version: i64, } - impl Entity for SurveyData { fn type_ref() -> TypeRef { TypeRef { @@ -3554,7 +3919,10 @@ impl Entity for SurveyData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SwitchAccountTypePostIn { pub _format: i64, pub accountType: i64, @@ -3565,7 +3933,6 @@ pub struct SwitchAccountTypePostIn { pub referralCode: Option, pub surveyData: Option, } - impl Entity for SwitchAccountTypePostIn { fn type_ref() -> TypeRef { TypeRef { @@ -3575,7 +3942,10 @@ impl Entity for SwitchAccountTypePostIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SystemKeysReturn { pub _format: i64, #[serde(with = "serde_bytes")] @@ -3594,7 +3964,6 @@ pub struct SystemKeysReturn { pub freeGroup: Option, pub premiumGroup: Option, } - impl Entity for SystemKeysReturn { fn type_ref() -> TypeRef { TypeRef { @@ -3604,7 +3973,10 @@ impl Entity for SystemKeysReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct TakeOverDeletedAddressData { pub _format: i64, pub authVerifier: String, @@ -3612,7 +3984,6 @@ pub struct TakeOverDeletedAddressData { pub recoverCodeVerifier: Option, pub targetAccountMailAddress: String, } - impl Entity for TakeOverDeletedAddressData { fn type_ref() -> TypeRef { TypeRef { @@ -3622,13 +3993,15 @@ impl Entity for TakeOverDeletedAddressData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct TypeInfo { pub _id: CustomId, pub application: String, pub typeId: i64, } - impl Entity for TypeInfo { fn type_ref() -> TypeRef { TypeRef { @@ -3638,14 +4011,16 @@ impl Entity for TypeInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct U2fChallenge { pub _id: CustomId, #[serde(with = "serde_bytes")] pub challenge: Vec, pub keys: Vec, } - impl Entity for U2fChallenge { fn type_ref() -> TypeRef { TypeRef { @@ -3655,7 +4030,10 @@ impl Entity for U2fChallenge { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct U2fKey { pub _id: CustomId, pub appId: String, @@ -3663,7 +4041,6 @@ pub struct U2fKey { pub keyHandle: Vec, pub secondFactor: IdTuple, } - impl Entity for U2fKey { fn type_ref() -> TypeRef { TypeRef { @@ -3673,7 +4050,10 @@ impl Entity for U2fKey { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct U2fRegisteredDevice { pub _id: CustomId, pub appId: String, @@ -3684,7 +4064,6 @@ pub struct U2fRegisteredDevice { #[serde(with = "serde_bytes")] pub publicKey: Vec, } - impl Entity for U2fRegisteredDevice { fn type_ref() -> TypeRef { TypeRef { @@ -3694,14 +4073,16 @@ impl Entity for U2fRegisteredDevice { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct U2fResponseData { pub _id: CustomId, pub clientData: String, pub keyHandle: String, pub signatureData: String, } - impl Entity for U2fResponseData { fn type_ref() -> TypeRef { TypeRef { @@ -3711,7 +4092,10 @@ impl Entity for U2fResponseData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UpdatePermissionKeyData { pub _format: i64, #[serde(with = "serde_bytes")] @@ -3720,7 +4104,6 @@ pub struct UpdatePermissionKeyData { pub bucketPermission: IdTuple, pub permission: IdTuple, } - impl Entity for UpdatePermissionKeyData { fn type_ref() -> TypeRef { TypeRef { @@ -3730,12 +4113,14 @@ impl Entity for UpdatePermissionKeyData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UpdateSessionKeysPostIn { pub _format: i64, pub ownerEncSessionKeys: Vec, } - impl Entity for UpdateSessionKeysPostIn { fn type_ref() -> TypeRef { TypeRef { @@ -3745,14 +4130,16 @@ impl Entity for UpdateSessionKeysPostIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UpgradePriceServiceData { pub _format: i64, pub campaign: Option, pub date: Option, pub referralCode: Option, } - impl Entity for UpgradePriceServiceData { fn type_ref() -> TypeRef { TypeRef { @@ -3762,7 +4149,10 @@ impl Entity for UpgradePriceServiceData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UpgradePriceServiceReturn { pub _format: i64, pub bonusMonthsForYearlyPlan: i64, @@ -3781,7 +4171,6 @@ pub struct UpgradePriceServiceReturn { pub teamsPrices: PlanPrices, pub unlimitedPrices: PlanPrices, } - impl Entity for UpgradePriceServiceReturn { fn type_ref() -> TypeRef { TypeRef { @@ -3791,7 +4180,10 @@ impl Entity for UpgradePriceServiceReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct User { pub _format: i64, pub _id: GeneratedId, @@ -3817,7 +4209,6 @@ pub struct User { pub successfulLogins: GeneratedId, pub userGroup: GroupMembership, } - impl Entity for User { fn type_ref() -> TypeRef { TypeRef { @@ -3827,7 +4218,10 @@ impl Entity for User { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserAlarmInfo { pub _format: i64, pub _id: IdTuple, @@ -3840,7 +4234,6 @@ pub struct UserAlarmInfo { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for UserAlarmInfo { fn type_ref() -> TypeRef { TypeRef { @@ -3850,12 +4243,14 @@ impl Entity for UserAlarmInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserAlarmInfoListType { pub _id: CustomId, pub alarms: GeneratedId, } - impl Entity for UserAlarmInfoListType { fn type_ref() -> TypeRef { TypeRef { @@ -3865,12 +4260,14 @@ impl Entity for UserAlarmInfoListType { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserAreaGroups { pub _id: CustomId, pub list: GeneratedId, } - impl Entity for UserAreaGroups { fn type_ref() -> TypeRef { TypeRef { @@ -3880,14 +4277,16 @@ impl Entity for UserAreaGroups { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserAuthentication { pub _id: CustomId, pub recoverCode: Option, pub secondFactors: GeneratedId, pub sessions: GeneratedId, } - impl Entity for UserAuthentication { fn type_ref() -> TypeRef { TypeRef { @@ -3897,14 +4296,16 @@ impl Entity for UserAuthentication { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserDataDelete { pub _format: i64, pub date: Option, pub restore: bool, pub user: GeneratedId, } - impl Entity for UserDataDelete { fn type_ref() -> TypeRef { TypeRef { @@ -3914,7 +4315,10 @@ impl Entity for UserDataDelete { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserExternalAuthInfo { pub _id: CustomId, pub authUpdateCounter: i64, @@ -3924,7 +4328,6 @@ pub struct UserExternalAuthInfo { pub latestSaltHash: Option>, pub variableAuthInfo: GeneratedId, } - impl Entity for UserExternalAuthInfo { fn type_ref() -> TypeRef { TypeRef { @@ -3934,7 +4337,10 @@ impl Entity for UserExternalAuthInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserGroupKeyDistribution { pub _format: i64, pub _id: GeneratedId, @@ -3944,7 +4350,6 @@ pub struct UserGroupKeyDistribution { pub distributionEncUserGroupKey: Vec, pub userGroupKeyVersion: i64, } - impl Entity for UserGroupKeyDistribution { fn type_ref() -> TypeRef { TypeRef { @@ -3954,7 +4359,10 @@ impl Entity for UserGroupKeyDistribution { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserGroupKeyRotationData { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -3974,7 +4382,6 @@ pub struct UserGroupKeyRotationData { pub pubAdminGroupEncUserGroupKey: Option, pub recoverCodeData: Option, } - impl Entity for UserGroupKeyRotationData { fn type_ref() -> TypeRef { TypeRef { @@ -3984,12 +4391,14 @@ impl Entity for UserGroupKeyRotationData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserGroupKeyRotationPostIn { pub _format: i64, pub userGroupKeyData: UserGroupKeyRotationData, } - impl Entity for UserGroupKeyRotationPostIn { fn type_ref() -> TypeRef { TypeRef { @@ -3999,7 +4408,10 @@ impl Entity for UserGroupKeyRotationPostIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserGroupRoot { pub _format: i64, pub _id: GeneratedId, @@ -4009,7 +4421,6 @@ pub struct UserGroupRoot { pub invitations: GeneratedId, pub keyRotations: Option, } - impl Entity for UserGroupRoot { fn type_ref() -> TypeRef { TypeRef { @@ -4019,7 +4430,10 @@ impl Entity for UserGroupRoot { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct VariableExternalAuthInfo { pub _format: i64, pub _id: GeneratedId, @@ -4034,7 +4448,6 @@ pub struct VariableExternalAuthInfo { pub loggedInVerifier: Option>, pub sentCount: i64, } - impl Entity for VariableExternalAuthInfo { fn type_ref() -> TypeRef { TypeRef { @@ -4044,13 +4457,15 @@ impl Entity for VariableExternalAuthInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct VerifyRegistrationCodeData { pub _format: i64, pub authToken: String, pub code: String, } - impl Entity for VerifyRegistrationCodeData { fn type_ref() -> TypeRef { TypeRef { @@ -4060,7 +4475,10 @@ impl Entity for VerifyRegistrationCodeData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Version { pub _id: CustomId, pub operation: String, @@ -4069,7 +4487,6 @@ pub struct Version { pub author: GeneratedId, pub authorGroupInfo: IdTuple, } - impl Entity for Version { fn type_ref() -> TypeRef { TypeRef { @@ -4079,7 +4496,10 @@ impl Entity for Version { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct VersionData { pub _format: i64, pub application: String, @@ -4087,7 +4507,6 @@ pub struct VersionData { pub listId: Option, pub typeId: i64, } - impl Entity for VersionData { fn type_ref() -> TypeRef { TypeRef { @@ -4097,7 +4516,10 @@ impl Entity for VersionData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct VersionInfo { pub _format: i64, pub _id: IdTuple, @@ -4114,7 +4536,6 @@ pub struct VersionInfo { pub author: GeneratedId, pub authorGroupInfo: IdTuple, } - impl Entity for VersionInfo { fn type_ref() -> TypeRef { TypeRef { @@ -4124,12 +4545,14 @@ impl Entity for VersionInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct VersionReturn { pub _format: i64, pub versions: Vec, } - impl Entity for VersionReturn { fn type_ref() -> TypeRef { TypeRef { @@ -4139,7 +4562,10 @@ impl Entity for VersionReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct WebauthnResponseData { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -4151,7 +4577,6 @@ pub struct WebauthnResponseData { #[serde(with = "serde_bytes")] pub signature: Vec, } - impl Entity for WebauthnResponseData { fn type_ref() -> TypeRef { TypeRef { @@ -4161,13 +4586,15 @@ impl Entity for WebauthnResponseData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct WebsocketCounterData { pub _format: i64, pub mailGroup: GeneratedId, pub counterValues: Vec, } - impl Entity for WebsocketCounterData { fn type_ref() -> TypeRef { TypeRef { @@ -4177,13 +4604,15 @@ impl Entity for WebsocketCounterData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct WebsocketCounterValue { pub _id: CustomId, pub count: i64, pub counterId: GeneratedId, } - impl Entity for WebsocketCounterValue { fn type_ref() -> TypeRef { TypeRef { @@ -4193,14 +4622,16 @@ impl Entity for WebsocketCounterValue { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct WebsocketEntityData { pub _format: i64, pub eventBatchId: GeneratedId, pub eventBatchOwner: GeneratedId, pub eventBatch: Vec, } - impl Entity for WebsocketEntityData { fn type_ref() -> TypeRef { TypeRef { @@ -4210,12 +4641,14 @@ impl Entity for WebsocketEntityData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct WebsocketLeaderStatus { pub _format: i64, pub leaderStatus: bool, } - impl Entity for WebsocketLeaderStatus { fn type_ref() -> TypeRef { TypeRef { @@ -4225,7 +4658,10 @@ impl Entity for WebsocketLeaderStatus { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct WhitelabelChild { pub _format: i64, pub _id: IdTuple, @@ -4242,7 +4678,6 @@ pub struct WhitelabelChild { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for WhitelabelChild { fn type_ref() -> TypeRef { TypeRef { @@ -4252,12 +4687,14 @@ impl Entity for WhitelabelChild { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct WhitelabelChildrenRef { pub _id: CustomId, pub items: GeneratedId, } - impl Entity for WhitelabelChildrenRef { fn type_ref() -> TypeRef { TypeRef { @@ -4267,7 +4704,10 @@ impl Entity for WhitelabelChildrenRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct WhitelabelConfig { pub _format: i64, pub _id: GeneratedId, @@ -4283,7 +4723,6 @@ pub struct WhitelabelConfig { pub certificateInfo: Option, pub whitelabelRegistrationDomains: Vec, } - impl Entity for WhitelabelConfig { fn type_ref() -> TypeRef { TypeRef { @@ -4293,13 +4732,15 @@ impl Entity for WhitelabelConfig { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct WhitelabelParent { pub _id: CustomId, pub customer: GeneratedId, pub whitelabelChildInParent: IdTuple, } - impl Entity for WhitelabelParent { fn type_ref() -> TypeRef { TypeRef { @@ -4308,3 +4749,6 @@ impl Entity for WhitelabelParent { } } } + + + diff --git a/tuta-sdk/rust/sdk/src/entities/tutanota.rs b/tuta-sdk/rust/sdk/src/entities/tutanota.rs index 87adb1cef3e8..c3661696e98f 100644 --- a/tuta-sdk/rust/sdk/src/entities/tutanota.rs +++ b/tuta-sdk/rust/sdk/src/entities/tutanota.rs @@ -2,7 +2,7 @@ use super::*; use serde::{Deserialize, Serialize}; -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct AttachmentKeyData { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -11,7 +11,6 @@ pub struct AttachmentKeyData { pub fileSessionKey: Option>, pub file: IdTuple, } - impl Entity for AttachmentKeyData { fn type_ref() -> TypeRef { TypeRef { @@ -21,14 +20,16 @@ impl Entity for AttachmentKeyData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Birthday { pub _id: CustomId, pub day: i64, pub month: i64, pub year: Option, } - impl Entity for Birthday { fn type_ref() -> TypeRef { TypeRef { @@ -38,14 +39,16 @@ impl Entity for Birthday { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Body { pub _id: CustomId, pub compressedText: Option>, pub text: Option, pub _finalIvs: HashMap, } - impl Entity for Body { fn type_ref() -> TypeRef { TypeRef { @@ -55,12 +58,14 @@ impl Entity for Body { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CalendarDeleteData { pub _format: i64, pub groupRootId: GeneratedId, } - impl Entity for CalendarDeleteData { fn type_ref() -> TypeRef { TypeRef { @@ -70,7 +75,10 @@ impl Entity for CalendarDeleteData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CalendarEvent { pub _format: i64, pub _id: IdTuple, @@ -97,7 +105,6 @@ pub struct CalendarEvent { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for CalendarEvent { fn type_ref() -> TypeRef { TypeRef { @@ -107,14 +114,16 @@ impl Entity for CalendarEvent { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CalendarEventAttendee { pub _id: CustomId, pub status: i64, pub address: EncryptedMailAddress, pub _finalIvs: HashMap, } - impl Entity for CalendarEventAttendee { fn type_ref() -> TypeRef { TypeRef { @@ -124,12 +133,14 @@ impl Entity for CalendarEventAttendee { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CalendarEventIndexRef { pub _id: CustomId, pub list: GeneratedId, } - impl Entity for CalendarEventIndexRef { fn type_ref() -> TypeRef { TypeRef { @@ -139,7 +150,10 @@ impl Entity for CalendarEventIndexRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CalendarEventUidIndex { pub _format: i64, pub _id: IdTuple, @@ -148,7 +162,6 @@ pub struct CalendarEventUidIndex { pub alteredInstances: Vec, pub progenitor: Option, } - impl Entity for CalendarEventUidIndex { fn type_ref() -> TypeRef { TypeRef { @@ -158,7 +171,10 @@ impl Entity for CalendarEventUidIndex { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CalendarEventUpdate { pub _format: i64, pub _id: IdTuple, @@ -172,7 +188,6 @@ pub struct CalendarEventUpdate { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for CalendarEventUpdate { fn type_ref() -> TypeRef { TypeRef { @@ -182,12 +197,14 @@ impl Entity for CalendarEventUpdate { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CalendarEventUpdateList { pub _id: CustomId, pub list: GeneratedId, } - impl Entity for CalendarEventUpdateList { fn type_ref() -> TypeRef { TypeRef { @@ -197,7 +214,10 @@ impl Entity for CalendarEventUpdateList { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CalendarGroupRoot { pub _format: i64, pub _id: GeneratedId, @@ -212,7 +232,6 @@ pub struct CalendarGroupRoot { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for CalendarGroupRoot { fn type_ref() -> TypeRef { TypeRef { @@ -222,7 +241,10 @@ impl Entity for CalendarGroupRoot { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CalendarRepeatRule { pub _id: CustomId, pub endType: i64, @@ -233,7 +255,6 @@ pub struct CalendarRepeatRule { pub excludedDates: Vec, pub _finalIvs: HashMap, } - impl Entity for CalendarRepeatRule { fn type_ref() -> TypeRef { TypeRef { @@ -243,7 +264,10 @@ impl Entity for CalendarRepeatRule { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Contact { pub _format: i64, pub _id: IdTuple, @@ -282,7 +306,6 @@ pub struct Contact { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for Contact { fn type_ref() -> TypeRef { TypeRef { @@ -292,7 +315,10 @@ impl Entity for Contact { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ContactAddress { pub _id: CustomId, pub address: String, @@ -301,7 +327,6 @@ pub struct ContactAddress { pub r#type: i64, pub _finalIvs: HashMap, } - impl Entity for ContactAddress { fn type_ref() -> TypeRef { TypeRef { @@ -311,7 +336,10 @@ impl Entity for ContactAddress { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ContactCustomDate { pub _id: CustomId, pub customTypeName: String, @@ -320,7 +348,6 @@ pub struct ContactCustomDate { pub r#type: i64, pub _finalIvs: HashMap, } - impl Entity for ContactCustomDate { fn type_ref() -> TypeRef { TypeRef { @@ -330,7 +357,10 @@ impl Entity for ContactCustomDate { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ContactList { pub _format: i64, pub _id: GeneratedId, @@ -344,7 +374,6 @@ pub struct ContactList { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for ContactList { fn type_ref() -> TypeRef { TypeRef { @@ -354,7 +383,10 @@ impl Entity for ContactList { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ContactListEntry { pub _format: i64, pub _id: IdTuple, @@ -367,7 +399,6 @@ pub struct ContactListEntry { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for ContactListEntry { fn type_ref() -> TypeRef { TypeRef { @@ -377,7 +408,10 @@ impl Entity for ContactListEntry { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ContactListGroupRoot { pub _format: i64, pub _id: GeneratedId, @@ -390,7 +424,6 @@ pub struct ContactListGroupRoot { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for ContactListGroupRoot { fn type_ref() -> TypeRef { TypeRef { @@ -400,7 +433,10 @@ impl Entity for ContactListGroupRoot { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ContactMailAddress { pub _id: CustomId, pub address: String, @@ -409,7 +445,6 @@ pub struct ContactMailAddress { pub r#type: i64, pub _finalIvs: HashMap, } - impl Entity for ContactMailAddress { fn type_ref() -> TypeRef { TypeRef { @@ -419,7 +454,10 @@ impl Entity for ContactMailAddress { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ContactMessengerHandle { pub _id: CustomId, pub customTypeName: String, @@ -428,7 +466,6 @@ pub struct ContactMessengerHandle { pub r#type: i64, pub _finalIvs: HashMap, } - impl Entity for ContactMessengerHandle { fn type_ref() -> TypeRef { TypeRef { @@ -438,7 +475,10 @@ impl Entity for ContactMessengerHandle { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ContactPhoneNumber { pub _id: CustomId, pub customTypeName: String, @@ -447,7 +487,6 @@ pub struct ContactPhoneNumber { pub r#type: i64, pub _finalIvs: HashMap, } - impl Entity for ContactPhoneNumber { fn type_ref() -> TypeRef { TypeRef { @@ -457,14 +496,16 @@ impl Entity for ContactPhoneNumber { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ContactPronouns { pub _id: CustomId, pub language: String, pub pronouns: String, pub _finalIvs: HashMap, } - impl Entity for ContactPronouns { fn type_ref() -> TypeRef { TypeRef { @@ -474,7 +515,10 @@ impl Entity for ContactPronouns { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ContactRelationship { pub _id: CustomId, pub customTypeName: String, @@ -483,7 +527,6 @@ pub struct ContactRelationship { pub r#type: i64, pub _finalIvs: HashMap, } - impl Entity for ContactRelationship { fn type_ref() -> TypeRef { TypeRef { @@ -493,7 +536,10 @@ impl Entity for ContactRelationship { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ContactSocialId { pub _id: CustomId, pub customTypeName: String, @@ -502,7 +548,6 @@ pub struct ContactSocialId { pub r#type: i64, pub _finalIvs: HashMap, } - impl Entity for ContactSocialId { fn type_ref() -> TypeRef { TypeRef { @@ -512,7 +557,10 @@ impl Entity for ContactSocialId { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ContactWebsite { pub _id: CustomId, pub customTypeName: String, @@ -521,7 +569,6 @@ pub struct ContactWebsite { pub url: String, pub _finalIvs: HashMap, } - impl Entity for ContactWebsite { fn type_ref() -> TypeRef { TypeRef { @@ -531,7 +578,10 @@ impl Entity for ContactWebsite { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ConversationEntry { pub _format: i64, pub _id: IdTuple, @@ -542,7 +592,6 @@ pub struct ConversationEntry { pub mail: Option, pub previous: Option, } - impl Entity for ConversationEntry { fn type_ref() -> TypeRef { TypeRef { @@ -552,7 +601,10 @@ impl Entity for ConversationEntry { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CreateExternalUserGroupData { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -562,7 +614,6 @@ pub struct CreateExternalUserGroupData { pub internalUserGroupKeyVersion: i64, pub mailAddress: String, } - impl Entity for CreateExternalUserGroupData { fn type_ref() -> TypeRef { TypeRef { @@ -572,14 +623,16 @@ impl Entity for CreateExternalUserGroupData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CreateGroupPostReturn { pub _format: i64, pub group: GeneratedId, pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for CreateGroupPostReturn { fn type_ref() -> TypeRef { TypeRef { @@ -589,7 +642,10 @@ impl Entity for CreateGroupPostReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CreateMailFolderData { pub _format: i64, pub folderName: String, @@ -601,7 +657,6 @@ pub struct CreateMailFolderData { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for CreateMailFolderData { fn type_ref() -> TypeRef { TypeRef { @@ -611,14 +666,16 @@ impl Entity for CreateMailFolderData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CreateMailFolderReturn { pub _format: i64, pub newFolder: IdTuple, pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for CreateMailFolderReturn { fn type_ref() -> TypeRef { TypeRef { @@ -628,7 +685,10 @@ impl Entity for CreateMailFolderReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CreateMailGroupData { pub _format: i64, #[serde(with = "serde_bytes")] @@ -638,7 +698,6 @@ pub struct CreateMailGroupData { pub mailEncMailboxSessionKey: Vec, pub groupData: InternalGroupData, } - impl Entity for CreateMailGroupData { fn type_ref() -> TypeRef { TypeRef { @@ -648,7 +707,10 @@ impl Entity for CreateMailGroupData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct CustomerAccountCreateData { pub _format: i64, pub accountGroupKeyVersion: i64, @@ -673,7 +735,6 @@ pub struct CustomerAccountCreateData { pub userData: UserAccountUserData, pub userGroupData: InternalGroupData, } - impl Entity for CustomerAccountCreateData { fn type_ref() -> TypeRef { TypeRef { @@ -683,13 +744,15 @@ impl Entity for CustomerAccountCreateData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DefaultAlarmInfo { pub _id: CustomId, pub trigger: String, pub _finalIvs: HashMap, } - impl Entity for DefaultAlarmInfo { fn type_ref() -> TypeRef { TypeRef { @@ -699,13 +762,15 @@ impl Entity for DefaultAlarmInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DeleteGroupData { pub _format: i64, pub restore: bool, pub group: GeneratedId, } - impl Entity for DeleteGroupData { fn type_ref() -> TypeRef { TypeRef { @@ -715,13 +780,15 @@ impl Entity for DeleteGroupData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DeleteMailData { pub _format: i64, pub folder: Option, pub mails: Vec, } - impl Entity for DeleteMailData { fn type_ref() -> TypeRef { TypeRef { @@ -731,14 +798,16 @@ impl Entity for DeleteMailData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DeleteMailFolderData { pub _format: i64, pub folders: Vec, pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for DeleteMailFolderData { fn type_ref() -> TypeRef { TypeRef { @@ -748,7 +817,10 @@ impl Entity for DeleteMailFolderData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DraftAttachment { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -757,7 +829,6 @@ pub struct DraftAttachment { pub existingFile: Option, pub newFile: Option, } - impl Entity for DraftAttachment { fn type_ref() -> TypeRef { TypeRef { @@ -767,7 +838,10 @@ impl Entity for DraftAttachment { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DraftCreateData { pub _format: i64, pub conversationType: i64, @@ -779,7 +853,6 @@ pub struct DraftCreateData { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for DraftCreateData { fn type_ref() -> TypeRef { TypeRef { @@ -789,12 +862,14 @@ impl Entity for DraftCreateData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DraftCreateReturn { pub _format: i64, pub draft: IdTuple, } - impl Entity for DraftCreateReturn { fn type_ref() -> TypeRef { TypeRef { @@ -804,7 +879,10 @@ impl Entity for DraftCreateReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DraftData { pub _id: CustomId, pub bodyText: String, @@ -822,7 +900,6 @@ pub struct DraftData { pub toRecipients: Vec, pub _finalIvs: HashMap, } - impl Entity for DraftData { fn type_ref() -> TypeRef { TypeRef { @@ -832,14 +909,16 @@ impl Entity for DraftData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DraftRecipient { pub _id: CustomId, pub mailAddress: String, pub name: String, pub _finalIvs: HashMap, } - impl Entity for DraftRecipient { fn type_ref() -> TypeRef { TypeRef { @@ -849,7 +928,10 @@ impl Entity for DraftRecipient { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DraftUpdateData { pub _format: i64, pub draft: IdTuple, @@ -857,7 +939,6 @@ pub struct DraftUpdateData { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for DraftUpdateData { fn type_ref() -> TypeRef { TypeRef { @@ -867,14 +948,16 @@ impl Entity for DraftUpdateData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct DraftUpdateReturn { pub _format: i64, pub attachments: Vec, pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for DraftUpdateReturn { fn type_ref() -> TypeRef { TypeRef { @@ -884,7 +967,10 @@ impl Entity for DraftUpdateReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct EmailTemplate { pub _format: i64, pub _id: IdTuple, @@ -899,7 +985,6 @@ pub struct EmailTemplate { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for EmailTemplate { fn type_ref() -> TypeRef { TypeRef { @@ -909,14 +994,16 @@ impl Entity for EmailTemplate { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct EmailTemplateContent { pub _id: CustomId, pub languageCode: String, pub text: String, pub _finalIvs: HashMap, } - impl Entity for EmailTemplateContent { fn type_ref() -> TypeRef { TypeRef { @@ -926,7 +1013,10 @@ impl Entity for EmailTemplateContent { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct EncryptTutanotaPropertiesData { pub _format: i64, #[serde(with = "serde_bytes")] @@ -934,7 +1024,6 @@ pub struct EncryptTutanotaPropertiesData { pub symKeyVersion: i64, pub properties: GeneratedId, } - impl Entity for EncryptTutanotaPropertiesData { fn type_ref() -> TypeRef { TypeRef { @@ -944,14 +1033,16 @@ impl Entity for EncryptTutanotaPropertiesData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct EncryptedMailAddress { pub _id: CustomId, pub address: String, pub name: String, pub _finalIvs: HashMap, } - impl Entity for EncryptedMailAddress { fn type_ref() -> TypeRef { TypeRef { @@ -961,14 +1052,16 @@ impl Entity for EncryptedMailAddress { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct EntropyData { pub _format: i64, #[serde(with = "serde_bytes")] pub userEncEntropy: Vec, pub userKeyVersion: i64, } - impl Entity for EntropyData { fn type_ref() -> TypeRef { TypeRef { @@ -978,7 +1071,10 @@ impl Entity for EntropyData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ExternalUserData { pub _format: i64, #[serde(with = "serde_bytes")] @@ -1003,7 +1099,6 @@ pub struct ExternalUserData { pub verifier: Vec, pub userGroupData: CreateExternalUserGroupData, } - impl Entity for ExternalUserData { fn type_ref() -> TypeRef { TypeRef { @@ -1013,7 +1108,10 @@ impl Entity for ExternalUserData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct TutanotaFile { pub _format: i64, pub _id: IdTuple, @@ -1032,7 +1130,6 @@ pub struct TutanotaFile { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for TutanotaFile { fn type_ref() -> TypeRef { TypeRef { @@ -1042,7 +1139,10 @@ impl Entity for TutanotaFile { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct FileSystem { pub _format: i64, pub _id: GeneratedId, @@ -1055,7 +1155,6 @@ pub struct FileSystem { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for FileSystem { fn type_ref() -> TypeRef { TypeRef { @@ -1065,12 +1164,14 @@ impl Entity for FileSystem { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupInvitationDeleteData { pub _format: i64, pub receivedInvitation: IdTuple, } - impl Entity for GroupInvitationDeleteData { fn type_ref() -> TypeRef { TypeRef { @@ -1080,13 +1181,15 @@ impl Entity for GroupInvitationDeleteData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupInvitationPostData { pub _format: i64, pub internalKeyData: Vec, pub sharedGroupData: SharedGroupData, } - impl Entity for GroupInvitationPostData { fn type_ref() -> TypeRef { TypeRef { @@ -1096,14 +1199,16 @@ impl Entity for GroupInvitationPostData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupInvitationPostReturn { pub _format: i64, pub existingMailAddresses: Vec, pub invalidMailAddresses: Vec, pub invitedMailAddresses: Vec, } - impl Entity for GroupInvitationPostReturn { fn type_ref() -> TypeRef { TypeRef { @@ -1113,7 +1218,10 @@ impl Entity for GroupInvitationPostReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupInvitationPutData { pub _format: i64, #[serde(with = "serde_bytes")] @@ -1124,7 +1232,6 @@ pub struct GroupInvitationPutData { pub userGroupKeyVersion: i64, pub receivedInvitation: IdTuple, } - impl Entity for GroupInvitationPutData { fn type_ref() -> TypeRef { TypeRef { @@ -1134,7 +1241,10 @@ impl Entity for GroupInvitationPutData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct GroupSettings { pub _id: CustomId, pub color: String, @@ -1144,7 +1254,6 @@ pub struct GroupSettings { pub group: GeneratedId, pub _finalIvs: HashMap, } - impl Entity for GroupSettings { fn type_ref() -> TypeRef { TypeRef { @@ -1154,14 +1263,16 @@ impl Entity for GroupSettings { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Header { pub _id: CustomId, pub compressedHeaders: Option>, pub headers: Option, pub _finalIvs: HashMap, } - impl Entity for Header { fn type_ref() -> TypeRef { TypeRef { @@ -1171,7 +1282,10 @@ impl Entity for Header { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ImapFolder { pub _id: CustomId, pub lastseenuid: String, @@ -1179,7 +1293,6 @@ pub struct ImapFolder { pub uidvalidity: String, pub syncInfo: GeneratedId, } - impl Entity for ImapFolder { fn type_ref() -> TypeRef { TypeRef { @@ -1189,7 +1302,10 @@ impl Entity for ImapFolder { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ImapSyncConfiguration { pub _id: CustomId, pub host: String, @@ -1198,7 +1314,6 @@ pub struct ImapSyncConfiguration { pub user: String, pub imapSyncState: Option, } - impl Entity for ImapSyncConfiguration { fn type_ref() -> TypeRef { TypeRef { @@ -1208,7 +1323,10 @@ impl Entity for ImapSyncConfiguration { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ImapSyncState { pub _format: i64, pub _id: GeneratedId, @@ -1216,7 +1334,6 @@ pub struct ImapSyncState { pub _permissions: GeneratedId, pub folders: Vec, } - impl Entity for ImapSyncState { fn type_ref() -> TypeRef { TypeRef { @@ -1226,7 +1343,10 @@ impl Entity for ImapSyncState { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct InboxRule { pub _id: CustomId, #[serde(rename = "type")] @@ -1235,7 +1355,6 @@ pub struct InboxRule { pub targetFolder: IdTuple, pub _finalIvs: HashMap, } - impl Entity for InboxRule { fn type_ref() -> TypeRef { TypeRef { @@ -1245,7 +1364,10 @@ impl Entity for InboxRule { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct InternalGroupData { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -1268,7 +1390,6 @@ pub struct InternalGroupData { pub pubRsaKey: Option>, pub adminGroup: Option, } - impl Entity for InternalGroupData { fn type_ref() -> TypeRef { TypeRef { @@ -1278,7 +1399,10 @@ impl Entity for InternalGroupData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct InternalRecipientKeyData { pub _id: CustomId, pub mailAddress: String, @@ -1288,7 +1412,6 @@ pub struct InternalRecipientKeyData { pub recipientKeyVersion: i64, pub senderKeyVersion: Option, } - impl Entity for InternalRecipientKeyData { fn type_ref() -> TypeRef { TypeRef { @@ -1298,7 +1421,10 @@ impl Entity for InternalRecipientKeyData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct KnowledgeBaseEntry { pub _format: i64, pub _id: IdTuple, @@ -1313,7 +1439,6 @@ pub struct KnowledgeBaseEntry { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for KnowledgeBaseEntry { fn type_ref() -> TypeRef { TypeRef { @@ -1323,13 +1448,15 @@ impl Entity for KnowledgeBaseEntry { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct KnowledgeBaseEntryKeyword { pub _id: CustomId, pub keyword: String, pub _finalIvs: HashMap, } - impl Entity for KnowledgeBaseEntryKeyword { fn type_ref() -> TypeRef { TypeRef { @@ -1339,14 +1466,16 @@ impl Entity for KnowledgeBaseEntryKeyword { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ListUnsubscribeData { pub _format: i64, pub headers: String, pub recipient: String, pub mail: IdTuple, } - impl Entity for ListUnsubscribeData { fn type_ref() -> TypeRef { TypeRef { @@ -1356,7 +1485,10 @@ impl Entity for ListUnsubscribeData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Mail { pub _format: i64, pub _id: IdTuple, @@ -1390,7 +1522,6 @@ pub struct Mail { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for Mail { fn type_ref() -> TypeRef { TypeRef { @@ -1400,7 +1531,10 @@ impl Entity for Mail { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailAddress { pub _id: CustomId, pub address: String, @@ -1408,7 +1542,6 @@ pub struct MailAddress { pub contact: Option, pub _finalIvs: HashMap, } - impl Entity for MailAddress { fn type_ref() -> TypeRef { TypeRef { @@ -1418,14 +1551,16 @@ impl Entity for MailAddress { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailAddressProperties { pub _id: CustomId, pub mailAddress: String, pub senderName: String, pub _finalIvs: HashMap, } - impl Entity for MailAddressProperties { fn type_ref() -> TypeRef { TypeRef { @@ -1435,12 +1570,14 @@ impl Entity for MailAddressProperties { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailBag { pub _id: CustomId, pub mails: GeneratedId, } - impl Entity for MailBag { fn type_ref() -> TypeRef { TypeRef { @@ -1450,7 +1587,10 @@ impl Entity for MailBag { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailBox { pub _format: i64, pub _id: GeneratedId, @@ -1470,7 +1610,6 @@ pub struct MailBox { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for MailBox { fn type_ref() -> TypeRef { TypeRef { @@ -1480,7 +1619,10 @@ impl Entity for MailBox { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailDetails { pub _id: CustomId, pub authStatus: i64, @@ -1490,7 +1632,6 @@ pub struct MailDetails { pub recipients: Recipients, pub replyTos: Vec, } - impl Entity for MailDetails { fn type_ref() -> TypeRef { TypeRef { @@ -1500,7 +1641,10 @@ impl Entity for MailDetails { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailDetailsBlob { pub _format: i64, pub _id: IdTuple, @@ -1513,7 +1657,6 @@ pub struct MailDetailsBlob { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for MailDetailsBlob { fn type_ref() -> TypeRef { TypeRef { @@ -1523,7 +1666,10 @@ impl Entity for MailDetailsBlob { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailDetailsDraft { pub _format: i64, pub _id: IdTuple, @@ -1536,7 +1682,6 @@ pub struct MailDetailsDraft { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for MailDetailsDraft { fn type_ref() -> TypeRef { TypeRef { @@ -1546,12 +1691,14 @@ impl Entity for MailDetailsDraft { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailDetailsDraftsRef { pub _id: CustomId, pub list: GeneratedId, } - impl Entity for MailDetailsDraftsRef { fn type_ref() -> TypeRef { TypeRef { @@ -1561,7 +1708,10 @@ impl Entity for MailDetailsDraftsRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailFolder { pub _format: i64, pub _id: IdTuple, @@ -1580,7 +1730,6 @@ pub struct MailFolder { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for MailFolder { fn type_ref() -> TypeRef { TypeRef { @@ -1590,12 +1739,14 @@ impl Entity for MailFolder { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailFolderRef { pub _id: CustomId, pub folders: GeneratedId, } - impl Entity for MailFolderRef { fn type_ref() -> TypeRef { TypeRef { @@ -1605,7 +1756,10 @@ impl Entity for MailFolderRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailSetEntry { pub _format: i64, pub _id: IdTuple, @@ -1613,7 +1767,6 @@ pub struct MailSetEntry { pub _permissions: GeneratedId, pub mail: IdTuple, } - impl Entity for MailSetEntry { fn type_ref() -> TypeRef { TypeRef { @@ -1623,7 +1776,10 @@ impl Entity for MailSetEntry { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailboxGroupRoot { pub _format: i64, pub _id: GeneratedId, @@ -1637,7 +1793,6 @@ pub struct MailboxGroupRoot { pub serverProperties: GeneratedId, pub whitelistRequests: GeneratedId, } - impl Entity for MailboxGroupRoot { fn type_ref() -> TypeRef { TypeRef { @@ -1647,7 +1802,10 @@ impl Entity for MailboxGroupRoot { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailboxProperties { pub _format: i64, pub _id: GeneratedId, @@ -1661,7 +1819,6 @@ pub struct MailboxProperties { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for MailboxProperties { fn type_ref() -> TypeRef { TypeRef { @@ -1671,7 +1828,10 @@ impl Entity for MailboxProperties { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MailboxServerProperties { pub _format: i64, pub _id: GeneratedId, @@ -1679,7 +1839,6 @@ pub struct MailboxServerProperties { pub _permissions: GeneratedId, pub whitelistProtectionEnabled: bool, } - impl Entity for MailboxServerProperties { fn type_ref() -> TypeRef { TypeRef { @@ -1689,14 +1848,16 @@ impl Entity for MailboxServerProperties { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct MoveMailData { pub _format: i64, pub mails: Vec, pub sourceFolder: Option, pub targetFolder: IdTuple, } - impl Entity for MoveMailData { fn type_ref() -> TypeRef { TypeRef { @@ -1706,7 +1867,10 @@ impl Entity for MoveMailData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct NewDraftAttachment { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -1717,7 +1881,6 @@ pub struct NewDraftAttachment { pub encMimeType: Vec, pub referenceTokens: Vec, } - impl Entity for NewDraftAttachment { fn type_ref() -> TypeRef { TypeRef { @@ -1727,13 +1890,15 @@ impl Entity for NewDraftAttachment { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct NewsId { pub _id: CustomId, pub newsItemId: GeneratedId, pub newsItemName: String, } - impl Entity for NewsId { fn type_ref() -> TypeRef { TypeRef { @@ -1743,12 +1908,14 @@ impl Entity for NewsId { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct NewsIn { pub _format: i64, pub newsItemId: Option, } - impl Entity for NewsIn { fn type_ref() -> TypeRef { TypeRef { @@ -1758,12 +1925,14 @@ impl Entity for NewsIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct NewsOut { pub _format: i64, pub newsItemIds: Vec, } - impl Entity for NewsOut { fn type_ref() -> TypeRef { TypeRef { @@ -1773,7 +1942,10 @@ impl Entity for NewsOut { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct NotificationMail { pub _id: CustomId, pub bodyText: String, @@ -1782,7 +1954,6 @@ pub struct NotificationMail { pub recipientName: String, pub subject: String, } - impl Entity for NotificationMail { fn type_ref() -> TypeRef { TypeRef { @@ -1792,7 +1963,10 @@ impl Entity for NotificationMail { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct OutOfOfficeNotification { pub _format: i64, pub _id: GeneratedId, @@ -1803,7 +1977,6 @@ pub struct OutOfOfficeNotification { pub startDate: Option, pub notifications: Vec, } - impl Entity for OutOfOfficeNotification { fn type_ref() -> TypeRef { TypeRef { @@ -1813,7 +1986,10 @@ impl Entity for OutOfOfficeNotification { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct OutOfOfficeNotificationMessage { pub _id: CustomId, pub message: String, @@ -1821,7 +1997,6 @@ pub struct OutOfOfficeNotificationMessage { #[serde(rename = "type")] pub r#type: i64, } - impl Entity for OutOfOfficeNotificationMessage { fn type_ref() -> TypeRef { TypeRef { @@ -1831,12 +2006,14 @@ impl Entity for OutOfOfficeNotificationMessage { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct OutOfOfficeNotificationRecipientList { pub _id: CustomId, pub list: GeneratedId, } - impl Entity for OutOfOfficeNotificationRecipientList { fn type_ref() -> TypeRef { TypeRef { @@ -1846,13 +2023,15 @@ impl Entity for OutOfOfficeNotificationRecipientList { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PhishingMarkerWebsocketData { pub _format: i64, pub lastId: GeneratedId, pub markers: Vec, } - impl Entity for PhishingMarkerWebsocketData { fn type_ref() -> TypeRef { TypeRef { @@ -1862,12 +2041,14 @@ impl Entity for PhishingMarkerWebsocketData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct PhotosRef { pub _id: CustomId, pub files: GeneratedId, } - impl Entity for PhotosRef { fn type_ref() -> TypeRef { TypeRef { @@ -1877,12 +2058,14 @@ impl Entity for PhotosRef { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ReceiveInfoServiceData { pub _format: i64, pub language: String, } - impl Entity for ReceiveInfoServiceData { fn type_ref() -> TypeRef { TypeRef { @@ -1892,14 +2075,16 @@ impl Entity for ReceiveInfoServiceData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Recipients { pub _id: CustomId, pub bccRecipients: Vec, pub ccRecipients: Vec, pub toRecipients: Vec, } - impl Entity for Recipients { fn type_ref() -> TypeRef { TypeRef { @@ -1909,7 +2094,10 @@ impl Entity for Recipients { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct RemoteImapSyncInfo { pub _format: i64, pub _id: IdTuple, @@ -1918,7 +2106,6 @@ pub struct RemoteImapSyncInfo { pub seen: bool, pub message: IdTuple, } - impl Entity for RemoteImapSyncInfo { fn type_ref() -> TypeRef { TypeRef { @@ -1928,7 +2115,10 @@ impl Entity for RemoteImapSyncInfo { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ReportMailPostData { pub _format: i64, #[serde(with = "serde_bytes")] @@ -1936,7 +2126,6 @@ pub struct ReportMailPostData { pub reportType: i64, pub mailId: IdTuple, } - impl Entity for ReportMailPostData { fn type_ref() -> TypeRef { TypeRef { @@ -1946,13 +2135,15 @@ impl Entity for ReportMailPostData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct ReportedMailFieldMarker { pub _id: CustomId, pub marker: String, pub status: i64, } - impl Entity for ReportedMailFieldMarker { fn type_ref() -> TypeRef { TypeRef { @@ -1962,7 +2153,10 @@ impl Entity for ReportedMailFieldMarker { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SecureExternalRecipientKeyData { pub _id: CustomId, pub kdfVersion: i64, @@ -1980,7 +2174,6 @@ pub struct SecureExternalRecipientKeyData { pub saltHash: Option>, pub userGroupKeyVersion: i64, } - impl Entity for SecureExternalRecipientKeyData { fn type_ref() -> TypeRef { TypeRef { @@ -1990,7 +2183,10 @@ impl Entity for SecureExternalRecipientKeyData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SendDraftData { pub _format: i64, #[serde(with = "serde_bytes")] @@ -2009,7 +2205,6 @@ pub struct SendDraftData { pub secureExternalRecipientKeyData: Vec, pub symEncInternalRecipientKeyData: Vec, } - impl Entity for SendDraftData { fn type_ref() -> TypeRef { TypeRef { @@ -2019,7 +2214,10 @@ impl Entity for SendDraftData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SendDraftReturn { pub _format: i64, pub messageId: String, @@ -2027,7 +2225,6 @@ pub struct SendDraftReturn { pub notifications: Vec, pub sentMail: IdTuple, } - impl Entity for SendDraftReturn { fn type_ref() -> TypeRef { TypeRef { @@ -2037,7 +2234,10 @@ impl Entity for SendDraftReturn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SharedGroupData { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -2056,7 +2256,6 @@ pub struct SharedGroupData { pub sharedGroupEncSharedGroupInfoKey: Vec, pub sharedGroupKeyVersion: i64, } - impl Entity for SharedGroupData { fn type_ref() -> TypeRef { TypeRef { @@ -2066,12 +2265,14 @@ impl Entity for SharedGroupData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SpamResults { pub _id: CustomId, pub list: GeneratedId, } - impl Entity for SpamResults { fn type_ref() -> TypeRef { TypeRef { @@ -2081,12 +2282,14 @@ impl Entity for SpamResults { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Subfiles { pub _id: CustomId, pub files: GeneratedId, } - impl Entity for Subfiles { fn type_ref() -> TypeRef { TypeRef { @@ -2096,7 +2299,10 @@ impl Entity for Subfiles { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct SymEncInternalRecipientKeyData { pub _id: CustomId, pub mailAddress: String, @@ -2105,7 +2311,6 @@ pub struct SymEncInternalRecipientKeyData { pub symKeyVersion: i64, pub keyGroup: GeneratedId, } - impl Entity for SymEncInternalRecipientKeyData { fn type_ref() -> TypeRef { TypeRef { @@ -2115,7 +2320,10 @@ impl Entity for SymEncInternalRecipientKeyData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct TemplateGroupRoot { pub _format: i64, pub _id: GeneratedId, @@ -2129,7 +2337,6 @@ pub struct TemplateGroupRoot { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for TemplateGroupRoot { fn type_ref() -> TypeRef { TypeRef { @@ -2139,12 +2346,14 @@ impl Entity for TemplateGroupRoot { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct TranslationGetIn { pub _format: i64, pub lang: String, } - impl Entity for TranslationGetIn { fn type_ref() -> TypeRef { TypeRef { @@ -2154,13 +2363,15 @@ impl Entity for TranslationGetIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct TranslationGetOut { pub _format: i64, pub giftCardSubject: String, pub invitationSubject: String, } - impl Entity for TranslationGetOut { fn type_ref() -> TypeRef { TypeRef { @@ -2170,7 +2381,10 @@ impl Entity for TranslationGetOut { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct TutanotaProperties { pub _format: i64, pub _id: GeneratedId, @@ -2196,7 +2410,6 @@ pub struct TutanotaProperties { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for TutanotaProperties { fn type_ref() -> TypeRef { TypeRef { @@ -2206,13 +2419,15 @@ impl Entity for TutanotaProperties { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UpdateMailFolderData { pub _format: i64, pub folder: IdTuple, pub newParent: Option, } - impl Entity for UpdateMailFolderData { fn type_ref() -> TypeRef { TypeRef { @@ -2222,14 +2437,16 @@ impl Entity for UpdateMailFolderData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserAccountCreateData { pub _format: i64, pub date: Option, pub userData: UserAccountUserData, pub userGroupData: InternalGroupData, } - impl Entity for UserAccountCreateData { fn type_ref() -> TypeRef { TypeRef { @@ -2239,7 +2456,10 @@ impl Entity for UserAccountCreateData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserAccountUserData { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -2284,7 +2504,6 @@ pub struct UserAccountUserData { #[serde(with = "serde_bytes")] pub verifier: Vec, } - impl Entity for UserAccountUserData { fn type_ref() -> TypeRef { TypeRef { @@ -2294,7 +2513,10 @@ impl Entity for UserAccountUserData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserAreaGroupData { pub _id: CustomId, #[serde(with = "serde_bytes")] @@ -2312,7 +2534,6 @@ pub struct UserAreaGroupData { pub userKeyVersion: i64, pub adminGroup: Option, } - impl Entity for UserAreaGroupData { fn type_ref() -> TypeRef { TypeRef { @@ -2322,12 +2543,14 @@ impl Entity for UserAreaGroupData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserAreaGroupDeleteData { pub _format: i64, pub group: GeneratedId, } - impl Entity for UserAreaGroupDeleteData { fn type_ref() -> TypeRef { TypeRef { @@ -2337,12 +2560,14 @@ impl Entity for UserAreaGroupDeleteData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserAreaGroupPostData { pub _format: i64, pub groupData: UserAreaGroupData, } - impl Entity for UserAreaGroupPostData { fn type_ref() -> TypeRef { TypeRef { @@ -2352,7 +2577,10 @@ impl Entity for UserAreaGroupPostData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UserSettingsGroupRoot { pub _format: i64, pub _id: GeneratedId, @@ -2368,7 +2596,6 @@ pub struct UserSettingsGroupRoot { pub _errors: Option, pub _finalIvs: HashMap, } - impl Entity for UserSettingsGroupRoot { fn type_ref() -> TypeRef { TypeRef { @@ -2377,3 +2604,6 @@ impl Entity for UserSettingsGroupRoot { } } } + + + diff --git a/tuta-sdk/rust/sdk/src/entities/usage.rs b/tuta-sdk/rust/sdk/src/entities/usage.rs index 30c6ca12856c..29498be13e2e 100644 --- a/tuta-sdk/rust/sdk/src/entities/usage.rs +++ b/tuta-sdk/rust/sdk/src/entities/usage.rs @@ -2,7 +2,7 @@ use super::*; use serde::{Deserialize, Serialize}; -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UsageTestAssignment { pub _id: CustomId, pub name: String, @@ -11,7 +11,6 @@ pub struct UsageTestAssignment { pub variant: Option, pub stages: Vec, } - impl Entity for UsageTestAssignment { fn type_ref() -> TypeRef { TypeRef { @@ -21,12 +20,14 @@ impl Entity for UsageTestAssignment { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UsageTestAssignmentIn { pub _format: i64, pub testDeviceId: Option, } - impl Entity for UsageTestAssignmentIn { fn type_ref() -> TypeRef { TypeRef { @@ -36,13 +37,15 @@ impl Entity for UsageTestAssignmentIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UsageTestAssignmentOut { pub _format: i64, pub testDeviceId: GeneratedId, pub assignments: Vec, } - impl Entity for UsageTestAssignmentOut { fn type_ref() -> TypeRef { TypeRef { @@ -52,7 +55,10 @@ impl Entity for UsageTestAssignmentOut { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UsageTestMetricConfig { pub _id: CustomId, pub name: String, @@ -60,7 +66,6 @@ pub struct UsageTestMetricConfig { pub r#type: i64, pub configValues: Vec, } - impl Entity for UsageTestMetricConfig { fn type_ref() -> TypeRef { TypeRef { @@ -70,13 +75,15 @@ impl Entity for UsageTestMetricConfig { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UsageTestMetricConfigValue { pub _id: CustomId, pub key: String, pub value: String, } - impl Entity for UsageTestMetricConfigValue { fn type_ref() -> TypeRef { TypeRef { @@ -86,13 +93,15 @@ impl Entity for UsageTestMetricConfigValue { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UsageTestMetricData { pub _id: CustomId, pub name: String, pub value: String, } - impl Entity for UsageTestMetricData { fn type_ref() -> TypeRef { TypeRef { @@ -102,7 +111,10 @@ impl Entity for UsageTestMetricData { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UsageTestParticipationIn { pub _format: i64, pub stage: i64, @@ -110,7 +122,6 @@ pub struct UsageTestParticipationIn { pub testId: GeneratedId, pub metrics: Vec, } - impl Entity for UsageTestParticipationIn { fn type_ref() -> TypeRef { TypeRef { @@ -120,7 +131,10 @@ impl Entity for UsageTestParticipationIn { } } -#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug)] + + + +#[derive(uniffi::Record, Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct UsageTestStage { pub _id: CustomId, pub maxPings: i64, @@ -128,7 +142,6 @@ pub struct UsageTestStage { pub name: String, pub metrics: Vec, } - impl Entity for UsageTestStage { fn type_ref() -> TypeRef { TypeRef { @@ -137,3 +150,6 @@ impl Entity for UsageTestStage { } } } + + + diff --git a/tuta-sdk/rust/sdk/src/instance_mapper.rs b/tuta-sdk/rust/sdk/src/instance_mapper.rs index ffdbc9ca7c69..f7b1ded59a6d 100644 --- a/tuta-sdk/rust/sdk/src/instance_mapper.rs +++ b/tuta-sdk/rust/sdk/src/instance_mapper.rs @@ -30,7 +30,6 @@ impl InstanceMapper { T::deserialize(de) } - #[allow(unused)] // TODO: Remove this when implementing mutations for entities pub fn serialize_entity( &self, entity: T, diff --git a/tuta-sdk/rust/sdk/src/json_serializer.rs b/tuta-sdk/rust/sdk/src/json_serializer.rs index 3cf69efe8948..adfc3c50cb42 100644 --- a/tuta-sdk/rust/sdk/src/json_serializer.rs +++ b/tuta-sdk/rust/sdk/src/json_serializer.rs @@ -14,7 +14,6 @@ use crate::metamodel::{ AssociationType, Cardinality, ElementType, ModelValue, TypeModel, ValueType, }; use crate::type_model_provider::TypeModelProvider; -use crate::util::resolve_default_value; use crate::{IdTuple, TypeRef}; impl From<&TypeModel> for TypeRef { @@ -78,7 +77,7 @@ impl JsonSerializer { if value_type.encrypted { ElementValue::String(String::new()) } else { - resolve_default_value(&value_type.value_type) + value_type.value_type.get_default() } }, (Cardinality::One | Cardinality::ZeroOrOne, JsonElement::String(s)) diff --git a/tuta-sdk/rust/sdk/src/key_loader_facade.rs b/tuta-sdk/rust/sdk/src/key_loader_facade.rs index 143d68e4e24e..fb7b45145455 100644 --- a/tuta-sdk/rust/sdk/src/key_loader_facade.rs +++ b/tuta-sdk/rust/sdk/src/key_loader_facade.rs @@ -597,12 +597,12 @@ mod tests { .await .unwrap(); match keypair { - AsymmetricKeyPair::RSAKeyPair(_) => panic!("key_loader_facade.load_key_pair() returned an RSAKeyPair! Expected PQKeyPairs."), - AsymmetricKeyPair::RSAEccKeyPair(_) => panic!("key_loader_facade.load_key_pair() returned an RSAEccKeyPair! Expected PQKeyPairs."), - AsymmetricKeyPair::PQKeyPairs(pq_key_pair) => { - assert_eq!(pq_key_pair, *former_key_pairs_decrypted.get(i).expect("former_key_pairs_decrypted should have FORMER_KEYS keys")) - } - } + AsymmetricKeyPair::RSAKeyPair(_) => panic!("key_loader_facade.load_key_pair() returned an RSAKeyPair! Expected PQKeyPairs."), + AsymmetricKeyPair::RSAEccKeyPair(_) => panic!("key_loader_facade.load_key_pair() returned an RSAEccKeyPair! Expected PQKeyPairs."), + AsymmetricKeyPair::PQKeyPairs(pq_key_pair) => { + assert_eq!(pq_key_pair, *former_key_pairs_decrypted.get(i).expect("former_key_pairs_decrypted should have FORMER_KEYS keys")) + }, + } } } @@ -677,11 +677,11 @@ mod tests { .await .unwrap(); match keypair { - GenericAesKey::Aes128(_) => panic!("key_loader_facade.load_sym_group_key() returned an AES128 key! Expected an AES256 key."), - GenericAesKey::Aes256(returned_group_key) => { - assert_eq!(returned_group_key, *former_keys_decrypted.get(i).expect("former_keys_decrypted should have FORMER_KEYS keys")) - } - } + GenericAesKey::Aes128(_) => panic!("key_loader_facade.load_sym_group_key() returned an AES128 key! Expected an AES256 key."), + GenericAesKey::Aes256(returned_group_key) => { + assert_eq!(returned_group_key, *former_keys_decrypted.get(i).expect("former_keys_decrypted should have FORMER_KEYS keys")) + }, + } } } diff --git a/tuta-sdk/rust/sdk/src/lib.rs b/tuta-sdk/rust/sdk/src/lib.rs index e8cc08d291a7..23d80c9446ab 100644 --- a/tuta-sdk/rust/sdk/src/lib.rs +++ b/tuta-sdk/rust/sdk/src/lib.rs @@ -1,3 +1,5 @@ +#![macro_use] + use std::collections::HashMap; use std::error::Error; use std::fmt::{Debug, Display, Formatter}; @@ -37,12 +39,12 @@ use crate::typed_entity_client::TypedEntityClient; #[mockall_double::double] use crate::user_facade::UserFacade; -mod crypto; +pub mod crypto; mod crypto_entity_client; -mod custom_id; +pub mod custom_id; pub mod date; mod element_value; -mod entities; +pub mod entities; mod entity_client; pub mod generated_id; mod instance_mapper; @@ -56,8 +58,9 @@ mod mail_facade; mod metamodel; pub mod rest_client; mod rest_error; +pub mod services; mod simple_crypto; -mod tutanota_constants; +pub mod tutanota_constants; mod type_model_provider; mod typed_entity_client; mod user_facade; @@ -225,7 +228,7 @@ pub enum ListLoadDirection { } /// A set of keys used to identify an element within a List Element Type -#[derive(uniffi::Record, Debug, PartialEq, Clone, Serialize, Deserialize)] +#[derive(uniffi::Record, Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] pub struct IdTuple { pub list_id: GeneratedId, pub element_id: GeneratedId, @@ -250,7 +253,7 @@ impl Display for IdTuple { } /// Contains an error from the SDK to be handled by the consuming code over the FFI -#[derive(Error, Debug, uniffi::Error)] +#[derive(Error, Debug, uniffi::Error, Eq, PartialEq, Clone)] pub enum ApiCallError { #[error("Rest client error, source: {source}")] RestClient { @@ -267,12 +270,12 @@ pub enum ApiCallError { } impl ApiCallError { - fn internal(message: String) -> ApiCallError { + pub fn internal(message: String) -> ApiCallError { ApiCallError::InternalSdkError { error_message: message, } } - fn internal_with_err(error: E, message: &str) -> ApiCallError { + pub fn internal_with_err(error: E, message: &str) -> ApiCallError { ApiCallError::InternalSdkError { error_message: format!("{}: {}", error, message), } @@ -351,10 +354,10 @@ impl Encode for ElementValue { } #[cfg(test)] mod tests { + use crate::entities::tutanota::Mail; + use crate::serialize_mail; use crate::util::test_utils::create_test_entity; - use super::*; - #[test] fn test_serialize_mail_does_not_panic() { let mail = create_test_entity::(); diff --git a/tuta-sdk/rust/sdk/src/login/credentials.rs b/tuta-sdk/rust/sdk/src/login/credentials.rs index e4f1a9df391f..8871d1fce638 100644 --- a/tuta-sdk/rust/sdk/src/login/credentials.rs +++ b/tuta-sdk/rust/sdk/src/login/credentials.rs @@ -1,16 +1,16 @@ use crate::generated_id::GeneratedId; -#[derive(uniffi::Record)] +#[derive(uniffi::Record, Clone)] pub struct Credentials { - pub login: String, - pub user_id: GeneratedId, - pub access_token: String, - pub encrypted_passphrase_key: Vec, - pub credential_type: CredentialType, + pub login: String, + pub user_id: GeneratedId, + pub access_token: String, + pub encrypted_passphrase_key: Vec, + pub credential_type: CredentialType, } #[derive(uniffi::Enum, Debug, PartialEq, Clone)] pub enum CredentialType { - Internal, - External, + Internal, + External, } diff --git a/tuta-sdk/rust/sdk/src/login/login_facade.rs b/tuta-sdk/rust/sdk/src/login/login_facade.rs index a2a841cc6043..a05e1c9c0a9f 100644 --- a/tuta-sdk/rust/sdk/src/login/login_facade.rs +++ b/tuta-sdk/rust/sdk/src/login/login_facade.rs @@ -22,7 +22,7 @@ use crate::ApiCallError::InternalSdkError; use crate::{ApiCallError, IdTuple}; /// Error that may occur during login and session creation -#[derive(Error, Debug, uniffi::Error)] +#[derive(Error, Debug, uniffi::Error, Clone)] pub enum LoginError { #[error("InvalidSessionId: {error_message}")] InvalidSessionId { error_message: String }, diff --git a/tuta-sdk/rust/sdk/src/metamodel.rs b/tuta-sdk/rust/sdk/src/metamodel.rs index d1d78840220b..dbefcb3daa2a 100644 --- a/tuta-sdk/rust/sdk/src/metamodel.rs +++ b/tuta-sdk/rust/sdk/src/metamodel.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; +use crate::date::DateTime; +use crate::element_value::ElementValue; use serde::Deserialize; /// A kind of element that can appear in the model @@ -34,6 +36,21 @@ pub enum ValueType { CompressedString, } +impl ValueType { + pub fn get_default(&self) -> ElementValue { + match self { + ValueType::String | ValueType::CompressedString => ElementValue::String(String::new()), + ValueType::Number => ElementValue::Number(0), + ValueType::Bytes => ElementValue::Bytes(Vec::new()), + ValueType::Date => ElementValue::Date(DateTime::default()), + ValueType::Boolean => ElementValue::Bool(false), + ValueType::GeneratedId | ValueType::CustomId => { + panic!("{self:?} is not a valid value type") + }, + } + } +} + /// Associations (references and aggregations) have two dimensions: the type they reference and /// their cardinality. #[derive(Deserialize, PartialEq, Clone)] @@ -47,7 +64,7 @@ pub enum Cardinality { } /// Relationships between elements are described as association -#[derive(Deserialize, Clone)] +#[derive(Deserialize, Clone, Eq, PartialEq)] pub enum AssociationType { /// References [ElementType] by id #[serde(rename = "ELEMENT_ASSOCIATION")] diff --git a/tuta-sdk/rust/sdk/src/rest_client.rs b/tuta-sdk/rust/sdk/src/rest_client.rs index 96c28fa72a22..e2f330bd9a23 100644 --- a/tuta-sdk/rust/sdk/src/rest_client.rs +++ b/tuta-sdk/rust/sdk/src/rest_client.rs @@ -3,32 +3,42 @@ use thiserror::Error; #[derive(uniffi::Enum, Debug, PartialEq, Hash, Eq)] pub enum HttpMethod { - GET, - POST, - PUT, - DELETE, + GET, + POST, + PUT, + DELETE, } /// HTTP(S) data inserted by the `RestClient` in its REST requests -#[derive(uniffi::Record)] +#[derive(uniffi::Record, Debug, Eq, PartialEq)] pub struct RestClientOptions { - pub headers: HashMap, - pub body: Option>, + pub headers: HashMap, + pub body: Option>, } /// An error thrown by the `RestClient` (the injected HTTP client Kotlin/Swift/JavaScript) -#[derive(Error, Debug, uniffi::Error)] +#[derive(Error, Debug, uniffi::Error, Eq, PartialEq, Clone)] pub enum RestClientError { - #[error("Network error")] - NetworkError, + #[error("Network error")] + NetworkError, + #[error("Invalid URL")] + InvalidURL(String), + #[error("Failed handshake")] + FailedHandshake, + #[error("Invalid request")] + InvalidRequest, + #[error("Invalid response")] + InvalidResponse, + #[error("failed tls setup")] + FailedTlsSetup, } /// HTTP(S) data contained within a response from the backend #[derive(uniffi::Record, Clone)] pub struct RestResponse { - pub status: u32, - pub headers: HashMap, - pub body: Option>, + pub status: u32, + pub headers: HashMap, + pub body: Option>, } /// Provides a Rust SDK level interface for performing REST requests @@ -37,11 +47,11 @@ pub struct RestResponse { #[cfg_attr(test, mockall::automock)] #[async_trait::async_trait] pub trait RestClient: Send + Sync + 'static { - /// Performs an HTTP request with binary data in its body using the injected HTTP client - async fn request_binary( - &self, - url: String, - method: HttpMethod, - options: RestClientOptions, - ) -> Result; + /// Performs an HTTP request with binary data in its body using the injected HTTP client + async fn request_binary( + &self, + url: String, + method: HttpMethod, + options: RestClientOptions, + ) -> Result; } diff --git a/tuta-sdk/rust/sdk/src/rest_error.rs b/tuta-sdk/rust/sdk/src/rest_error.rs index 9d94fac9b6f9..45ffbed257f4 100644 --- a/tuta-sdk/rust/sdk/src/rest_error.rs +++ b/tuta-sdk/rust/sdk/src/rest_error.rs @@ -8,7 +8,7 @@ use thiserror::Error; pub struct ParseFailureError; /// The failed preconditions of a REST request to attempt downgrading an account -#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq)] +#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq, Clone)] pub enum UnsubscribeFailureReason { #[error("TooManyEnabledUsers")] TooManyEnabledUsers, @@ -67,7 +67,7 @@ impl FromStr for UnsubscribeFailureReason { } // legacy, should be deleted after clients older than 3.114 have been disabled. -#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq)] +#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq, Clone)] pub enum BookingFailureReason { #[error("TooManyDomains")] TooManyDomains, @@ -101,7 +101,7 @@ impl FromStr for BookingFailureReason { } /// The failed preconditions of a REST request to attempt setting a whitelabel domain -#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq)] +#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq, Clone)] pub enum DomainFailureReason { // Renamed from FAILURE_CONTACT_FORM_ACTIVE #[error("ContactFormActive")] @@ -134,7 +134,7 @@ impl FromStr for DomainFailureReason { /// The failed preconditions of a REST request to attempt setting a custom domain /// for an email address -#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq)] +#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq, Clone)] pub enum CustomDomainFailureReason { #[error("LimitReached")] LimitReached, @@ -156,7 +156,7 @@ impl FromStr for CustomDomainFailureReason { } /// The failed preconditions of unsuccessful template group operations -#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq)] +#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq, Clone)] pub enum TemplateGroupFailureReason { #[error("BusinessFeatureRequired")] BusinessFeatureRequired, @@ -178,7 +178,7 @@ impl FromStr for TemplateGroupFailureReason { } /// The failed preconditions of unsuccessful usage test operations -#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq)] +#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq, Clone)] pub enum UsageTestFailureReason { #[error("InvalidState")] InvalidState, @@ -209,7 +209,7 @@ impl FromStr for UsageTestFailureReason { } /// The possible failed preconditions when unsuccessfully performing an operation on the backend -#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq)] +#[derive(Error, Debug, uniffi::Enum, Eq, PartialEq, Clone)] pub enum PreconditionFailedReason { #[error("UnsubscribeFailure")] UnsubscribeFailure(#[from] UnsubscribeFailureReason), @@ -262,7 +262,7 @@ impl FromStr for PreconditionFailedReason { } /// The possible error responses from the server -#[derive(Error, Debug, uniffi::Error, Eq, PartialEq)] +#[derive(Error, Debug, uniffi::Error, Eq, PartialEq, Clone)] pub enum HttpError { #[error("Connection lost")] ConnectionError, diff --git a/tuta-sdk/rust/sdk/src/services/accounting.rs b/tuta-sdk/rust/sdk/src/services/accounting.rs new file mode 100644 index 000000000000..2c9647d516c6 --- /dev/null +++ b/tuta-sdk/rust/sdk/src/services/accounting.rs @@ -0,0 +1,11 @@ +#![allow(unused_imports, dead_code, unused_variables)] +use crate::ApiCallError; +use crate::entities::Entity; +use crate::services::{PostService, GetService, PutService, DeleteService, Service, Executor, ExtraServiceParams}; +use crate::rest_client::HttpMethod; +use crate::services::hidden::Nothing; +use crate::entities::accounting::CustomerAccountReturn; +pub struct CustomerAccountService; + +crate::service_impl!(base, CustomerAccountService, "accounting/customeraccountservice", 7); +crate::service_impl!(GET, CustomerAccountService, (), CustomerAccountReturn); diff --git a/tuta-sdk/rust/sdk/src/services/base.rs b/tuta-sdk/rust/sdk/src/services/base.rs new file mode 100644 index 000000000000..213f090e5f55 --- /dev/null +++ b/tuta-sdk/rust/sdk/src/services/base.rs @@ -0,0 +1,6 @@ +#![allow(unused_imports, dead_code, unused_variables)] +use crate::ApiCallError; +use crate::entities::Entity; +use crate::services::{PostService, GetService, PutService, DeleteService, Service, Executor, ExtraServiceParams}; +use crate::rest_client::HttpMethod; +use crate::services::hidden::Nothing; \ No newline at end of file diff --git a/tuta-sdk/rust/sdk/src/services/generator.rs b/tuta-sdk/rust/sdk/src/services/generator.rs new file mode 100644 index 000000000000..1020b62c15d6 --- /dev/null +++ b/tuta-sdk/rust/sdk/src/services/generator.rs @@ -0,0 +1,64 @@ +#[macro_export] +macro_rules! __service_input_type { + (()) => {$crate::services::hidden::Nothing}; + ($i:ident) => {$i}; + } + +#[macro_export] +macro_rules! __service_input_value { + ($data:tt, ()) => {None}; + ($data:tt, $i: tt) => {Some($data)}; +} + +#[macro_export] +macro_rules! __service_handle_response { + ($x:expr, $res:expr, ()) => { return Ok(()); }; + ($x:expr, $res:expr, $i: tt) => { return $x.handle_response::($res).await; }; +} + + +#[macro_export] +macro_rules! service_impl { + (base, $service_name: ty, $service_path: expr, $service_version: expr) => { + impl $crate::services::Service for $service_name { + const PATH: &'static str = $service_path; + const VERSION: u32 = $service_version; + } + }; + + (POST, $service_name: ty, $input_type:tt, $output_type:tt) => { + $crate::service_impl!(@internal, $crate::services::PostService, POST, $service_name, $input_type, $output_type); + }; + + (PUT, $service_name: ty, $input_type:tt, $output_type:tt) => { + $crate::service_impl!(@internal, $crate::services::PutService, PUT, $service_name, $input_type, $output_type); + }; + + (GET, $service_name: ty, $input_type:tt, $output_type:tt) => { + $crate::service_impl!(@internal, $crate::services::GetService, GET, $service_name, $input_type, $output_type); + }; + + (DELETE, $service_name: ty, $input_type:tt, $output_type:tt) => { + $crate::service_impl!(@internal, $crate::services::DeleteService, DELETE, $service_name, $input_type, $output_type); + }; + + (@internal, $service_trait: path, $method_name: ident, $service_name: ty, $input_type:tt, $output_type:tt) => { + impl $service_trait for $service_name { + type Input = $input_type; + type Output = $output_type; + + async fn $method_name( + x: &impl $crate::services::Executor, + data: $input_type, + params: $crate::services::ExtraServiceParams + ) -> ::core::result::Result { + let res = x.do_request::( + $crate::__service_input_value!(data, $input_type), + $crate::rest_client::HttpMethod::$method_name, + params + ).await?; + $crate::__service_handle_response!(x, res, $output_type); + } + } + }; +} diff --git a/tuta-sdk/rust/sdk/src/services/gossip.rs b/tuta-sdk/rust/sdk/src/services/gossip.rs new file mode 100644 index 000000000000..213f090e5f55 --- /dev/null +++ b/tuta-sdk/rust/sdk/src/services/gossip.rs @@ -0,0 +1,6 @@ +#![allow(unused_imports, dead_code, unused_variables)] +use crate::ApiCallError; +use crate::entities::Entity; +use crate::services::{PostService, GetService, PutService, DeleteService, Service, Executor, ExtraServiceParams}; +use crate::rest_client::HttpMethod; +use crate::services::hidden::Nothing; \ No newline at end of file diff --git a/tuta-sdk/rust/sdk/src/services/mod.rs b/tuta-sdk/rust/sdk/src/services/mod.rs new file mode 100644 index 000000000000..c19a88976798 --- /dev/null +++ b/tuta-sdk/rust/sdk/src/services/mod.rs @@ -0,0 +1,139 @@ +use crate::crypto::key::GenericAesKey; +use crate::services::hidden::Executor; +use crate::ApiCallError; +use std::collections::HashMap; + +pub mod accounting; +pub mod base; +pub mod generator; +pub mod gossip; +pub mod monitor; +pub mod service_executor; +pub mod storage; +pub mod sys; +pub mod tutanota; +pub mod usage; + +// todo: how to be used from tutanota-mimimi +// #[cfg(test)] +pub mod test_services; + +pub trait Service { + const PATH: &'static str; + const VERSION: u32; +} + +pub trait GetService: Service { + type Input; + type Output; + + #[allow(non_snake_case)] + async fn GET( + x: &impl Executor, + data: Self::Input, + params: ExtraServiceParams, + ) -> Result; +} + +pub trait PostService: Service { + type Input; + type Output; + + #[allow(non_snake_case)] + async fn POST( + x: &impl Executor, + data: Self::Input, + params: ExtraServiceParams, + ) -> Result; +} + +pub trait PutService: Service { + type Input; + type Output; + + #[allow(non_snake_case)] + async fn PUT( + x: &impl Executor, + data: Self::Input, + params: ExtraServiceParams, + ) -> Result; +} + +pub trait DeleteService: Service { + type Input; + type Output; + + #[allow(non_snake_case)] + async fn DELETE( + x: &impl Executor, + data: Self::Input, + params: ExtraServiceParams, + ) -> Result; +} + +pub enum SuspensionBehavior { + Suspend, + Throw, +} + +#[derive(Default)] +pub struct ExtraServiceParams { + pub query_params: Option>, + pub session_key: Option, + pub extra_headers: Option>, + pub suspension_behavior: Option, + /** override origin for the request */ + pub base_url: Option, +} + +mod hidden { + use crate::entities::Entity; + use crate::rest_client::HttpMethod; + use crate::services::{ExtraServiceParams, Service}; + use crate::{ApiCallError, TypeRef}; + use serde::{Deserialize, Serialize, Serializer}; + + /// Type that allows us to call the executor even + /// if the service doesn't have an input or output. + /// it is unimportable outside of the services module and uninstantiable because the enum + /// has no variants. + /// using this construct prevents anyone outside the services module from trying to obtain + /// the type model of this entity type or to return it from a service call. + /// + /// also thought about just providing impl Entity for () but then we'd have problems with people + /// trying to use that impl by accident and Entity::type_ref returning an Option. + /// also we'd still need a way to actually construct the output when we notice that there's + /// nothing to serialize, and just deciding that Service::Output and () are the same thing + /// doesn't work. + pub enum Nothing {} + impl Entity for Nothing { + fn type_ref() -> TypeRef { + unreachable!() + } + } + + impl Serialize for Nothing { + fn serialize(&self, _serializer: S) -> Result + where + S: Serializer, + { + unreachable!() + } + } + + pub trait Executor { + async fn do_request( + &self, + data: Option, + method: HttpMethod, + extra_service_params: ExtraServiceParams, + ) -> Result>, ApiCallError> + where + S: Service, + I: Entity + Serialize; + + async fn handle_response(&self, body: Option>) -> Result + where + O: Entity + Deserialize<'static>; + } +} diff --git a/tuta-sdk/rust/sdk/src/services/monitor.rs b/tuta-sdk/rust/sdk/src/services/monitor.rs new file mode 100644 index 000000000000..8e21549ed79d --- /dev/null +++ b/tuta-sdk/rust/sdk/src/services/monitor.rs @@ -0,0 +1,21 @@ +#![allow(unused_imports, dead_code, unused_variables)] +use crate::ApiCallError; +use crate::entities::Entity; +use crate::services::{PostService, GetService, PutService, DeleteService, Service, Executor, ExtraServiceParams}; +use crate::rest_client::HttpMethod; +use crate::services::hidden::Nothing; +use crate::entities::monitor::WriteCounterData; +use crate::entities::monitor::ReadCounterData; +use crate::entities::monitor::ReadCounterReturn; +use crate::entities::monitor::ReportErrorIn; +pub struct CounterService; + +crate::service_impl!(base, CounterService, "monitor/counterservice", 28); +crate::service_impl!(POST, CounterService, WriteCounterData, ()); +crate::service_impl!(GET, CounterService, ReadCounterData, ReadCounterReturn); + + +pub struct ReportErrorService; + +crate::service_impl!(base, ReportErrorService, "monitor/reporterrorservice", 28); +crate::service_impl!(POST, ReportErrorService, ReportErrorIn, ()); diff --git a/tuta-sdk/rust/sdk/src/services/service_executor.rs b/tuta-sdk/rust/sdk/src/services/service_executor.rs new file mode 100644 index 000000000000..c364c30e9421 --- /dev/null +++ b/tuta-sdk/rust/sdk/src/services/service_executor.rs @@ -0,0 +1,648 @@ +#[mockall_double::double] +use crate::crypto::crypto_facade::CryptoFacade; +#[mockall_double::double] +use crate::entities::entity_facade::EntityFacade; +use crate::entities::Entity; +use crate::instance_mapper::InstanceMapper; +use crate::json_element::RawEntity; +use crate::json_serializer::JsonSerializer; +use crate::metamodel::TypeModel; +use crate::rest_client::{HttpMethod, RestClient, RestClientOptions}; +use crate::rest_error::HttpError; +use crate::services::hidden::Executor; +use crate::services::{ + DeleteService, ExtraServiceParams, GetService, PostService, PutService, Service, +}; +use crate::type_model_provider::TypeModelProvider; +use crate::{ApiCallError, HeadersProvider}; +use serde::Deserialize; +use serde::Serialize; +use std::sync::Arc; + +pub struct ServiceExecutor { + auth_headers_provider: Arc, + crypto_facade: Arc, + entity_facade: Arc, + instance_mapper: Arc, + json_serializer: Arc, + rest_client: Arc, + type_model_provider: Arc, + base_url: String, +} +impl ServiceExecutor { + pub fn new( + auth_headers_provider: Arc, + crypto_facade: Arc, + entity_facade: Arc, + instance_mapper: Arc, + json_serializer: Arc, + rest_client: Arc, + type_model_provider: Arc, + ) -> Self { + Self { + auth_headers_provider, + crypto_facade, + entity_facade, + instance_mapper, + json_serializer, + rest_client, + type_model_provider, + base_url: "http://api.tuta.com".to_string(), + } + } + + pub async fn get( + &self, + data: S::Input, + params: ExtraServiceParams, + ) -> Result + where + S: GetService, + { + S::GET(self, data, params).await + } + + pub async fn post( + &self, + data: S::Input, + params: ExtraServiceParams, + ) -> Result + where + S: PostService, + { + S::POST(self, data, params).await + } + + pub async fn put( + &self, + data: S::Input, + params: ExtraServiceParams, + ) -> Result + where + S: PutService, + { + S::PUT(self, data, params).await + } + + pub async fn delete( + &self, + data: S::Input, + params: ExtraServiceParams, + ) -> Result + where + S: DeleteService, + { + S::DELETE(self, data, params).await + } +} + +impl Executor for ServiceExecutor { + async fn do_request( + &self, + data: Option, + method: HttpMethod, + extra_service_params: ExtraServiceParams, + ) -> Result>, ApiCallError> + where + S: Service, + I: Entity + Serialize, + { + let url = format!( + "{}/rest/{}", + if let Some(url) = extra_service_params.base_url { + url.clone() + } else { + self.base_url.clone() + }, + S::PATH, + ); + let model_version: u32 = S::VERSION; + + let body: Option> = if let Some(input_entity) = data { + let parsed_entity = self + .instance_mapper + .serialize_entity(input_entity) + .map_err(|e| { + ApiCallError::internal_with_err(e, "failed to convert to ParsedEntity") + })?; + let input_type_ref = I::type_ref(); + let type_model = self + .type_model_provider + .get_type_model(input_type_ref.app, input_type_ref.type_) + .ok_or(ApiCallError::internal(format!( + "type {:?} does not exist", + input_type_ref + )))?; + let encrypted_parsed_entity = self.entity_facade.encrypt_and_map_to_literal( + type_model, + &parsed_entity, + extra_service_params.session_key, + )?; + + let raw_entity = self + .json_serializer + .serialize(&I::type_ref(), encrypted_parsed_entity)?; + let bytes = serde_json::to_vec::(&raw_entity).map_err(|e| { + ApiCallError::internal_with_err(e, "failed to serialize input to string") + })?; + Some(bytes) + } else { + None + }; + + let mut headers = self.auth_headers_provider.provide_headers(model_version); + if let Some(extra_headers) = extra_service_params.extra_headers { + headers.extend(extra_headers); + } + + let response = self + .rest_client + .request_binary(url, method, RestClientOptions { body, headers }) + .await?; + let precondition = response.headers.get("precondition"); + match response.status { + 200 | 201 => Ok(response.body), + _ => Err(ApiCallError::ServerResponseError { + source: HttpError::from_http_response(response.status, precondition)?, + }), + } + } + + async fn handle_response( + &self, + body: Option>, + ) -> Result + where + OutputType: Entity + Deserialize<'static>, + { + let response_bytes = body.expect("no body"); + let response_entity = serde_json::from_slice::(response_bytes.as_slice()) + .map_err(|e| ApiCallError::internal_with_err(e, "Failed to serialize instance"))?; + let output_type_ref = &OutputType::type_ref(); + let mut parsed_entity = self + .json_serializer + .parse(output_type_ref, response_entity)?; + let type_model: &TypeModel = self + .type_model_provider + .get_type_model(output_type_ref.app, output_type_ref.type_) + .expect("invalid type ref!"); + + if type_model.marked_encrypted() { + let possible_session_key = self + .crypto_facade + .resolve_session_key(&mut parsed_entity, type_model) + .await + .map_err(|error| { + ApiCallError::internal(format!( + "Failed to resolve session key for service response '{}'; {}", + type_model.name, error + )) + })?; + match possible_session_key { + Some(session_key) => { + let decrypted_entity = self.entity_facade.decrypt_and_map( + type_model, + parsed_entity, + session_key, + )?; + let typed_entity = self + .instance_mapper + .parse_entity::(decrypted_entity) + .map_err(|e| { + ApiCallError::internal_with_err( + e, + "Failed to parse encrypted entity into proper types", + ) + })?; + Ok(typed_entity) + }, + // `resolve_session_key()` only returns none if the entity is unencrypted, so + // no need to handle it + None => { + unreachable!() + }, + } + } else { + let typed_entity = self + .instance_mapper + .parse_entity::(parsed_entity) + .map_err(|error| { + ApiCallError::internal_with_err( + error, + "Failed to parse unencrypted entity into proper types", + ) + })?; + Ok(typed_entity) + } + } +} + +#[cfg(test)] +mod tests { + #[mockall_double::double] + use crate::crypto::crypto_facade::CryptoFacade; + use crate::crypto::crypto_facade::{MockCryptoFacade, ResolvedSessionKey}; + use crate::crypto::key::GenericAesKey; + use crate::crypto::AES_256_KEY_SIZE; + use crate::date::DateTime; + use crate::element_value::ElementValue; + use crate::entities::entity_facade::MockEntityFacade; + use crate::instance_mapper::InstanceMapper; + use crate::json_element::RawEntity; + use crate::json_serializer::JsonSerializer; + use crate::rest_client::{HttpMethod, MockRestClient, RestResponse}; + use crate::services::service_executor::ServiceExecutor; + use crate::services::test_services::{ + HelloEncInput, HelloEncOutput, HelloEncryptedService, HelloUnEncInput, HelloUnEncOutput, + HelloUnEncryptedService, APP_VERSION_STR, + }; + use crate::services::{test_services, ExtraServiceParams, GetService}; + use crate::type_model_provider::TypeModelProvider; + use crate::HeadersProvider; + use base64::prelude::BASE64_STANDARD; + use base64::Engine; + use std::collections::HashMap; + use std::sync::Arc; + + #[tokio::test] + pub async fn post_should_map_unencrypted_data_and_response() { + let hello_input_data = HelloUnEncInput { + message: "Something".to_string(), + }; + let executor = maps_unencrypted_data_and_response(HttpMethod::POST).await; + let result = executor + .post::(hello_input_data, ExtraServiceParams::default()) + .await; + + assert_eq!( + Ok(HelloUnEncOutput { + timestamp: DateTime::from_millis(3000), + answer: "Response to some request".to_string(), + }), + result + ); + } + + #[tokio::test] + pub async fn put_should_map_unencrypted_data_and_response() { + let hello_input_data = HelloUnEncInput { + message: "Something".to_string(), + }; + let executor = maps_unencrypted_data_and_response(HttpMethod::PUT).await; + let result = executor + .put::(hello_input_data, ExtraServiceParams::default()) + .await; + + assert_eq!( + Ok(HelloUnEncOutput { + timestamp: DateTime::from_millis(3000), + answer: "Response to some request".to_string(), + }), + result + ); + } + + #[tokio::test] + pub async fn get_should_map_unencrypted_data_and_response() { + let hello_input_data = HelloUnEncInput { + message: "Something".to_string(), + }; + let executor = maps_unencrypted_data_and_response(HttpMethod::GET).await; + let result = executor + .get::(hello_input_data, ExtraServiceParams::default()) + .await; + + assert_eq!( + Ok(HelloUnEncOutput { + timestamp: DateTime::from_millis(3000), + answer: "Response to some request".to_string(), + }), + result + ); + } + + #[tokio::test] + pub async fn delete_should_map_unencrypted_data_and_response() { + let hello_input_data = HelloUnEncInput { + message: "Something".to_string(), + }; + let executor = maps_unencrypted_data_and_response(HttpMethod::DELETE).await; + let result = executor + .delete::(hello_input_data, ExtraServiceParams::default()) + .await; + + assert_eq!( + Ok(HelloUnEncOutput { + timestamp: DateTime::from_millis(3000), + answer: "Response to some request".to_string(), + }), + result + ); + } + + #[tokio::test] + pub async fn post_should_decrypt_map_encrypted_data_and_response() { + let session_key = + GenericAesKey::from_bytes(&rand::random::<[u8; AES_256_KEY_SIZE]>()).unwrap(); + let executor = + maps_encrypted_data_and_response_data(HttpMethod::POST, session_key.clone()).await; + + let mut params = ExtraServiceParams::default(); + params.session_key = Some(session_key.clone()); + let input_entity = HelloEncInput { + message: "my encrypted request".to_string(), + }; + + let result = executor + .post::(input_entity, params) + .await; + assert_eq!( + Ok(HelloEncOutput { + answer: "my secret response".to_string(), + timestamp: DateTime::from_millis(3000), + _finalIvs: HashMap::new() + }), + result + ); + } + + #[tokio::test] + pub async fn put_should_decrypt_map_encrypted_data_and_response() { + let session_key = + GenericAesKey::from_bytes(&rand::random::<[u8; AES_256_KEY_SIZE]>()).unwrap(); + let executor = + maps_encrypted_data_and_response_data(HttpMethod::PUT, session_key.clone()).await; + + let mut params = ExtraServiceParams::default(); + params.session_key = Some(session_key.clone()); + let input_entity = HelloEncInput { + message: "my encrypted request".to_string(), + }; + + let result = executor + .put::(input_entity, params) + .await; + assert_eq!( + Ok(HelloEncOutput { + answer: "my secret response".to_string(), + timestamp: DateTime::from_millis(3000), + _finalIvs: HashMap::new() + }), + result + ); + } + + #[tokio::test] + pub async fn get_should_decrypt_map_encrypted_data_and_response() { + let session_key = + GenericAesKey::from_bytes(&rand::random::<[u8; AES_256_KEY_SIZE]>()).unwrap(); + let executor = + maps_encrypted_data_and_response_data(HttpMethod::GET, session_key.clone()).await; + + let mut params = ExtraServiceParams::default(); + params.session_key = Some(session_key.clone()); + let input_entity = HelloEncInput { + message: "my encrypted request".to_string(), + }; + + let result = executor + .get::(input_entity, params) + .await; + assert_eq!( + Ok(HelloEncOutput { + answer: "my secret response".to_string(), + timestamp: DateTime::from_millis(3000), + _finalIvs: HashMap::new() + }), + result + ); + } + + #[tokio::test] + pub async fn delete_should_decrypt_map_encrypted_data_and_response() { + let session_key = + GenericAesKey::from_bytes(&rand::random::<[u8; AES_256_KEY_SIZE]>()).unwrap(); + let executor = + maps_encrypted_data_and_response_data(HttpMethod::DELETE, session_key.clone()).await; + + let mut params = ExtraServiceParams::default(); + params.session_key = Some(session_key.clone()); + let input_entity = HelloEncInput { + message: "my encrypted request".to_string(), + }; + + let result = executor + .delete::(input_entity, params) + .await; + assert_eq!( + Ok(HelloEncOutput { + answer: "my secret response".to_string(), + timestamp: DateTime::from_millis(3000), + _finalIvs: HashMap::new() + }), + result + ); + } + + fn setup() -> ServiceExecutor { + let mut model_provider_map = HashMap::new(); + test_services::extend_model_resolver(&mut model_provider_map); + let type_model_provider: Arc = + Arc::new(TypeModelProvider::new(model_provider_map)); + + let crypto_facade = Arc::new(CryptoFacade::default()); + let entity_facade = Arc::new(MockEntityFacade::default()); + let auth_headers_provider = Arc::new(HeadersProvider::new( + "1.2.3".to_string(), + "access_token".to_string(), + )); + let instance_mapper = Arc::new(InstanceMapper::new()); + let json_serializer = Arc::new(JsonSerializer::new(type_model_provider.clone())); + let rest_client = Arc::new(MockRestClient::new()); + + ServiceExecutor::new( + auth_headers_provider, + crypto_facade, + entity_facade, + instance_mapper, + json_serializer, + rest_client, + type_model_provider.clone(), + ) + } + + async fn maps_unencrypted_data_and_response(http_method: HttpMethod) -> ServiceExecutor { + let executor = setup(); + let rest_client; + let entity_facade; + unsafe { + rest_client = Arc::as_ptr(&executor.rest_client) + .cast::() + .cast_mut() + .as_mut() + .unwrap(); + entity_facade = Arc::as_ptr(&executor.entity_facade) + .cast::() + .cast_mut() + .as_mut() + .unwrap(); + } + + rest_client + .expect_request_binary() + .return_once(move |url, method, opts| { + assert_eq!( + "http://api.tuta.com/rest/test/unencrypted-hello", + url.as_str() + ); + assert_eq!(http_method, method); + + let expected_headers = [ + ("v", APP_VERSION_STR), + ("accessToken", "access_token"), + ("cv", "1.2.3"), + ] + .into_iter() + .map(|(a, b)| (a.to_string(), b.to_string())) + .collect::>(); + assert_eq!(expected_headers, opts.headers); + let expected_body = + serde_json::from_str::(r#"{"message":"Something"}"#).unwrap(); + let body = + serde_json::from_slice::(opts.body.unwrap().as_slice()).unwrap(); + assert_eq!(expected_body, body); + + Ok(RestResponse { + status: 200, + headers: HashMap::new(), + body: Some( + br#"{"answer":"Response to some request","timestamp":"3000"}"#.to_vec(), + ), + }) + }); + + entity_facade + .expect_encrypt_and_map_to_literal() + .return_once(|_, entity, sk| { + assert_eq!(None, sk); + Ok(entity.clone()) + }); + + executor + } + + pub async fn maps_encrypted_data_and_response_data( + http_method: HttpMethod, + session_key: GenericAesKey, + ) -> ServiceExecutor { + let executor = setup(); + let crypto_facade; + let rest_client; + let entity_facade; + unsafe { + crypto_facade = Arc::as_ptr(&executor.crypto_facade) + .cast::() + .cast_mut() + .as_mut() + .unwrap(); + rest_client = Arc::as_ptr(&executor.rest_client) + .cast::() + .cast_mut() + .as_mut() + .unwrap(); + + entity_facade = Arc::as_ptr(&executor.entity_facade) + .cast::() + .cast_mut() + .as_mut() + .unwrap(); + } + let owner_enc_session_key = [rand::random(); 32].to_vec(); + + rest_client + .expect_request_binary() + .return_once(move |url, method, opts| { + assert_eq!( + "http://api.tuta.com/rest/test/encrypted-hello", + url.as_str() + ); + assert_eq!(http_method, method); + let expected_body = + serde_json::from_str::(r#"{"message": "my encrypted request"}"#) + .unwrap(); + let body = + serde_json::from_slice::(opts.body.unwrap().as_slice()).unwrap(); + assert_eq!(expected_body, body); + let expected_headers = [ + ("accessToken", "access_token"), + ("cv", "1.2.3"), + ("v", APP_VERSION_STR), + ] + .into_iter() + .map(|(a, b)| (a.to_string(), b.to_string())) + .collect::>(); + assert_eq!(expected_headers, opts.headers); + Ok(RestResponse { + status: 200, + headers: HashMap::new(), + body: Some( + br#"{ "answer":"bXkgc2VjcmV0IHJlc3BvbnNl","timestamp":"MzAwMA==" }"# + .to_vec(), + ), + }) + }); + + let session_key_clone = session_key.clone(); + crypto_facade + .expect_resolve_session_key() + .returning(move |_entity, model| { + assert_eq!(("test", "HelloEncOutput"), (model.app, model.name)); + assert_eq!(true, model.marked_encrypted()); + + Ok(Some(ResolvedSessionKey { + session_key: session_key_clone.clone(), + owner_enc_session_key: owner_enc_session_key.clone(), + })) + }); + + let session_key_clone = session_key.clone(); + entity_facade + .expect_encrypt_and_map_to_literal() + .return_once(move |_, instance, sk| { + assert_eq!(Some(session_key_clone), sk); + Ok(instance.clone()) + }); + + let session_key_clone = session_key.clone(); + entity_facade.expect_decrypt_and_map().return_once( + move |_, mut entity, resolved_session_key| { + assert_eq!(session_key_clone, resolved_session_key.session_key); + assert_eq!( + &ElementValue::Bytes(BASE64_STANDARD.decode(r#"MzAwMA=="#).unwrap()), + entity.get("timestamp").unwrap() + ); + assert_eq!( + &ElementValue::Bytes( + BASE64_STANDARD + .decode(r#"bXkgc2VjcmV0IHJlc3BvbnNl"#) + .unwrap() + ), + entity.get("answer").unwrap() + ); + + entity.insert( + "answer".to_string(), + ElementValue::String(String::from("my secret response")), + ); + entity.insert( + "timestamp".to_string(), + ElementValue::Date(DateTime::from_millis(3000)), + ); + entity.insert("_finalIvs".to_string(), ElementValue::Dict(HashMap::new())); + Ok(entity.clone()) + }, + ); + + executor + } +} diff --git a/tuta-sdk/rust/sdk/src/services/storage.rs b/tuta-sdk/rust/sdk/src/services/storage.rs new file mode 100644 index 000000000000..03a8713cfce8 --- /dev/null +++ b/tuta-sdk/rust/sdk/src/services/storage.rs @@ -0,0 +1,30 @@ +#![allow(unused_imports, dead_code, unused_variables)] +use crate::ApiCallError; +use crate::entities::Entity; +use crate::services::{PostService, GetService, PutService, DeleteService, Service, Executor, ExtraServiceParams}; +use crate::rest_client::HttpMethod; +use crate::services::hidden::Nothing; +use crate::entities::storage::BlobAccessTokenPostIn; +use crate::entities::storage::BlobAccessTokenPostOut; +use crate::entities::storage::BlobReferencePutIn; +use crate::entities::storage::BlobReferenceDeleteIn; +use crate::entities::storage::BlobPostOut; +use crate::entities::storage::BlobGetIn; +pub struct BlobAccessTokenService; + +crate::service_impl!(base, BlobAccessTokenService, "storage/blobaccesstokenservice", 9); +crate::service_impl!(POST, BlobAccessTokenService, BlobAccessTokenPostIn, BlobAccessTokenPostOut); + + +pub struct BlobReferenceService; + +crate::service_impl!(base, BlobReferenceService, "storage/blobreferenceservice", 9); +crate::service_impl!(PUT, BlobReferenceService, BlobReferencePutIn, ()); +crate::service_impl!(DELETE, BlobReferenceService, BlobReferenceDeleteIn, ()); + + +pub struct BlobService; + +crate::service_impl!(base, BlobService, "storage/blobservice", 9); +crate::service_impl!(POST, BlobService, (), BlobPostOut); +crate::service_impl!(GET, BlobService, BlobGetIn, ()); diff --git a/tuta-sdk/rust/sdk/src/services/sys.rs b/tuta-sdk/rust/sdk/src/services/sys.rs new file mode 100644 index 000000000000..2479973fc3cd --- /dev/null +++ b/tuta-sdk/rust/sdk/src/services/sys.rs @@ -0,0 +1,408 @@ +#![allow(unused_imports, dead_code, unused_variables)] +use crate::ApiCallError; +use crate::entities::Entity; +use crate::services::{PostService, GetService, PutService, DeleteService, Service, Executor, ExtraServiceParams}; +use crate::rest_client::HttpMethod; +use crate::services::hidden::Nothing; +use crate::entities::sys::AdminGroupKeyRotationPostIn; +use crate::entities::sys::AffiliatePartnerKpiServiceGetOut; +use crate::entities::sys::AlarmServicePost; +use crate::entities::sys::AutoLoginDataReturn; +use crate::entities::sys::AutoLoginPostReturn; +use crate::entities::sys::AutoLoginDataGet; +use crate::entities::sys::AutoLoginDataDelete; +use crate::entities::sys::BrandingDomainData; +use crate::entities::sys::BrandingDomainGetReturn; +use crate::entities::sys::BrandingDomainDeleteData; +use crate::entities::sys::ChangeKdfPostIn; +use crate::entities::sys::ChangePasswordPostIn; +use crate::entities::sys::CloseSessionServicePost; +use crate::entities::sys::CreateCustomerServerPropertiesData; +use crate::entities::sys::CreateCustomerServerPropertiesReturn; +use crate::entities::sys::CustomDomainCheckGetIn; +use crate::entities::sys::CustomDomainCheckGetOut; +use crate::entities::sys::CustomDomainData; +use crate::entities::sys::CustomDomainReturn; +use crate::entities::sys::CustomerAccountTerminationPostIn; +use crate::entities::sys::CustomerAccountTerminationPostOut; +use crate::entities::sys::PublicKeyGetOut; +use crate::entities::sys::DeleteCustomerData; +use crate::entities::sys::DebitServicePutData; +use crate::entities::sys::DomainMailAddressAvailabilityData; +use crate::entities::sys::DomainMailAddressAvailabilityReturn; +use crate::entities::sys::ExternalPropertiesReturn; +use crate::entities::sys::GiftCardRedeemData; +use crate::entities::sys::GiftCardRedeemGetReturn; +use crate::entities::sys::GiftCardCreateData; +use crate::entities::sys::GiftCardCreateReturn; +use crate::entities::sys::GiftCardGetReturn; +use crate::entities::sys::GiftCardDeleteData; +use crate::entities::sys::GroupKeyRotationInfoGetOut; +use crate::entities::sys::GroupKeyRotationPostIn; +use crate::entities::sys::InvoiceDataGetIn; +use crate::entities::sys::InvoiceDataGetOut; +use crate::entities::sys::LocationServiceGetReturn; +use crate::entities::sys::MailAddressAliasServiceData; +use crate::entities::sys::MailAddressAliasGetIn; +use crate::entities::sys::MailAddressAliasServiceReturn; +use crate::entities::sys::MailAddressAliasServiceDataDelete; +use crate::entities::sys::MembershipAddData; +use crate::entities::sys::MembershipPutIn; +use crate::entities::sys::MembershipRemoveData; +use crate::entities::sys::MultipleMailAddressAvailabilityData; +use crate::entities::sys::MultipleMailAddressAvailabilityReturn; +use crate::entities::sys::PaymentDataServicePostData; +use crate::entities::sys::PaymentDataServiceGetData; +use crate::entities::sys::PaymentDataServiceGetReturn; +use crate::entities::sys::PaymentDataServicePutData; +use crate::entities::sys::PaymentDataServicePutReturn; +use crate::entities::sys::PlanServiceGetOut; +use crate::entities::sys::PriceServiceData; +use crate::entities::sys::PriceServiceReturn; +use crate::entities::sys::PublicKeyGetIn; +use crate::entities::sys::PublicKeyPutIn; +use crate::entities::sys::ReferralCodePostIn; +use crate::entities::sys::ReferralCodePostOut; +use crate::entities::sys::ReferralCodeGetIn; +use crate::entities::sys::RegistrationCaptchaServiceData; +use crate::entities::sys::RegistrationCaptchaServiceGetData; +use crate::entities::sys::RegistrationCaptchaServiceReturn; +use crate::entities::sys::RegistrationServiceData; +use crate::entities::sys::RegistrationReturn; +use crate::entities::sys::ResetFactorsDeleteData; +use crate::entities::sys::ResetPasswordPostIn; +use crate::entities::sys::SaltData; +use crate::entities::sys::SaltReturn; +use crate::entities::sys::SecondFactorAuthAllowedReturn; +use crate::entities::sys::SecondFactorAuthData; +use crate::entities::sys::SecondFactorAuthGetData; +use crate::entities::sys::SecondFactorAuthGetReturn; +use crate::entities::sys::SecondFactorAuthDeleteData; +use crate::entities::sys::CreateSessionData; +use crate::entities::sys::CreateSessionReturn; +use crate::entities::sys::SignOrderProcessingAgreementData; +use crate::entities::sys::SwitchAccountTypePostIn; +use crate::entities::sys::SystemKeysReturn; +use crate::entities::sys::TakeOverDeletedAddressData; +use crate::entities::sys::UpdatePermissionKeyData; +use crate::entities::sys::UpdateSessionKeysPostIn; +use crate::entities::sys::UpgradePriceServiceData; +use crate::entities::sys::UpgradePriceServiceReturn; +use crate::entities::sys::UserGroupKeyRotationPostIn; +use crate::entities::sys::UserDataDelete; +use crate::entities::sys::VersionData; +use crate::entities::sys::VersionReturn; +pub struct AdminGroupKeyRotationService; + +crate::service_impl!(base, AdminGroupKeyRotationService, "sys/admingroupkeyrotationservice", 111); +crate::service_impl!(POST, AdminGroupKeyRotationService, AdminGroupKeyRotationPostIn, ()); + + +pub struct AffiliatePartnerKpiService; + +crate::service_impl!(base, AffiliatePartnerKpiService, "sys/affiliatepartnerkpiservice", 111); +crate::service_impl!(GET, AffiliatePartnerKpiService, (), AffiliatePartnerKpiServiceGetOut); + + +pub struct AlarmService; + +crate::service_impl!(base, AlarmService, "sys/alarmservice", 111); +crate::service_impl!(POST, AlarmService, AlarmServicePost, ()); + + +pub struct AutoLoginService; + +crate::service_impl!(base, AutoLoginService, "sys/autologinservice", 111); +crate::service_impl!(POST, AutoLoginService, AutoLoginDataReturn, AutoLoginPostReturn); +crate::service_impl!(GET, AutoLoginService, AutoLoginDataGet, AutoLoginDataReturn); +crate::service_impl!(DELETE, AutoLoginService, AutoLoginDataDelete, ()); + + +pub struct BrandingDomainService; + +crate::service_impl!(base, BrandingDomainService, "sys/brandingdomainservice", 111); +crate::service_impl!(POST, BrandingDomainService, BrandingDomainData, ()); +crate::service_impl!(GET, BrandingDomainService, (), BrandingDomainGetReturn); +crate::service_impl!(PUT, BrandingDomainService, BrandingDomainData, ()); +crate::service_impl!(DELETE, BrandingDomainService, BrandingDomainDeleteData, ()); + + +pub struct ChangeKdfService; + +crate::service_impl!(base, ChangeKdfService, "sys/changekdfservice", 111); +crate::service_impl!(POST, ChangeKdfService, ChangeKdfPostIn, ()); + + +pub struct ChangePasswordService; + +crate::service_impl!(base, ChangePasswordService, "sys/changepasswordservice", 111); +crate::service_impl!(POST, ChangePasswordService, ChangePasswordPostIn, ()); + + +pub struct CloseSessionService; + +crate::service_impl!(base, CloseSessionService, "sys/closesessionservice", 111); +crate::service_impl!(POST, CloseSessionService, CloseSessionServicePost, ()); + + +pub struct CreateCustomerServerProperties; + +crate::service_impl!(base, CreateCustomerServerProperties, "sys/createcustomerserverproperties", 111); +crate::service_impl!(POST, CreateCustomerServerProperties, CreateCustomerServerPropertiesData, CreateCustomerServerPropertiesReturn); + + +pub struct CustomDomainCheckService; + +crate::service_impl!(base, CustomDomainCheckService, "sys/customdomaincheckservice", 111); +crate::service_impl!(GET, CustomDomainCheckService, CustomDomainCheckGetIn, CustomDomainCheckGetOut); + + +pub struct CustomDomainService; + +crate::service_impl!(base, CustomDomainService, "sys/customdomainservice", 111); +crate::service_impl!(POST, CustomDomainService, CustomDomainData, CustomDomainReturn); +crate::service_impl!(PUT, CustomDomainService, CustomDomainData, ()); +crate::service_impl!(DELETE, CustomDomainService, CustomDomainData, ()); + + +pub struct CustomerAccountTerminationService; + +crate::service_impl!(base, CustomerAccountTerminationService, "sys/customeraccountterminationservice", 111); +crate::service_impl!(POST, CustomerAccountTerminationService, CustomerAccountTerminationPostIn, CustomerAccountTerminationPostOut); + + +pub struct CustomerPublicKeyService; + +crate::service_impl!(base, CustomerPublicKeyService, "sys/customerpublickeyservice", 111); +crate::service_impl!(GET, CustomerPublicKeyService, (), PublicKeyGetOut); + + +pub struct CustomerService; + +crate::service_impl!(base, CustomerService, "sys/customerservice", 111); +crate::service_impl!(DELETE, CustomerService, DeleteCustomerData, ()); + + +pub struct DebitService; + +crate::service_impl!(base, DebitService, "sys/debitservice", 111); +crate::service_impl!(PUT, DebitService, DebitServicePutData, ()); + + +pub struct DomainMailAddressAvailabilityService; + +crate::service_impl!(base, DomainMailAddressAvailabilityService, "sys/domainmailaddressavailabilityservice", 111); +crate::service_impl!(GET, DomainMailAddressAvailabilityService, DomainMailAddressAvailabilityData, DomainMailAddressAvailabilityReturn); + + +pub struct ExternalPropertiesService; + +crate::service_impl!(base, ExternalPropertiesService, "sys/externalpropertiesservice", 111); +crate::service_impl!(GET, ExternalPropertiesService, (), ExternalPropertiesReturn); + + +pub struct GiftCardRedeemService; + +crate::service_impl!(base, GiftCardRedeemService, "sys/giftcardredeemservice", 111); +crate::service_impl!(POST, GiftCardRedeemService, GiftCardRedeemData, ()); +crate::service_impl!(GET, GiftCardRedeemService, GiftCardRedeemData, GiftCardRedeemGetReturn); + + +pub struct GiftCardService; + +crate::service_impl!(base, GiftCardService, "sys/giftcardservice", 111); +crate::service_impl!(POST, GiftCardService, GiftCardCreateData, GiftCardCreateReturn); +crate::service_impl!(GET, GiftCardService, (), GiftCardGetReturn); +crate::service_impl!(DELETE, GiftCardService, GiftCardDeleteData, ()); + + +pub struct GroupKeyRotationInfoService; + +crate::service_impl!(base, GroupKeyRotationInfoService, "sys/groupkeyrotationinfoservice", 111); +crate::service_impl!(GET, GroupKeyRotationInfoService, (), GroupKeyRotationInfoGetOut); + + +pub struct GroupKeyRotationService; + +crate::service_impl!(base, GroupKeyRotationService, "sys/groupkeyrotationservice", 111); +crate::service_impl!(POST, GroupKeyRotationService, GroupKeyRotationPostIn, ()); + + +pub struct InvoiceDataService; + +crate::service_impl!(base, InvoiceDataService, "sys/invoicedataservice", 111); +crate::service_impl!(GET, InvoiceDataService, InvoiceDataGetIn, InvoiceDataGetOut); + + +pub struct LocationService; + +crate::service_impl!(base, LocationService, "sys/locationservice", 111); +crate::service_impl!(GET, LocationService, (), LocationServiceGetReturn); + + +pub struct MailAddressAliasService; + +crate::service_impl!(base, MailAddressAliasService, "sys/mailaddressaliasservice", 111); +crate::service_impl!(POST, MailAddressAliasService, MailAddressAliasServiceData, ()); +crate::service_impl!(GET, MailAddressAliasService, MailAddressAliasGetIn, MailAddressAliasServiceReturn); +crate::service_impl!(DELETE, MailAddressAliasService, MailAddressAliasServiceDataDelete, ()); + + +pub struct MembershipService; + +crate::service_impl!(base, MembershipService, "sys/membershipservice", 111); +crate::service_impl!(POST, MembershipService, MembershipAddData, ()); +crate::service_impl!(PUT, MembershipService, MembershipPutIn, ()); +crate::service_impl!(DELETE, MembershipService, MembershipRemoveData, ()); + + +pub struct MultipleMailAddressAvailabilityService; + +crate::service_impl!(base, MultipleMailAddressAvailabilityService, "sys/multiplemailaddressavailabilityservice", 111); +crate::service_impl!(GET, MultipleMailAddressAvailabilityService, MultipleMailAddressAvailabilityData, MultipleMailAddressAvailabilityReturn); + + +pub struct PaymentDataService; + +crate::service_impl!(base, PaymentDataService, "sys/paymentdataservice", 111); +crate::service_impl!(POST, PaymentDataService, PaymentDataServicePostData, ()); +crate::service_impl!(GET, PaymentDataService, PaymentDataServiceGetData, PaymentDataServiceGetReturn); +crate::service_impl!(PUT, PaymentDataService, PaymentDataServicePutData, PaymentDataServicePutReturn); + + +pub struct PlanService; + +crate::service_impl!(base, PlanService, "sys/planservice", 111); +crate::service_impl!(GET, PlanService, (), PlanServiceGetOut); + + +pub struct PriceService; + +crate::service_impl!(base, PriceService, "sys/priceservice", 111); +crate::service_impl!(GET, PriceService, PriceServiceData, PriceServiceReturn); + + +pub struct PublicKeyService; + +crate::service_impl!(base, PublicKeyService, "sys/publickeyservice", 111); +crate::service_impl!(GET, PublicKeyService, PublicKeyGetIn, PublicKeyGetOut); +crate::service_impl!(PUT, PublicKeyService, PublicKeyPutIn, ()); + + +pub struct ReferralCodeService; + +crate::service_impl!(base, ReferralCodeService, "sys/referralcodeservice", 111); +crate::service_impl!(POST, ReferralCodeService, ReferralCodePostIn, ReferralCodePostOut); +crate::service_impl!(GET, ReferralCodeService, ReferralCodeGetIn, ()); + + +pub struct RegistrationCaptchaService; + +crate::service_impl!(base, RegistrationCaptchaService, "sys/registrationcaptchaservice", 111); +crate::service_impl!(POST, RegistrationCaptchaService, RegistrationCaptchaServiceData, ()); +crate::service_impl!(GET, RegistrationCaptchaService, RegistrationCaptchaServiceGetData, RegistrationCaptchaServiceReturn); + + +pub struct RegistrationService; + +crate::service_impl!(base, RegistrationService, "sys/registrationservice", 111); +crate::service_impl!(POST, RegistrationService, RegistrationServiceData, RegistrationReturn); +crate::service_impl!(GET, RegistrationService, (), RegistrationServiceData); + + +pub struct ResetFactorsService; + +crate::service_impl!(base, ResetFactorsService, "sys/resetfactorsservice", 111); +crate::service_impl!(DELETE, ResetFactorsService, ResetFactorsDeleteData, ()); + + +pub struct ResetPasswordService; + +crate::service_impl!(base, ResetPasswordService, "sys/resetpasswordservice", 111); +crate::service_impl!(POST, ResetPasswordService, ResetPasswordPostIn, ()); + + +pub struct SaltService; + +crate::service_impl!(base, SaltService, "sys/saltservice", 111); +crate::service_impl!(GET, SaltService, SaltData, SaltReturn); + + +pub struct SecondFactorAuthAllowedService; + +crate::service_impl!(base, SecondFactorAuthAllowedService, "sys/secondfactorauthallowedservice", 111); +crate::service_impl!(GET, SecondFactorAuthAllowedService, (), SecondFactorAuthAllowedReturn); + + +pub struct SecondFactorAuthService; + +crate::service_impl!(base, SecondFactorAuthService, "sys/secondfactorauthservice", 111); +crate::service_impl!(POST, SecondFactorAuthService, SecondFactorAuthData, ()); +crate::service_impl!(GET, SecondFactorAuthService, SecondFactorAuthGetData, SecondFactorAuthGetReturn); +crate::service_impl!(DELETE, SecondFactorAuthService, SecondFactorAuthDeleteData, ()); + + +pub struct SessionService; + +crate::service_impl!(base, SessionService, "sys/sessionservice", 111); +crate::service_impl!(POST, SessionService, CreateSessionData, CreateSessionReturn); + + +pub struct SignOrderProcessingAgreementService; + +crate::service_impl!(base, SignOrderProcessingAgreementService, "sys/signorderprocessingagreementservice", 111); +crate::service_impl!(POST, SignOrderProcessingAgreementService, SignOrderProcessingAgreementData, ()); + + +pub struct SwitchAccountTypeService; + +crate::service_impl!(base, SwitchAccountTypeService, "sys/switchaccounttypeservice", 111); +crate::service_impl!(POST, SwitchAccountTypeService, SwitchAccountTypePostIn, ()); + + +pub struct SystemKeysService; + +crate::service_impl!(base, SystemKeysService, "sys/systemkeysservice", 111); +crate::service_impl!(GET, SystemKeysService, (), SystemKeysReturn); + + +pub struct TakeOverDeletedAddressService; + +crate::service_impl!(base, TakeOverDeletedAddressService, "sys/takeoverdeletedaddressservice", 111); +crate::service_impl!(POST, TakeOverDeletedAddressService, TakeOverDeletedAddressData, ()); + + +pub struct UpdatePermissionKeyService; + +crate::service_impl!(base, UpdatePermissionKeyService, "sys/updatepermissionkeyservice", 111); +crate::service_impl!(POST, UpdatePermissionKeyService, UpdatePermissionKeyData, ()); + + +pub struct UpdateSessionKeysService; + +crate::service_impl!(base, UpdateSessionKeysService, "sys/updatesessionkeysservice", 111); +crate::service_impl!(POST, UpdateSessionKeysService, UpdateSessionKeysPostIn, ()); + + +pub struct UpgradePriceService; + +crate::service_impl!(base, UpgradePriceService, "sys/upgradepriceservice", 111); +crate::service_impl!(GET, UpgradePriceService, UpgradePriceServiceData, UpgradePriceServiceReturn); + + +pub struct UserGroupKeyRotationService; + +crate::service_impl!(base, UserGroupKeyRotationService, "sys/usergroupkeyrotationservice", 111); +crate::service_impl!(POST, UserGroupKeyRotationService, UserGroupKeyRotationPostIn, ()); + + +pub struct UserService; + +crate::service_impl!(base, UserService, "sys/userservice", 111); +crate::service_impl!(DELETE, UserService, UserDataDelete, ()); + + +pub struct VersionService; + +crate::service_impl!(base, VersionService, "sys/versionservice", 111); +crate::service_impl!(GET, VersionService, VersionData, VersionReturn); diff --git a/tuta-sdk/rust/sdk/src/services/test_services.rs b/tuta-sdk/rust/sdk/src/services/test_services.rs new file mode 100644 index 000000000000..0e0d46937b07 --- /dev/null +++ b/tuta-sdk/rust/sdk/src/services/test_services.rs @@ -0,0 +1,216 @@ +use crate::date::DateTime; +use crate::entities::{Entity, FinalIv}; +use crate::metamodel::TypeModel; +use crate::services::GetService; +use crate::type_model_provider::{AppName, TypeName}; +use crate::{service_impl, TypeRef}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +pub const APP_NAME: &str = "test"; +pub const APP_VERSION_NUMBER: u32 = 75; +pub const APP_VERSION_STR: &str = "75"; + +pub const HELLO_OUTPUT_ENCRYPTED: &str = r#"{ + "name": "HelloEncOutput", + "since": 7, + "type": "DATA_TRANSFER_TYPE", + "id": 458, + "rootId": "CHR1dGFub3RhAAHK", + "versioned": false, + "encrypted": true, + "values": { + "answer": { + "final": false, + "name": "answer", + "id": 459, + "since": 7, + "type": "String", + "cardinality": "One", + "encrypted": true + }, + "timestamp": { + "final": false, + "name": "timestamp", + "id": 460, + "since": 7, + "type": "Date", + "cardinality": "One", + "encrypted": true + } + }, + "associations": {}, + "app": "test", + "version": "75" + }"#; +pub const HELLO_INPUT_ENCRYPTED: &str = r#"{ + "name": "HelloEncInput", + "since": 7, + "type": "DATA_TRANSFER_TYPE", + "id": 358, + "rootId": "RDR1dGFub3RhAAHK", + "versioned": false, + "encrypted": true, + "values": { + "message": { + "final": false, + "name": "message", + "id": 359, + "since": 7, + "type": "String", + "cardinality": "One", + "encrypted": true + } + }, + "associations": {}, + "app": "test", + "version": "75" + }"#; + +pub const HELLO_OUTPUT_UNENCRYPTED: &str = r#"{ + "name": "HelloUnEncOutput", + "since": 7, + "type": "DATA_TRANSFER_TYPE", + "id": 458, + "rootId": "CHR1dGFub3RhAAHK", + "versioned": false, + "encrypted": false, + "values": { + "answer": { + "final": false, + "name": "answer", + "id": 159, + "since": 7, + "type": "String", + "cardinality": "One", + "encrypted": false + }, + "timestamp": { + "final": false, + "name": "timestamp", + "id": 160, + "since": 7, + "type": "Date", + "cardinality": "One", + "encrypted": false + } + }, + "associations": {}, + "app": "test", + "version": "75" + }"#; +pub const HELLO_INPUT_UNENCRYPTED: &str = r#"{ + "name": "HelloUnEncInput", + "since": 7, + "type": "DATA_TRANSFER_TYPE", + "id": 148, + "rootId": "RDR1dGFub3RhAAHK", + "versioned": false, + "encrypted": false, + "values": { + "message": { + "final": false, + "name": "message", + "id": 149, + "since": 7, + "type": "String", + "cardinality": "One", + "encrypted": false + } + }, + "associations": {}, + "app": "test", + "version": "75" + }"#; + +pub fn extend_model_resolver(model_resolver: &mut HashMap>) { + assert!(model_resolver.get("test").is_none()); + + let enc_input_type_model = serde_json::from_str::(HELLO_INPUT_ENCRYPTED).unwrap(); + let enc_output_type_model = serde_json::from_str::(HELLO_OUTPUT_ENCRYPTED).unwrap(); + let unenc_input_type_model = serde_json::from_str::(HELLO_INPUT_UNENCRYPTED).unwrap(); + let unenc_output_type_model = serde_json::from_str::(HELLO_OUTPUT_UNENCRYPTED).unwrap(); + + let test_types = [ + ("HelloEncInput", enc_input_type_model), + ("HelloEncOutput", enc_output_type_model), + ("HelloUnEncInput", unenc_input_type_model), + ("HelloUnEncOutput", unenc_output_type_model) + ].into_iter().collect(); + model_resolver.insert("test", test_types); +} + + +pub struct HelloEncryptedService; +pub struct HelloUnEncryptedService; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct HelloEncInput { + pub message: String, +} +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct HelloEncOutput { + pub answer: String, + pub timestamp: DateTime, + pub _finalIvs: HashMap, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct HelloUnEncInput { + pub message: String, +} +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct HelloUnEncOutput { + pub answer: String, + pub timestamp: DateTime, +} + +impl Entity for HelloEncInput { + fn type_ref() -> TypeRef { + TypeRef { + app: "test", + type_: "HelloEncInput", + } + } +} + + +impl Entity for HelloEncOutput { + fn type_ref() -> TypeRef { + TypeRef { + app: "test", + type_: "HelloEncOutput", + } + } +} + +impl Entity for HelloUnEncInput { + fn type_ref() -> TypeRef { + TypeRef { + app: "test", + type_: "HelloUnEncInput", + } + } +} + + +impl Entity for HelloUnEncOutput { + fn type_ref() -> TypeRef { + TypeRef { + app: "test", + type_: "HelloUnEncOutput", + } + } +} + +service_impl!(base, HelloEncryptedService, "test/encrypted-hello", APP_VERSION_NUMBER); +service_impl!(POST, HelloEncryptedService, HelloEncInput, HelloEncOutput); +service_impl!(PUT, HelloEncryptedService, HelloEncInput, HelloEncOutput); +service_impl!(GET, HelloEncryptedService, HelloEncInput, HelloEncOutput); +service_impl!(DELETE, HelloEncryptedService, HelloEncInput, HelloEncOutput); + +service_impl!(base, HelloUnEncryptedService, "test/unencrypted-hello", APP_VERSION_NUMBER); +service_impl!(POST, HelloUnEncryptedService, HelloUnEncInput, HelloUnEncOutput); +service_impl!(PUT, HelloUnEncryptedService, HelloUnEncInput, HelloUnEncOutput); +service_impl!(GET, HelloUnEncryptedService, HelloUnEncInput, HelloUnEncOutput); +service_impl!(DELETE, HelloUnEncryptedService, HelloUnEncInput, HelloUnEncOutput); \ No newline at end of file diff --git a/tuta-sdk/rust/sdk/src/services/tutanota.rs b/tuta-sdk/rust/sdk/src/services/tutanota.rs new file mode 100644 index 000000000000..6a369eb9636e --- /dev/null +++ b/tuta-sdk/rust/sdk/src/services/tutanota.rs @@ -0,0 +1,168 @@ +#![allow(unused_imports, dead_code, unused_variables)] +use crate::ApiCallError; +use crate::entities::Entity; +use crate::services::{PostService, GetService, PutService, DeleteService, Service, Executor, ExtraServiceParams}; +use crate::rest_client::HttpMethod; +use crate::services::hidden::Nothing; +use crate::entities::tutanota::UserAreaGroupPostData; +use crate::entities::tutanota::CreateGroupPostReturn; +use crate::entities::tutanota::CalendarDeleteData; +use crate::entities::tutanota::UserAreaGroupDeleteData; +use crate::entities::tutanota::CustomerAccountCreateData; +use crate::entities::tutanota::DraftCreateData; +use crate::entities::tutanota::DraftCreateReturn; +use crate::entities::tutanota::DraftUpdateData; +use crate::entities::tutanota::DraftUpdateReturn; +use crate::entities::tutanota::EncryptTutanotaPropertiesData; +use crate::entities::tutanota::EntropyData; +use crate::entities::tutanota::ExternalUserData; +use crate::entities::tutanota::GroupInvitationPostData; +use crate::entities::tutanota::GroupInvitationPostReturn; +use crate::entities::tutanota::GroupInvitationPutData; +use crate::entities::tutanota::GroupInvitationDeleteData; +use crate::entities::tutanota::ListUnsubscribeData; +use crate::entities::tutanota::CreateMailFolderData; +use crate::entities::tutanota::CreateMailFolderReturn; +use crate::entities::tutanota::UpdateMailFolderData; +use crate::entities::tutanota::DeleteMailFolderData; +use crate::entities::tutanota::CreateMailGroupData; +use crate::entities::tutanota::DeleteGroupData; +use crate::entities::tutanota::DeleteMailData; +use crate::entities::tutanota::MoveMailData; +use crate::entities::tutanota::NewsIn; +use crate::entities::tutanota::NewsOut; +use crate::entities::tutanota::ReceiveInfoServiceData; +use crate::entities::tutanota::ReportMailPostData; +use crate::entities::tutanota::SendDraftData; +use crate::entities::tutanota::SendDraftReturn; +use crate::entities::tutanota::TranslationGetIn; +use crate::entities::tutanota::TranslationGetOut; +use crate::entities::tutanota::UserAccountCreateData; +pub struct CalendarService; + +crate::service_impl!(base, CalendarService, "tutanota/calendarservice", 75); +crate::service_impl!(POST, CalendarService, UserAreaGroupPostData, CreateGroupPostReturn); +crate::service_impl!(DELETE, CalendarService, CalendarDeleteData, ()); + + +pub struct ContactListGroupService; + +crate::service_impl!(base, ContactListGroupService, "tutanota/contactlistgroupservice", 75); +crate::service_impl!(POST, ContactListGroupService, UserAreaGroupPostData, CreateGroupPostReturn); +crate::service_impl!(DELETE, ContactListGroupService, UserAreaGroupDeleteData, ()); + + +pub struct CustomerAccountService; + +crate::service_impl!(base, CustomerAccountService, "tutanota/customeraccountservice", 75); +crate::service_impl!(POST, CustomerAccountService, CustomerAccountCreateData, ()); + + +pub struct DraftService; + +crate::service_impl!(base, DraftService, "tutanota/draftservice", 75); +crate::service_impl!(POST, DraftService, DraftCreateData, DraftCreateReturn); +crate::service_impl!(PUT, DraftService, DraftUpdateData, DraftUpdateReturn); + + +pub struct EncryptTutanotaPropertiesService; + +crate::service_impl!(base, EncryptTutanotaPropertiesService, "tutanota/encrypttutanotapropertiesservice", 75); +crate::service_impl!(POST, EncryptTutanotaPropertiesService, EncryptTutanotaPropertiesData, ()); + + +pub struct EntropyService; + +crate::service_impl!(base, EntropyService, "tutanota/entropyservice", 75); +crate::service_impl!(PUT, EntropyService, EntropyData, ()); + + +pub struct ExternalUserService; + +crate::service_impl!(base, ExternalUserService, "tutanota/externaluserservice", 75); +crate::service_impl!(POST, ExternalUserService, ExternalUserData, ()); + + +pub struct GroupInvitationService; + +crate::service_impl!(base, GroupInvitationService, "tutanota/groupinvitationservice", 75); +crate::service_impl!(POST, GroupInvitationService, GroupInvitationPostData, GroupInvitationPostReturn); +crate::service_impl!(PUT, GroupInvitationService, GroupInvitationPutData, ()); +crate::service_impl!(DELETE, GroupInvitationService, GroupInvitationDeleteData, ()); + + +pub struct ListUnsubscribeService; + +crate::service_impl!(base, ListUnsubscribeService, "tutanota/listunsubscribeservice", 75); +crate::service_impl!(POST, ListUnsubscribeService, ListUnsubscribeData, ()); + + +pub struct MailFolderService; + +crate::service_impl!(base, MailFolderService, "tutanota/mailfolderservice", 75); +crate::service_impl!(POST, MailFolderService, CreateMailFolderData, CreateMailFolderReturn); +crate::service_impl!(PUT, MailFolderService, UpdateMailFolderData, ()); +crate::service_impl!(DELETE, MailFolderService, DeleteMailFolderData, ()); + + +pub struct MailGroupService; + +crate::service_impl!(base, MailGroupService, "tutanota/mailgroupservice", 75); +crate::service_impl!(POST, MailGroupService, CreateMailGroupData, ()); +crate::service_impl!(DELETE, MailGroupService, DeleteGroupData, ()); + + +pub struct MailService; + +crate::service_impl!(base, MailService, "tutanota/mailservice", 75); +crate::service_impl!(DELETE, MailService, DeleteMailData, ()); + + +pub struct MoveMailService; + +crate::service_impl!(base, MoveMailService, "tutanota/movemailservice", 75); +crate::service_impl!(POST, MoveMailService, MoveMailData, ()); + + +pub struct NewsService; + +crate::service_impl!(base, NewsService, "tutanota/newsservice", 75); +crate::service_impl!(POST, NewsService, NewsIn, ()); +crate::service_impl!(GET, NewsService, (), NewsOut); + + +pub struct ReceiveInfoService; + +crate::service_impl!(base, ReceiveInfoService, "tutanota/receiveinfoservice", 75); +crate::service_impl!(POST, ReceiveInfoService, ReceiveInfoServiceData, ()); + + +pub struct ReportMailService; + +crate::service_impl!(base, ReportMailService, "tutanota/reportmailservice", 75); +crate::service_impl!(POST, ReportMailService, ReportMailPostData, ()); + + +pub struct SendDraftService; + +crate::service_impl!(base, SendDraftService, "tutanota/senddraftservice", 75); +crate::service_impl!(POST, SendDraftService, SendDraftData, SendDraftReturn); + + +pub struct TemplateGroupService; + +crate::service_impl!(base, TemplateGroupService, "tutanota/templategroupservice", 75); +crate::service_impl!(POST, TemplateGroupService, UserAreaGroupPostData, CreateGroupPostReturn); +crate::service_impl!(DELETE, TemplateGroupService, UserAreaGroupDeleteData, ()); + + +pub struct TranslationService; + +crate::service_impl!(base, TranslationService, "tutanota/translationservice", 75); +crate::service_impl!(GET, TranslationService, TranslationGetIn, TranslationGetOut); + + +pub struct UserAccountService; + +crate::service_impl!(base, UserAccountService, "tutanota/useraccountservice", 75); +crate::service_impl!(POST, UserAccountService, UserAccountCreateData, ()); diff --git a/tuta-sdk/rust/sdk/src/services/usage.rs b/tuta-sdk/rust/sdk/src/services/usage.rs new file mode 100644 index 000000000000..0218931627c2 --- /dev/null +++ b/tuta-sdk/rust/sdk/src/services/usage.rs @@ -0,0 +1,20 @@ +#![allow(unused_imports, dead_code, unused_variables)] +use crate::ApiCallError; +use crate::entities::Entity; +use crate::services::{PostService, GetService, PutService, DeleteService, Service, Executor, ExtraServiceParams}; +use crate::rest_client::HttpMethod; +use crate::services::hidden::Nothing; +use crate::entities::usage::UsageTestAssignmentIn; +use crate::entities::usage::UsageTestAssignmentOut; +use crate::entities::usage::UsageTestParticipationIn; +pub struct UsageTestAssignmentService; + +crate::service_impl!(base, UsageTestAssignmentService, "usage/usagetestassignmentservice", 2); +crate::service_impl!(POST, UsageTestAssignmentService, UsageTestAssignmentIn, UsageTestAssignmentOut); +crate::service_impl!(PUT, UsageTestAssignmentService, UsageTestAssignmentIn, UsageTestAssignmentOut); + + +pub struct UsageTestParticipationService; + +crate::service_impl!(base, UsageTestParticipationService, "usage/usagetestparticipationservice", 2); +crate::service_impl!(POST, UsageTestParticipationService, UsageTestParticipationIn, ()); diff --git a/tuta-sdk/rust/sdk/src/type_model_provider.rs b/tuta-sdk/rust/sdk/src/type_model_provider.rs index 73e8b695d04f..cab77d38f4c5 100644 --- a/tuta-sdk/rust/sdk/src/type_model_provider.rs +++ b/tuta-sdk/rust/sdk/src/type_model_provider.rs @@ -10,19 +10,19 @@ pub type TypeName = &'static str; /// Contains a map between backend apps and entity/instance types within them pub struct TypeModelProvider { - app_models: HashMap>, + app_models: HashMap>, } impl TypeModelProvider { - pub fn new(app_models: HashMap>) -> TypeModelProvider { - TypeModelProvider { app_models } - } - - /// Gets an entity/instance type with a specified name in a backend app - pub fn get_type_model(&self, app_name: &str, entity_name: &str) -> Option<&TypeModel> { - let app_map = self.app_models.get(app_name)?; - app_map.get(entity_name) - } + pub fn new(app_models: HashMap>) -> TypeModelProvider { + TypeModelProvider { app_models } + } + + /// Gets an entity/instance type with a specified name in a backend app + pub fn get_type_model(&self, app_name: &str, entity_name: &str) -> Option<&TypeModel> { + let app_map = self.app_models.get(app_name)?; + app_map.get(entity_name) + } } // Reads all provided type models into a map. @@ -46,7 +46,7 @@ macro_rules! read_type_models { /// Creates a new `TypeModelProvider` populated with the type models from the JSON type model files pub fn init_type_model_provider() -> TypeModelProvider { - let type_model_map = read_type_models![ + let type_model_map = read_type_models![ "accounting", "base", "gossip", @@ -56,6 +56,6 @@ pub fn init_type_model_provider() -> TypeModelProvider { "tutanota", "usage" ]; - let type_model_provider = TypeModelProvider::new(type_model_map); - type_model_provider + let type_model_provider = TypeModelProvider::new(type_model_map); + type_model_provider } diff --git a/tuta-sdk/rust/sdk/src/util/entity_test_utils.rs b/tuta-sdk/rust/sdk/src/util/entity_test_utils.rs index 533fedae2eca..2d34db973f5d 100644 --- a/tuta-sdk/rust/sdk/src/util/entity_test_utils.rs +++ b/tuta-sdk/rust/sdk/src/util/entity_test_utils.rs @@ -1,12 +1,15 @@ use crate::crypto::key::GenericAesKey; use crate::crypto::Iv; use crate::date::DateTime; +use crate::element_value::ElementValue::Dict; use crate::element_value::{ElementValue, ParsedEntity}; use crate::entities::tutanota::{Mail, MailAddress}; use crate::entities::Entity; +use crate::generated_id::GeneratedId; use crate::type_model_provider::{init_type_model_provider, TypeModelProvider}; use crate::util::test_utils::{create_test_entity, typed_entity_to_parsed_entity}; -use crate::TypeRef; +use crate::{IdTuple, TypeRef}; +use std::collections::HashMap; /// Generates and returns an encrypted Mail ParsedEntity. It also returns the decrypted Mail for comparison pub fn generate_email_entity( @@ -18,6 +21,10 @@ pub fn generate_email_entity( recipient_name: String, ) -> (ParsedEntity, ParsedEntity) { let original_mail = Mail { + _id: IdTuple { + list_id: GeneratedId("O1RT1m6-0R-0".to_string()), + element_id: GeneratedId("O1RT2Dj----0".to_string()), + }, receivedDate: DateTime::from_millis(1470039025474), confidential, subject, diff --git a/tuta-sdk/rust/sdk/src/util/mod.rs b/tuta-sdk/rust/sdk/src/util/mod.rs index 11e8fbb9b53f..50a507757bdb 100644 --- a/tuta-sdk/rust/sdk/src/util/mod.rs +++ b/tuta-sdk/rust/sdk/src/util/mod.rs @@ -198,18 +198,6 @@ pub fn array_cast_size( } } -pub(crate) fn resolve_default_value(value_type: &ValueType) -> ElementValue { - match value_type { - ValueType::String => ElementValue::String(String::new()), - ValueType::Number => ElementValue::Number(0), - ValueType::Bytes => ElementValue::Bytes(Vec::new()), - ValueType::Date => ElementValue::Date(DateTime::default()), - ValueType::Boolean => ElementValue::Bool(false), - ValueType::CompressedString => ElementValue::String(String::new()), - v => unreachable!("Invalid type {v:?}"), - } -} - #[cfg(test)] mod test { use super::*; diff --git a/tuta-sdk/rust/sdk/src/util/test_utils.rs b/tuta-sdk/rust/sdk/src/util/test_utils.rs index 374b60e9fc7f..50f64a2af80f 100644 --- a/tuta-sdk/rust/sdk/src/util/test_utils.rs +++ b/tuta-sdk/rust/sdk/src/util/test_utils.rs @@ -172,28 +172,28 @@ fn create_test_entity_dict_with_provider( Cardinality::Any => ElementValue::Array(Vec::new()), Cardinality::One => { match value.value_type { - ValueType::String => ElementValue::String(Default::default()), - ValueType::Number => ElementValue::Number(Default::default()), - ValueType::Bytes => ElementValue::Bytes(Default::default()), - ValueType::Date => ElementValue::Date(Default::default()), - ValueType::Boolean => ElementValue::Bool(Default::default()), - ValueType::GeneratedId => { - if name == "_id" && (model.element_type == ElementType::ListElement || model.element_type == ElementType::BlobElement) { - ElementValue::IdTupleId(IdTuple::new(GeneratedId::test_random(), GeneratedId::test_random())) - } else { - ElementValue::IdGeneratedId(GeneratedId::test_random()) - } - } - ValueType::CustomId => { - if name == "_id" && (model.element_type == ElementType::ListElement || model.element_type == ElementType::BlobElement) { - // TODO: adapt this when Custom Id tuples are supported - ElementValue::IdTupleId(IdTuple::new(GeneratedId::test_random(), GeneratedId::test_random())) - } else { - ElementValue::IdCustomId(CustomId::test_random()) - } - } - ValueType::CompressedString => todo!("Failed to create test entity {app}/{type_}: Compressed strings ({name}) are not yet supported!"), - } + ValueType::String => ElementValue::String(Default::default()), + ValueType::Number => ElementValue::Number(Default::default()), + ValueType::Bytes => ElementValue::Bytes(Default::default()), + ValueType::Date => ElementValue::Date(Default::default()), + ValueType::Boolean => ElementValue::Bool(Default::default()), + ValueType::GeneratedId => { + if name == "_id" && (model.element_type == ElementType::ListElement || model.element_type == ElementType::BlobElement) { + ElementValue::IdTupleId(IdTuple::new(GeneratedId::test_random(), GeneratedId::test_random())) + } else { + ElementValue::IdGeneratedId(GeneratedId::test_random()) + } + }, + ValueType::CustomId => { + if name == "_id" && (model.element_type == ElementType::ListElement || model.element_type == ElementType::BlobElement) { + // TODO: adapt this when Custom Id tuples are supported + ElementValue::IdTupleId(IdTuple::new(GeneratedId::test_random(), GeneratedId::test_random())) + } else { + ElementValue::IdCustomId(CustomId::test_random()) + } + }, + ValueType::CompressedString => todo!("Failed to create test entity {app}/{type_}: Compressed strings ({name}) are not yet supported!"), + } }, }; diff --git a/tuta-sdk/rust/sdk/tests/download_mail_test.rs b/tuta-sdk/rust/sdk/tests/download_mail_test.rs index 59182c097fe0..0211bf473fe3 100644 --- a/tuta-sdk/rust/sdk/tests/download_mail_test.rs +++ b/tuta-sdk/rust/sdk/tests/download_mail_test.rs @@ -52,12 +52,7 @@ async fn test_download_mail() { fn make_rest_client() -> Arc { let mut client = TestRestClient::default(); - client.insert_response( - "http://localhost:9000/rest/sys/Session/O1qC702-1J-0/3u3i8Lr9_7TnDDdAVw7w3TypTD2k1L00vIUTMF0SIPY", - HttpMethod::GET, - 200, - Some(include_bytes!("download_mail_test/session.json")) - ); + client.insert_response("http://localhost:9000/rest/sys/Session/O1qC702-1J-0/3u3i8Lr9_7TnDDdAVw7w3TypTD2k1L00vIUTMF0SIPY", HttpMethod::GET, 200, Some(include_bytes!("download_mail_test/session.json"))); client.insert_response( "http://localhost:9000/rest/sys/User/O1qC700----0", diff --git a/tuta-sdk/rust/sdk/tests/test_rest_client.rs b/tuta-sdk/rust/sdk/tests/test_rest_client.rs index 81ecafd2555a..6e48459a31cc 100644 --- a/tuta-sdk/rust/sdk/tests/test_rest_client.rs +++ b/tuta-sdk/rust/sdk/tests/test_rest_client.rs @@ -36,6 +36,7 @@ pub struct TestRestRequest { pub method: HttpMethod, } + #[async_trait::async_trait] impl RestClient for TestRestClient { async fn request_binary(