├── .gitignore ├── c_tests ├── .gitignore ├── Makefile ├── tests.c ├── test_common.h └── test_service.c ├── .github └── FUNDING.yml ├── etebase.pc.in ├── Cargo.toml ├── README.md ├── EtebaseConfig.cmake ├── Makefile ├── ChangeLog.md ├── LICENSE ├── cbindgen.toml ├── Cargo.lock └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | Session.vim 4 | -------------------------------------------------------------------------------- /c_tests/.gitignore: -------------------------------------------------------------------------------- 1 | /tests 2 | *.o 3 | 4 | /compile_commands.json 5 | /.clangd/ 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: etesync 2 | custom: https://www.etesync.com/contribute/#donate 3 | -------------------------------------------------------------------------------- /etebase.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | libdir=${prefix}/lib 3 | includedir=${prefix}/include 4 | 5 | Name: Etebase 6 | Description: A C library for Etebase 7 | Version: 0.5.3 8 | Libs: -L${libdir} -letebase 9 | Cflags: -I${includedir}/etebase 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libetebase" 3 | description = "C library for etebase" 4 | homepage = "https://www.etebase.com" 5 | repository = "https://github.com/etesync/libetebase/" 6 | version = "0.5.3" 7 | authors = ["Tom Hacohen "] 8 | edition = "2018" 9 | license = "BSD-3-Clause" 10 | readme = "README.md" 11 | publish = false 12 | 13 | [lib] 14 | name = "etebase" 15 | crate-type = ["staticlib", "cdylib"] 16 | 17 | [build-dependencies] 18 | cbindgen = "^0.14.2" 19 | 20 | [dependencies] 21 | etebase = "^0.5.3" 22 | -------------------------------------------------------------------------------- /c_tests/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = tests 2 | LIBS = -L. -l:../target/debug/libetebase.so 3 | CC = gcc 4 | CFLAGS = -g -Wall -I ../target 5 | 6 | .PHONY: default all clean 7 | 8 | default: $(TARGET) 9 | all: default 10 | 11 | OBJECTS = $(patsubst %.c, %.o, $(wildcard *.c)) 12 | HEADERS = $(wildcard *.h) ../target/etebase.h 13 | 14 | %.o: %.c $(HEADERS) 15 | $(CC) $(CFLAGS) -c $< -o $@ 16 | 17 | .PRECIOUS: $(TARGET) $(OBJECTS) 18 | 19 | $(TARGET): $(OBJECTS) 20 | $(CC) $(OBJECTS) -Wall -fsanitize=address -fno-omit-frame-pointer $(LIBS) -o $@ 21 | 22 | check: $(TARGET) 23 | ./tests 24 | 25 | clean: 26 | -rm -f *.o 27 | -rm -f $(TARGET) 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

Etebase - Encrypt Everything

4 |

5 | 6 | A C library for Etebase 7 | 8 | This package is implemented in Rust and exposes a C API for people to use. 9 | 10 | ![GitHub tag](https://img.shields.io/github/tag/etesync/libetebase.svg) 11 | [![Build Status](https://travis-ci.com/etesync/libetebase.svg?branch=master)](https://travis-ci.com/etesync/libetebase) 12 | [![Chat with us](https://img.shields.io/badge/chat-IRC%20|%20Matrix%20|%20Web-blue.svg)](https://www.etebase.com/community-chat/) 13 | 14 | # Documentation 15 | 16 | In addition to the API documentation, there are docs available at https://docs.etebase.com 17 | 18 | # Build 19 | 20 | ``` 21 | make 22 | ``` 23 | 24 | # Install 25 | 26 | ``` 27 | sudo make install 28 | ``` 29 | -------------------------------------------------------------------------------- /c_tests/tests.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2020 EteSync Authors 2 | // SPDX-License-Identifier: LGPL-2.1-only 3 | 4 | #include "test_common.h" 5 | 6 | int test_simple_getters(); 7 | int test_base64(); 8 | int test_utils(); 9 | int test_check_etebase_server(); 10 | int test_simple(); 11 | int test_item_deps(); 12 | int test_collection_transactions(); 13 | int test_items_transactions(); 14 | int test_collection_as_item(); 15 | int test_item_revisions(); 16 | int test_basic_invitations(); 17 | int test_bad_auth(); 18 | 19 | int 20 | main() { 21 | int ret = 0; 22 | 23 | RUN_TEST(test_simple_getters); 24 | RUN_TEST(test_base64); 25 | RUN_TEST(test_utils); 26 | RUN_TEST(test_check_etebase_server); 27 | RUN_TEST(test_simple); 28 | RUN_TEST(test_item_deps); 29 | RUN_TEST(test_collection_transactions); 30 | RUN_TEST(test_items_transactions); 31 | RUN_TEST(test_collection_as_item); 32 | RUN_TEST(test_item_revisions); 33 | RUN_TEST(test_basic_invitations); 34 | RUN_TEST(test_bad_auth); 35 | 36 | return ret; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /EtebaseConfig.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find etebase 2 | # Once done this will define 3 | # ETEBASE_FOUND - System has etebase 4 | # ETEBASE_INCLUDE_DIRS - The etebase include directories 5 | # ETEBASE_LIBRARIES - The libraries needed to use etebase 6 | # ETEBASE_DEFINITIONS - Compiler switches required for using etebase 7 | 8 | find_package(PkgConfig) 9 | if ("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER "2.8.1") 10 | # "QUIET" was introduced in 2.8.2 11 | set(_QUIET QUIET) 12 | endif () 13 | pkg_check_modules(PC_ETEBASE ${_QUIET} etebase) 14 | 15 | find_library(ETEBASE_LIBRARY 16 | NAMES ${PC_ETEBASE_LIBRARIES} 17 | HINTS ${PC_ETEBASE_LIBDIR} ${PC_ETEBASE_LIBRARY_DIRS} ) 18 | 19 | set(ETEBASE_DEFINITIONS ${PC_ETEBASE_CFLAGS_OTHER}) 20 | set(ETEBASE_LIBRARIES ${ETEBASE_LIBRARY}) 21 | set(ETEBASE_INCLUDE_DIRS ${PC_ETEBASE_INCLUDE_DIRS}) 22 | 23 | include(FindPackageHandleStandardArgs) 24 | # handle the QUIETLY and REQUIRED arguments and set ETEBASE_FOUND to TRUE 25 | # if all listed variables are TRUE 26 | find_package_handle_standard_args(Etebase DEFAULT_MSG 27 | ETEBASE_LIBRARIES ETEBASE_INCLUDE_DIRS) 28 | 29 | mark_as_advanced(ETEBASE_INCLUDE_DIRS ETEBASE_LIBRARY ETEBASE_LIBRARIES ETEBASE_DEFINITIONS) 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr 2 | DESTDIR ?= 3 | # Set MODE to debug to build in debug mode 4 | MODE ?= release 5 | 6 | PKGNAME = etebase 7 | 8 | BUILDDIR = ./target 9 | 10 | DST_LIBRARY_DIR = $(DESTDIR)$(PREFIX)/lib 11 | DST_PKGCONFIG_DIR = $(DST_LIBRARY_DIR)/pkgconfig 12 | DST_CMAKECONFIG_DIR = $(DST_LIBRARY_DIR)/cmake/Etebase 13 | DST_INCLUDE_DIR = $(DESTDIR)$(PREFIX)/include/$(PKGNAME) 14 | 15 | LIBRARY_FILE = $(BUILDDIR)/$(MODE)/lib$(PKGNAME).so 16 | HEADER_FILE = $(BUILDDIR)/$(PKGNAME).h 17 | PKGCONFIG_FILE = $(BUILDDIR)/$(PKGNAME).pc 18 | CMAKECONFIG_FILE = EtebaseConfig.cmake 19 | 20 | .PHONY: default all clean 21 | 22 | all: build 23 | 24 | pkgconfig: $(PKGCONFIG_FILE) 25 | 26 | $(PKGCONFIG_FILE): $(PKGNAME).pc.in 27 | mkdir -p "$(BUILDDIR)" 28 | sed "s#@prefix@#$(PREFIX)#g" $< > "$(BUILDDIR)/$(PKGNAME).pc" 29 | 30 | build-release: pkgconfig 31 | cargo build --release 32 | 33 | build-debug: pkgconfig 34 | cargo build 35 | 36 | # https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-06-rust-on-ios.html 37 | build-ios: pkgconfig 38 | cargo lipo --release 39 | 40 | build: build-$(MODE) 41 | 42 | install: 43 | install -Dm644 "$(PKGCONFIG_FILE)" -t "$(DST_PKGCONFIG_DIR)" 44 | install -Dm644 "$(CMAKECONFIG_FILE)" -t "$(DST_CMAKECONFIG_DIR)" 45 | install -Dm644 "$(HEADER_FILE)" -t "$(DST_INCLUDE_DIR)" 46 | install -Dm755 "$(LIBRARY_FILE)" -t "$(DST_LIBRARY_DIR)" 47 | 48 | check: build 49 | cargo check 50 | cd c_tests && $(MAKE) check 51 | 52 | clean: 53 | cargo clean 54 | cd c_tests && $(MAKE) clean 55 | rm -f "$(BUILDDIR)/$(PKGNAME).p"c 56 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Version 0.5.2 4 | * Update openssl deps to support more recent libopenssl vesrions 5 | 6 | ## Version 0.5.1 7 | * Update to the latest etebase-rs release 8 | 9 | ## Version 0.5.0 10 | * Document all of the API 11 | * Add fetch_multi te fetch items by UIDs 12 | 13 | ## Version 0.4.1 14 | * Build the release version by default 15 | 16 | ## Version 0.4.0 17 | * Relicense to BSD-3-Clause 18 | * Improve Makefile 19 | * Set SONAME for the library on Unix-like systems 20 | 21 | ## Version 0.3.1 22 | * Fix issue with custom urls lacking a trailing slash 23 | * Update etebase dependency 24 | 25 | ## Version 0.3.0 26 | * Login: automatically create the account if not init 27 | * Have global and immutable collection types (changes the create and list APIs) 28 | * Update etebase dependency 29 | 30 | ## Version 0.2.0 31 | * Expose a function to fetch the dashboard url of an account 32 | * Expose the FileSystemCache module for caching the data locally 33 | * Expose a function te check whether it's an etebase server 34 | * Update etebase dependency 35 | 36 | ## Version 0.1.4 37 | * Invitations: expose from_username 38 | * Update etebase dependency 39 | 40 | ## Version 0.1.3 41 | * Collection stoken: fix fetching a collection's stoken 42 | * Update etebase dependency 43 | 44 | ## Version 0.1.2 45 | * Expose the access level as int rather than string 46 | * Add Cmake configuration file 47 | * Update etebase dependency 48 | 49 | ## Version 0.1.1 50 | * Split the batch/transaction functions to with and without deps 51 | 52 | ## Version 0.1.0 53 | * Initial release 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Tom Hacohen. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /c_tests/test_common.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2020 EteSync Authors 2 | // SPDX-License-Identifier: LGPL-2.1-only 3 | 4 | #ifndef _TEST_COMMON_H 5 | #define _TEST_COMMON_H 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "etebase.h" 14 | 15 | 16 | #define fail_if(expr) \ 17 | do { \ 18 | if (expr) { \ 19 | fprintf(stderr, "%s:%d: Failure '"#expr"' occurred.\n", __func__, __LINE__); \ 20 | exit(1); \ 21 | } \ 22 | } while (0) 23 | 24 | #define assert_str_eq(str1, str2) \ 25 | do { \ 26 | const char *a = str1, *b = str2; \ 27 | if (strcmp(a, b)) { \ 28 | fprintf(stderr, "%s:%d: Falilure: '%s' != '%s'.\n", __func__, __LINE__, a, b); \ 29 | exit(1); \ 30 | } \ 31 | } while (0) 32 | 33 | #define assert_int_eq(num1, num2) \ 34 | do { \ 35 | const int a = num1, b = num2; \ 36 | if (a != b) { \ 37 | fprintf(stderr, "%s:%d: Falilure: '%d' != '%d'.\n", __func__, __LINE__, a, b); \ 38 | exit(1); \ 39 | } \ 40 | } while (0) 41 | 42 | #define assert_int_ne(num1, num2) \ 43 | do { \ 44 | const int a = num1, b = num2; \ 45 | if (a == b) { \ 46 | fprintf(stderr, "%s:%d: Falilure: '%d' == '%d'.\n", __func__, __LINE__, a, b); \ 47 | exit(1); \ 48 | } \ 49 | } while (0) 50 | 51 | #define RUN_TEST(test) \ 52 | do { \ 53 | fprintf(stderr, "> Starting test: %s\n", #test); \ 54 | ret = ret || test(); \ 55 | fprintf(stderr, "= Finished test: %s\n", #test); \ 56 | } while(0) 57 | 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /cbindgen.toml: -------------------------------------------------------------------------------- 1 | language = "C" 2 | 3 | # An optional name to use as an include guard 4 | # default: doesn't emit an include guard 5 | include_guard = "ETEBASE_H" 6 | 7 | cpp_compat = true 8 | 9 | # An optional string of text to output between major sections of the generated 10 | # file as a warning against manual editing 11 | autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" 12 | 13 | # Whether to include a comment with the version of cbindgen used to generate the file 14 | # default: false 15 | include_version = true 16 | 17 | after_includes = """ 18 | // +1 so we fake ceil it 19 | #define ETEBASE_UTILS_FROM_BASE64_MAX_LEN(X) (((X) * 3U / 4U) + 1U) 20 | 21 | // +2 so we fake ceil it + terminating null 22 | #define ETEBASE_UTILS_TO_BASE64_MAX_LEN(X) (((X) * 4U / 3U) + 2U) 23 | 24 | #define ETEBASE_UTILS_C_ARRAY_LEN(arr) (sizeof(arr) / sizeof((arr)[0])) 25 | """ 26 | 27 | # Codegen Options 28 | 29 | # When generating a C header, the kind of declaration style to use for structs 30 | # or enums. 31 | # 32 | # possible values: 33 | # * "type": typedef struct { ... } MyType; 34 | # * "tag": struct MyType { ... }; 35 | # * "both": typedef struct MyType { ... } MyType; 36 | # 37 | # default: "both" 38 | style = "both" 39 | 40 | [export] 41 | prefix = "Etebase" 42 | renaming_overrides_prefixing = true 43 | 44 | exclude = ["CURRENT_VERSION", "SYMMETRIC_KEY_SIZE", "SYMMETRIC_NONCE_SIZE", "SYMMETRIC_TAG_SIZE"] 45 | 46 | [export.rename] 47 | "ETEBASE_UTILS_PRETTY_FINGERPRINT_SIZE" = "ETEBASE_UTILS_PRETTY_FINGERPRINT_SIZE" 48 | 49 | [fn] 50 | # An optional prefix to put before every function declaration 51 | # default: no prefix added 52 | # prefix = "WR_START_FUNC" 53 | 54 | # An optional postfix to put after any function declaration 55 | # default: no postix added 56 | # postfix = "WR_END_FUNC" 57 | 58 | sort_by = "None" 59 | 60 | [struct] 61 | # A rule to use to rename struct field names. The renaming assumes the input is 62 | # the Rust standard snake_case, however it acccepts all the different rename_args 63 | # inputs. This means many options here are no-ops or redundant. 64 | # 65 | # possible values (that actually do something): 66 | # * "CamelCase": my_arg => myArg 67 | # * "PascalCase": my_arg => MyArg 68 | # * "GeckoCase": my_arg => mMyArg 69 | # * "ScreamingSnakeCase": my_arg => MY_ARG 70 | # * "None": apply no renaming 71 | # 72 | # technically possible values (that shouldn't have a purpose here): 73 | # * "SnakeCase": apply no renaming 74 | # * "LowerCase": apply no renaming (actually applies to_lowercase, is this bug?) 75 | # * "UpperCase": same as ScreamingSnakeCase in this context 76 | # * "QualifiedScreamingSnakeCase" => same as ScreamingSnakeCase in this context 77 | # 78 | # default: "None" 79 | rename_fields = "None" 80 | 81 | 82 | [enum] 83 | # A rule to use to rename enum variants, and the names of any fields those 84 | # variants have. This should probably be split up into two separate options, but 85 | # for now, they're the same! See the documentation for `[struct]rename_fields` 86 | # for how this applies to fields. Renaming of the variant assumes that the input 87 | # is the Rust standard PascalCase. In the case of QualifiedScreamingSnakeCase, 88 | # it also assumed that the enum's name is PascalCase. 89 | # 90 | # possible values (that actually do something): 91 | # * "CamelCase": MyVariant => myVariant 92 | # * "SnakeCase": MyVariant => my_variant 93 | # * "ScreamingSnakeCase": MyVariant => MY_VARIANT 94 | # * "QualifiedScreamingSnakeCase": MyVariant => ENUM_NAME_MY_VARIANT 95 | # * "LowerCase": MyVariant => myvariant 96 | # * "UpperCase": MyVariant => MYVARIANT 97 | # * "None": apply no renaming 98 | # 99 | # technically possible values (that shouldn't have a purpose for the variants): 100 | # * "PascalCase": apply no renaming 101 | # * "GeckoCase": apply no renaming 102 | # 103 | # default: "None" 104 | rename_variants = "ScreamingSnakeCase" 105 | prefix_with_name = true 106 | 107 | [parse] 108 | parse_deps = true 109 | include = ["etebase"] 110 | extra_bindings = ["etebase"] 111 | -------------------------------------------------------------------------------- /c_tests/test_service.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2020 EteSync Authors 2 | // SPDX-License-Identifier: LGPL-2.1-only 3 | 4 | #include "etebase.h" 5 | #include "test_common.h" 6 | #include 7 | #include 8 | #include 9 | 10 | const char TEST_USER_SESSION[] = "gqd2ZXJzaW9uAa1lbmNyeXB0ZWREYXRhxQGr_KWyDChQ6tXOJwJKf0Kw3QyR99itPIF3vZ5w6pVXSIq7AWul3fIXjIZOsBEwTVRumw7e9Af38D5oIL2VLNPLlmTOMjzIvuB00z3zDMFbH8pwrg2p_FvAhLHGjUGoXzU2XIxS4If7rQUfEz1zWkHPqWMrj4hACML5fks302dOUw7OsSMekcQaaVqMyj82MY3lG2qj8CL6ykSED7nW6OYWwMBJ1rSDGXhQRd5JuCGl6kgAHxKS6gkkIAWeUKjC6-Th2etk1XPKDiks0SZrQpmuXG8h_TBdd4igjRUqnIk09z5wvJFViXIU4M3pQomyFPk3Slh7KHvWhzxG0zbC2kUngQZ5h-LbVTLuT_TQWjYmHiOIihenrzl7z9MLebUq6vuwusZMRJ1Atau0Y2HcOzulYt4tLRP49d56qFEId3R4xomZ666hy-EFodsbzpxEKHeBUro3_gifOOKR8zkyLKTRz1UipZfKvnWk_RHFgZlSClRsXyaP34wstUavSiz-HNmTEmflNQKM7Awfel108FcSbW9NQAogW2Y2copP-P-R-DiHThrXmgDsWkTQFA"; 11 | 12 | const char *COL_TYPE = "some.coltype"; 13 | 14 | const char * 15 | get_test_url() { 16 | const char *env = getenv("ETEBASE_TEST_API_URL"); 17 | return (env) ? env : "http://localhost:8033"; 18 | } 19 | 20 | int 21 | test_simple_getters() { 22 | EtebaseUser *user = etebase_user_new("username", "email@localhost"); 23 | assert_str_eq(etebase_user_get_username(user), "username"); 24 | assert_str_eq(etebase_user_get_email(user), "email@localhost"); 25 | etebase_user_destroy(user); 26 | 27 | EtebaseClient *client = etebase_client_new("libetebase-test", get_test_url()); 28 | EtebaseAccount *etebase = etebase_account_restore(client, TEST_USER_SESSION, NULL, 0); 29 | etebase_client_destroy(client); 30 | 31 | EtebaseCollectionManager *col_mgr = etebase_account_get_collection_manager(etebase); 32 | EtebaseItemMetadata *col_meta = etebase_item_metadata_new(); 33 | etebase_item_metadata_set_name(col_meta, "Name"); 34 | const char content[] = "Something"; 35 | EtebaseCollection *col = etebase_collection_manager_create(col_mgr, COL_TYPE, col_meta, content, strlen(content)); 36 | etebase_item_metadata_destroy(col_meta); 37 | 38 | { 39 | char *col_type = etebase_collection_get_collection_type(col); 40 | assert_str_eq(col_type, COL_TYPE); 41 | free(col_type); 42 | } 43 | 44 | // Check we can just get the content size if we pass null as the buffer 45 | { 46 | uintptr_t len = etebase_collection_get_content(col, NULL, 0); 47 | assert_int_eq(len, strlen(content)); 48 | char tmp[len]; 49 | etebase_collection_get_content(col, tmp, len); 50 | } 51 | 52 | EtebaseCollectionInvitationManager *invitation_manager = etebase_account_get_invitation_manager(etebase); 53 | 54 | uintptr_t pubkey_size = etebase_invitation_manager_get_pubkey_size(invitation_manager); 55 | fail_if(pubkey_size == 0); 56 | const char *pubkey = etebase_invitation_manager_get_pubkey(invitation_manager); 57 | fail_if(!pubkey); 58 | 59 | etebase_invitation_manager_destroy(invitation_manager); 60 | etebase_collection_destroy(col); 61 | etebase_collection_manager_destroy(col_mgr); 62 | etebase_account_destroy(etebase); 63 | 64 | return 0; 65 | } 66 | 67 | int 68 | test_base64() { 69 | const char *text = "Test"; 70 | char encoded[ETEBASE_UTILS_TO_BASE64_MAX_LEN(strlen(text) + 1)]; 71 | fail_if(etebase_utils_to_base64(text, strlen(text) + 1, encoded, sizeof(encoded))); 72 | char decoded[ETEBASE_UTILS_FROM_BASE64_MAX_LEN(strlen(encoded))]; 73 | uintptr_t decoded_len = 0; 74 | fail_if(etebase_utils_from_base64(encoded, decoded, sizeof(decoded), &decoded_len)); 75 | assert_int_eq(decoded_len, strlen(text) + 1); 76 | assert_str_eq(decoded, text); 77 | 78 | fail_if(etebase_utils_from_base64(encoded, decoded, sizeof(decoded), NULL)); 79 | 80 | fail_if(!etebase_utils_from_base64("#@$@#$*@#$", decoded, sizeof(decoded), NULL)); 81 | assert_int_eq(ETEBASE_ERROR_CODE_BASE64, etebase_error_get_code()); 82 | return 0; 83 | } 84 | 85 | int 86 | test_utils() { 87 | assert_str_eq(etebase_get_default_server_url(), "https://api.etebase.com"); 88 | char buf[32]; 89 | fail_if(etebase_utils_randombytes(buf, sizeof(buf))); 90 | char pretty[ETEBASE_UTILS_PRETTY_FINGERPRINT_SIZE]; 91 | fail_if(etebase_utils_pretty_fingerprint(buf, sizeof(buf), pretty)); 92 | return 0; 93 | } 94 | 95 | int 96 | test_check_etebase_server() { 97 | EtebaseClient *client = etebase_client_new("libetebase-test", get_test_url()); 98 | fail_if(etebase_client_check_etebase_server(client)); 99 | etebase_client_destroy(client); 100 | /* 101 | let test_url = format!("{}/a", test_url()); 102 | let client = Client::new(CLIENT_NAME, &test_url)?; 103 | assert!(!Account::is_etebase_server(&client)?); 104 | */ 105 | client = etebase_client_new("libetebase-test", "http://doesnotexist"); 106 | fail_if(!etebase_client_check_etebase_server(client)); 107 | etebase_client_destroy(client); 108 | 109 | return 0; 110 | } 111 | 112 | int 113 | test_simple() { 114 | EtebaseClient *client = etebase_client_new("libetebase-test", get_test_url()); 115 | EtebaseAccount *etebase = etebase_account_restore(client, TEST_USER_SESSION, NULL, 0); 116 | etebase_client_destroy(client); 117 | 118 | etebase_account_force_server_url(etebase, get_test_url()); 119 | etebase_account_fetch_token(etebase); 120 | 121 | EtebaseCollectionManager *col_mgr = etebase_account_get_collection_manager(etebase); 122 | EtebaseItemMetadata *col_meta = etebase_item_metadata_new(); 123 | etebase_item_metadata_set_name(col_meta, "Name"); 124 | const char content[] = "Something"; 125 | EtebaseCollection *col = etebase_collection_manager_create(col_mgr, COL_TYPE, col_meta, content, strlen(content)); 126 | etebase_item_metadata_destroy(col_meta); 127 | 128 | { 129 | char tmp[1000]; 130 | intptr_t tmp_size = etebase_collection_get_content(col, &tmp, sizeof(tmp)); 131 | fail_if(tmp_size < 0); 132 | assert_int_eq(tmp_size, strlen(content)); 133 | tmp[tmp_size] = 0; 134 | assert_str_eq(tmp, content); 135 | } 136 | 137 | { 138 | EtebaseFetchOptions *fetch_options = etebase_fetch_options_new(); 139 | etebase_fetch_options_set_prefetch(fetch_options, ETEBASE_PREFETCH_OPTION_AUTO); 140 | fail_if(etebase_collection_manager_upload(col_mgr, col, fetch_options)); 141 | etebase_fetch_options_destroy(fetch_options); 142 | } 143 | 144 | { 145 | EtebaseCollectionListResponse *col_list = etebase_collection_manager_list(col_mgr, COL_TYPE, NULL); 146 | fail_if(!col_list); 147 | assert_int_ne(0, etebase_collection_list_response_get_data_length(col_list)); 148 | 149 | EtebaseFetchOptions *fetch_options = etebase_fetch_options_new(); 150 | etebase_fetch_options_set_stoken(fetch_options, etebase_collection_list_response_get_stoken(col_list)); 151 | 152 | etebase_collection_list_response_destroy(col_list); 153 | col_list = etebase_collection_manager_list(col_mgr, COL_TYPE, fetch_options); 154 | assert_int_eq(0, etebase_collection_list_response_get_data_length(col_list)); 155 | 156 | etebase_fetch_options_destroy(fetch_options); 157 | etebase_collection_list_response_destroy(col_list); 158 | } 159 | 160 | { 161 | EtebaseCollection *col2 = etebase_collection_manager_fetch(col_mgr, etebase_collection_get_uid(col), NULL); 162 | char content2[1000]; 163 | intptr_t content2_size = etebase_collection_get_content(col2, &content2, sizeof(content2)); 164 | fail_if(content2_size < 0); 165 | assert_int_eq(content2_size, strlen(content)); 166 | content2[content2_size] = 0; 167 | assert_str_eq(content2, content); 168 | 169 | const char tmp[] = "Something else"; 170 | etebase_collection_set_content(col2, tmp, strlen(tmp)); 171 | etebase_collection_manager_transaction(col_mgr, col2, NULL); 172 | 173 | etebase_collection_destroy(col2); 174 | } 175 | 176 | EtebaseItemManager *item_mgr = etebase_collection_manager_get_item_manager(col_mgr, col); 177 | EtebaseItemMetadata *item_meta = etebase_item_metadata_new(); 178 | etebase_item_metadata_set_item_type(item_meta, "Bla"); 179 | const char item_content[] = "Something item"; 180 | EtebaseItem *item = etebase_item_manager_create(item_mgr, item_meta, item_content, strlen(item_content)); 181 | etebase_item_metadata_destroy(item_meta); 182 | fail_if(!strcmp("", etebase_item_get_uid(item))); 183 | fail_if(!etebase_item_get_etag(item)); 184 | 185 | { 186 | char content2[1000]; 187 | intptr_t content2_size = etebase_item_get_content(item, &content2, sizeof(content2)); 188 | fail_if(content2_size < 0); 189 | assert_int_eq(content2_size, strlen(item_content)); 190 | content2[content2_size] = 0; 191 | assert_str_eq(content2, item_content); 192 | } 193 | 194 | { 195 | const EtebaseItem *items[] = { item }; 196 | fail_if(etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL)); 197 | } 198 | 199 | { 200 | char *old_etag = strdup(etebase_item_get_etag(item)); 201 | const char tmp[] = "Something item2"; 202 | etebase_item_set_content(item, tmp, strlen(tmp)); 203 | fail_if(!strcmp(old_etag, etebase_item_get_etag(item))); 204 | free(old_etag); 205 | 206 | const EtebaseItem *items[] = { item }; 207 | fail_if(etebase_item_manager_transaction(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL)); 208 | 209 | EtebaseItemListResponse *item_list = etebase_item_manager_list(item_mgr, NULL); 210 | fail_if(!item_list); 211 | uintptr_t list_len = etebase_item_list_response_get_data_length(item_list); 212 | assert_int_eq(1, list_len); 213 | 214 | const EtebaseItem *list_items[list_len]; 215 | fail_if(etebase_item_list_response_get_data(item_list, list_items)); 216 | EtebaseItem *item2 = etebase_item_clone(list_items[0]); 217 | etebase_item_destroy(item2); 218 | 219 | char content2[1000]; 220 | intptr_t content2_size = etebase_item_get_content(list_items[0], &content2, sizeof(content2)); 221 | fail_if(content2_size < 0); 222 | assert_int_eq(content2_size, strlen(tmp)); 223 | content2[content2_size] = 0; 224 | assert_str_eq(content2, tmp); 225 | 226 | EtebaseFetchOptions *fetch_options = etebase_fetch_options_new(); 227 | etebase_fetch_options_set_stoken(fetch_options, etebase_item_list_response_get_stoken(item_list)); 228 | EtebaseItemListResponse *item_list2 = etebase_item_manager_list(item_mgr, fetch_options); 229 | 230 | uintptr_t list_len2 = etebase_item_list_response_get_data_length(item_list2); 231 | assert_int_eq(0, list_len2); 232 | 233 | etebase_fetch_options_destroy(fetch_options); 234 | etebase_item_list_response_destroy(item_list2); 235 | etebase_item_list_response_destroy(item_list); 236 | } 237 | 238 | etebase_account_logout(etebase); 239 | 240 | etebase_item_destroy(item); 241 | etebase_item_manager_destroy(item_mgr); 242 | etebase_collection_destroy(col); 243 | etebase_collection_manager_destroy(col_mgr); 244 | etebase_account_destroy(etebase); 245 | return 0; 246 | } 247 | 248 | int 249 | test_item_deps() { 250 | EtebaseClient *client = etebase_client_new("libetebase-test", get_test_url()); 251 | EtebaseAccount *etebase = etebase_account_restore(client, TEST_USER_SESSION, NULL, 0); 252 | etebase_client_destroy(client); 253 | 254 | etebase_account_force_server_url(etebase, get_test_url()); 255 | etebase_account_fetch_token(etebase); 256 | 257 | EtebaseCollectionManager *col_mgr = etebase_account_get_collection_manager(etebase); 258 | EtebaseItemMetadata *col_meta = etebase_item_metadata_new(); 259 | etebase_item_metadata_set_name(col_meta, "Name"); 260 | EtebaseCollection *col = etebase_collection_manager_create(col_mgr, COL_TYPE, col_meta, "", 0); 261 | etebase_item_metadata_destroy(col_meta); 262 | 263 | etebase_collection_manager_upload(col_mgr, col, NULL); 264 | 265 | EtebaseItemManager *item_mgr = etebase_collection_manager_get_item_manager(col_mgr, col); 266 | EtebaseItemMetadata *item_meta = etebase_item_metadata_new(); 267 | const char item_content[] = "Item 1"; 268 | EtebaseItem *item1 = etebase_item_manager_create(item_mgr, item_meta, item_content, strlen(item_content)); 269 | const char item_content2[] = "Item 2"; 270 | EtebaseItem *item2 = etebase_item_manager_create(item_mgr, item_meta, item_content2, strlen(item_content2)); 271 | etebase_item_metadata_destroy(item_meta); 272 | 273 | { 274 | const EtebaseItem *items[] = { item1, item2 }; 275 | fail_if(etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL)); 276 | } 277 | 278 | { 279 | const char *item1_uid = etebase_item_get_uid(item1); 280 | // -> On device B: 281 | EtebaseItem *item1 = etebase_item_manager_fetch(item_mgr, item1_uid, NULL); 282 | fail_if(!item1); 283 | const char tmp[] = "Something else for item1"; 284 | etebase_item_set_content(item1, tmp, strlen(tmp)); 285 | const EtebaseItem *items[] = { item1 }; 286 | fail_if(etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL)); 287 | etebase_item_destroy(item1); 288 | } 289 | 290 | 291 | { 292 | // -> On device A (using the previously saved collection) 293 | const char tmp[] = "New content for item 2"; 294 | etebase_item_set_content(item2, tmp, strlen(tmp)); 295 | 296 | // Will both fail because item1 changed 297 | const EtebaseItem *items[] = { item2 }; 298 | const EtebaseItem *deps[] = { item1 }; 299 | fail_if(!etebase_item_manager_transaction_deps(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), 300 | deps, ETEBASE_UTILS_C_ARRAY_LEN(deps), NULL)); 301 | fail_if(!etebase_item_manager_batch_deps(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), 302 | deps, ETEBASE_UTILS_C_ARRAY_LEN(deps), NULL)); 303 | 304 | // Can even use the item in both the list and deps in batch 305 | // Will fail because item1 changed on device B 306 | 307 | const EtebaseItem *items2[] = { item1, item2 }; 308 | fail_if(!etebase_item_manager_batch_deps(item_mgr, items2, ETEBASE_UTILS_C_ARRAY_LEN(items2), 309 | deps, ETEBASE_UTILS_C_ARRAY_LEN(deps), NULL)); 310 | } 311 | 312 | etebase_account_logout(etebase); 313 | 314 | etebase_item_destroy(item2); 315 | etebase_item_destroy(item1); 316 | etebase_item_manager_destroy(item_mgr); 317 | etebase_collection_destroy(col); 318 | etebase_collection_manager_destroy(col_mgr); 319 | etebase_account_destroy(etebase); 320 | return 0; 321 | } 322 | 323 | int 324 | test_bad_auth() { 325 | EtebaseClient *client = etebase_client_new("libetebase-test", get_test_url()); 326 | { 327 | EtebaseAccount *etebase = etebase_account_login(client, "non-existent", "passward"); 328 | fail_if(etebase); 329 | assert_int_eq(ETEBASE_ERROR_CODE_UNAUTHORIZED, etebase_error_get_code()); 330 | } 331 | { 332 | EtebaseAccount *etebase = etebase_account_login(client, "test_user", "bad-passward"); 333 | fail_if(etebase); 334 | assert_int_eq(ETEBASE_ERROR_CODE_UNAUTHORIZED, etebase_error_get_code()); 335 | } 336 | 337 | EtebaseAccount *etebase = etebase_account_restore(client, TEST_USER_SESSION, NULL, 0); 338 | etebase_client_destroy(client); 339 | 340 | etebase_account_force_server_url(etebase, get_test_url()); 341 | etebase_account_fetch_token(etebase); 342 | etebase_account_logout(etebase); 343 | 344 | EtebaseCollectionManager *col_mgr = etebase_account_get_collection_manager(etebase); 345 | 346 | EtebaseCollectionListResponse *col_list = etebase_collection_manager_list(col_mgr, COL_TYPE, NULL); 347 | fail_if(col_list); 348 | assert_int_eq(ETEBASE_ERROR_CODE_UNAUTHORIZED, etebase_error_get_code()); 349 | 350 | etebase_collection_manager_destroy(col_mgr); 351 | etebase_account_destroy(etebase); 352 | return 0; 353 | } 354 | 355 | int 356 | test_collection_transactions() { 357 | EtebaseClient *client = etebase_client_new("libetebase-test", get_test_url()); 358 | EtebaseAccount *etebase = etebase_account_restore(client, TEST_USER_SESSION, NULL, 0); 359 | etebase_client_destroy(client); 360 | 361 | etebase_account_force_server_url(etebase, get_test_url()); 362 | etebase_account_fetch_token(etebase); 363 | 364 | EtebaseCollectionManager *col_mgr = etebase_account_get_collection_manager(etebase); 365 | EtebaseItemMetadata *col_meta = etebase_item_metadata_new(); 366 | etebase_item_metadata_set_name(col_meta, "Name"); 367 | EtebaseCollection *col = etebase_collection_manager_create(col_mgr, COL_TYPE, col_meta, "", 0); 368 | etebase_item_metadata_destroy(col_meta); 369 | 370 | etebase_collection_manager_upload(col_mgr, col, NULL); 371 | 372 | const char *col_uid = etebase_collection_get_uid(col); 373 | { 374 | // -> On device A: 375 | EtebaseCollection *col = etebase_collection_manager_fetch(col_mgr, col_uid, NULL); 376 | 377 | 378 | { 379 | // -> On device B: 380 | EtebaseCollection *col = etebase_collection_manager_fetch(col_mgr, col_uid, NULL); 381 | EtebaseItemMetadata *col_meta = etebase_collection_get_meta(col); 382 | etebase_item_metadata_set_name(col_meta, "New name"); 383 | etebase_collection_set_meta(col, col_meta); 384 | etebase_item_metadata_destroy(col_meta); 385 | 386 | etebase_collection_manager_upload(col_mgr, col, NULL); 387 | 388 | etebase_collection_destroy(col); 389 | } 390 | 391 | 392 | // -> On device A (using the previously saved collection) 393 | EtebaseItemMetadata *col_meta = etebase_collection_get_meta(col); 394 | etebase_item_metadata_set_name(col_meta, "Another name"); 395 | etebase_collection_set_meta(col, col_meta); 396 | etebase_item_metadata_destroy(col_meta); 397 | 398 | // Will fail 399 | fail_if(!etebase_collection_manager_transaction(col_mgr, col, NULL)); 400 | // Will succeed 401 | fail_if(etebase_collection_manager_upload(col_mgr, col, NULL)); 402 | 403 | etebase_collection_destroy(col); 404 | } 405 | 406 | // Using stoken 407 | { 408 | // -> On device A: 409 | EtebaseCollection *col = etebase_collection_manager_fetch(col_mgr, col_uid, NULL); 410 | const char *stoken = etebase_collection_get_stoken(col); 411 | 412 | { 413 | // -> On device B: 414 | EtebaseCollection *col = etebase_collection_manager_fetch(col_mgr, col_uid, NULL); 415 | EtebaseItemManager *item_mgr = etebase_collection_manager_get_item_manager(col_mgr, col); 416 | { 417 | EtebaseItemMetadata *item_meta = etebase_item_metadata_new(); 418 | etebase_item_metadata_set_name(item_meta, "Name"); 419 | EtebaseItem *item = etebase_item_manager_create(item_mgr, item_meta, NULL, 0); 420 | etebase_item_metadata_destroy(item_meta); 421 | { 422 | const EtebaseItem *items[] = { item }; 423 | fail_if(etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL)); 424 | } 425 | etebase_item_destroy(item); 426 | } 427 | 428 | etebase_item_manager_destroy(item_mgr); 429 | etebase_collection_destroy(col); 430 | } 431 | 432 | // -> On device A (using the previously saved collection) 433 | EtebaseItemMetadata *col_meta = etebase_collection_get_meta(col); 434 | etebase_item_metadata_set_name(col_meta, "Another name"); 435 | etebase_collection_set_meta(col, col_meta); 436 | etebase_item_metadata_destroy(col_meta); 437 | 438 | // Will both fail 439 | EtebaseFetchOptions *fetch_options = etebase_fetch_options_new(); 440 | etebase_fetch_options_set_stoken(fetch_options, stoken); 441 | fail_if(!etebase_collection_manager_transaction(col_mgr, col, fetch_options)); 442 | fail_if(!etebase_collection_manager_upload(col_mgr, col, fetch_options)); 443 | etebase_fetch_options_destroy(fetch_options); 444 | // Will both succeed 445 | fail_if(etebase_collection_manager_transaction(col_mgr, col, NULL)); 446 | // Previous one fail_if(etebase_collection_manager_upload(col_mgr, col, NULL)); 447 | 448 | etebase_collection_destroy(col); 449 | } 450 | 451 | 452 | etebase_account_logout(etebase); 453 | 454 | etebase_collection_destroy(col); 455 | etebase_collection_manager_destroy(col_mgr); 456 | etebase_account_destroy(etebase); 457 | return 0; 458 | } 459 | 460 | int 461 | test_items_transactions() { 462 | EtebaseClient *client = etebase_client_new("libetebase-test", get_test_url()); 463 | EtebaseAccount *etebase = etebase_account_restore(client, TEST_USER_SESSION, NULL, 0); 464 | etebase_client_destroy(client); 465 | 466 | etebase_account_force_server_url(etebase, get_test_url()); 467 | etebase_account_fetch_token(etebase); 468 | 469 | EtebaseCollectionManager *col_mgr = etebase_account_get_collection_manager(etebase); 470 | EtebaseItemMetadata *col_meta = etebase_item_metadata_new(); 471 | etebase_item_metadata_set_name(col_meta, "Name"); 472 | EtebaseCollection *col = etebase_collection_manager_create(col_mgr, COL_TYPE, col_meta, "", 0); 473 | etebase_item_metadata_destroy(col_meta); 474 | 475 | etebase_collection_manager_upload(col_mgr, col, NULL); 476 | char *stoken; 477 | { 478 | EtebaseCollection *col2 = etebase_collection_manager_fetch(col_mgr, etebase_collection_get_uid(col), NULL); 479 | stoken = strdup(etebase_collection_get_stoken(col2)); 480 | etebase_collection_destroy(col2); 481 | } 482 | 483 | EtebaseItemManager *item_mgr = etebase_collection_manager_get_item_manager(col_mgr, col); 484 | 485 | EtebaseItemMetadata *item_meta = etebase_item_metadata_new(); 486 | etebase_item_metadata_set_name(item_meta, "Item 1"); 487 | EtebaseItem *item1 = etebase_item_manager_create(item_mgr, item_meta, "", 0); 488 | etebase_item_metadata_set_name(item_meta, "Item 2"); 489 | EtebaseItem *item2 = etebase_item_manager_create(item_mgr, item_meta, "", 0); 490 | etebase_item_metadata_destroy(item_meta); 491 | 492 | const EtebaseItem *items[] = { item1, item2 }; 493 | fail_if(etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL)); 494 | 495 | char *item1_uid = strdup(etebase_item_get_uid(item1)); 496 | char *item2_uid = strdup(etebase_item_get_uid(item2)); 497 | { 498 | // -> On device A: 499 | EtebaseItem *item1 = etebase_item_manager_fetch(item_mgr, item1_uid, NULL); 500 | EtebaseItem *item2 = etebase_item_manager_fetch(item_mgr, item2_uid, NULL); 501 | 502 | { 503 | // -> On device B: 504 | EtebaseItem *item1 = etebase_item_manager_fetch(item_mgr, item1_uid, NULL); 505 | const char tmp[] = "Something else for item 1"; 506 | etebase_item_set_content(item1, tmp, strlen(tmp)); 507 | 508 | const EtebaseItem *items[] = { item1 }; 509 | etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL); 510 | 511 | etebase_item_destroy(item1); 512 | } 513 | 514 | 515 | // -> On device A (using the previously saved item) 516 | const char tmp[] = "New content for item 1"; 517 | etebase_item_set_content(item1, tmp, strlen(tmp)); 518 | const char tmp2[] = "New content for item 2"; 519 | etebase_item_set_content(item2, tmp2, strlen(tmp2)); 520 | 521 | // Will fail because item1 changed on device B 522 | const EtebaseItem *items[] = { item1, item2 }; 523 | fail_if(!etebase_item_manager_transaction(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL)); 524 | // Will succeed 525 | fail_if(etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL)); 526 | // Will succeed because item2 hasn't changed on device B 527 | // const EtebaseItem *items2[] = { item2 }; 528 | // fail_if(etebase_item_manager_transaction(item_mgr, items2, ETEBASE_UTILS_C_ARRAY_LEN(items2), NULL)); 529 | 530 | etebase_item_destroy(item2); 531 | etebase_item_destroy(item1); 532 | } 533 | 534 | const char *item_uid = item1_uid; 535 | const char *another_item_uid = item2_uid; 536 | // Using stoken 537 | { 538 | // -> On device A: 539 | EtebaseItem *item = etebase_item_manager_fetch(item_mgr, item_uid, NULL); 540 | 541 | { 542 | // -> On device B: 543 | EtebaseItem *another_item = etebase_item_manager_fetch(item_mgr, another_item_uid, NULL); 544 | const char tmp[] = "content for another item"; 545 | etebase_item_set_content(item1, tmp, strlen(tmp)); 546 | 547 | const EtebaseItem *items[] = { another_item }; 548 | etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL); 549 | 550 | etebase_item_destroy(another_item); 551 | } 552 | 553 | 554 | // -> On device A (using the previously saved item and stoken) 555 | const char tmp[] = "new secret content"; 556 | etebase_item_set_content(item, tmp, strlen(tmp)); 557 | 558 | 559 | // Will both fail 560 | EtebaseFetchOptions *fetch_options = etebase_fetch_options_new(); 561 | etebase_fetch_options_set_stoken(fetch_options, stoken); 562 | const EtebaseItem *items[] = { item }; 563 | fail_if(!etebase_item_manager_transaction(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), fetch_options)); 564 | fail_if(!etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), fetch_options)); 565 | 566 | etebase_fetch_options_destroy(fetch_options); 567 | 568 | // Will both succeed 569 | fail_if(etebase_item_manager_transaction(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL)); 570 | // fail_if(etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL)); 571 | 572 | etebase_item_destroy(item); 573 | } 574 | 575 | // Additional dependencies 576 | { 577 | // -> On device A: 578 | EtebaseItem *item1 = etebase_item_manager_fetch(item_mgr, item1_uid, NULL); 579 | EtebaseItem *item2 = etebase_item_manager_fetch(item_mgr, item2_uid, NULL); 580 | 581 | 582 | { 583 | // -> On device B: 584 | EtebaseItem *item1 = etebase_item_manager_fetch(item_mgr, item1_uid, NULL); 585 | const char tmp[] = "Something else for item 1"; 586 | etebase_item_set_content(item1, tmp, strlen(tmp)); 587 | 588 | const EtebaseItem *items[] = { item1 }; 589 | etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL); 590 | 591 | etebase_item_destroy(item1); 592 | } 593 | 594 | 595 | // -> On device A (using the previously saved collection) 596 | const char tmp2[] = "New content for item 2"; 597 | etebase_item_set_content(item2, tmp2, strlen(tmp2)); 598 | 599 | // Will both fail because item1 changed 600 | { 601 | const EtebaseItem *items[] = { item2 }; 602 | const EtebaseItem *deps[] = { item1 }; 603 | fail_if(!etebase_item_manager_batch_deps(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), deps, ETEBASE_UTILS_C_ARRAY_LEN(deps), NULL)); 604 | fail_if(!etebase_item_manager_transaction_deps(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), deps, ETEBASE_UTILS_C_ARRAY_LEN(deps), NULL)); 605 | } 606 | 607 | // Can even use the item in both the list and deps in batch 608 | // Will fail because item1 changed on device B 609 | const EtebaseItem *items[] = { item1, item2 }; 610 | const EtebaseItem *deps[] = { item1 }; 611 | fail_if(!etebase_item_manager_batch_deps(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), deps, ETEBASE_UTILS_C_ARRAY_LEN(deps), NULL)); 612 | 613 | etebase_item_destroy(item2); 614 | etebase_item_destroy(item1); 615 | } 616 | 617 | free(item2_uid); 618 | free(item1_uid); 619 | 620 | free(stoken); 621 | 622 | etebase_account_logout(etebase); 623 | 624 | etebase_item_destroy(item2); 625 | etebase_item_destroy(item1); 626 | etebase_item_manager_destroy(item_mgr); 627 | etebase_collection_destroy(col); 628 | etebase_collection_manager_destroy(col_mgr); 629 | etebase_account_destroy(etebase); 630 | return 0; 631 | } 632 | 633 | int 634 | test_collection_as_item() { 635 | EtebaseClient *client = etebase_client_new("libetebase-test", get_test_url()); 636 | EtebaseAccount *etebase = etebase_account_restore(client, TEST_USER_SESSION, NULL, 0); 637 | etebase_client_destroy(client); 638 | 639 | etebase_account_force_server_url(etebase, get_test_url()); 640 | etebase_account_fetch_token(etebase); 641 | 642 | EtebaseCollectionManager *col_mgr = etebase_account_get_collection_manager(etebase); 643 | EtebaseItemMetadata *col_meta = etebase_item_metadata_new(); 644 | etebase_item_metadata_set_name(col_meta, "Name"); 645 | EtebaseCollection *col = etebase_collection_manager_create(col_mgr, COL_TYPE, col_meta, "", 0); 646 | etebase_item_metadata_destroy(col_meta); 647 | 648 | etebase_collection_manager_upload(col_mgr, col, NULL); 649 | 650 | EtebaseItemManager *item_mgr = etebase_collection_manager_get_item_manager(col_mgr, col); 651 | 652 | EtebaseItemMetadata *item_meta = etebase_item_metadata_new(); 653 | etebase_item_metadata_set_name(item_meta, "Item 1"); 654 | EtebaseItem *item1 = etebase_item_manager_create(item_mgr, item_meta, "", 0); 655 | etebase_item_metadata_set_name(item_meta, "Item 2"); 656 | EtebaseItem *item2 = etebase_item_manager_create(item_mgr, item_meta, "", 0); 657 | etebase_item_metadata_destroy(item_meta); 658 | 659 | char *item1_uid = strdup(etebase_item_get_uid(item1)); 660 | char *item2_uid = strdup(etebase_item_get_uid(item2)); 661 | { 662 | // Get the item out of the collection 663 | EtebaseItem *col_item = etebase_collection_as_item(col); 664 | 665 | // The collection item can then be used like any other item: 666 | { 667 | const EtebaseItem *items[] = { col_item, item1 }; 668 | const EtebaseItem *deps[] = { item2 }; 669 | fail_if(!etebase_item_manager_transaction_deps(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), 670 | deps, ETEBASE_UTILS_C_ARRAY_LEN(deps), NULL)); 671 | } 672 | { 673 | const EtebaseItem *items[] = { item1, item2 }; 674 | const EtebaseItem *deps[] = { col_item }; 675 | fail_if(etebase_item_manager_transaction_deps(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), 676 | deps, ETEBASE_UTILS_C_ARRAY_LEN(deps), NULL)); 677 | } 678 | { 679 | const EtebaseItem *items[] = { col_item, item1 }; 680 | fail_if(!etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL)); 681 | } 682 | 683 | // In addition, these are true: 684 | assert_int_eq(etebase_collection_get_meta_raw(col, NULL, 0), etebase_item_get_meta_raw(col_item, NULL, 0)); 685 | assert_int_eq(etebase_collection_get_content(col, NULL, 0), etebase_item_get_content(col_item, NULL, 0)); 686 | 687 | etebase_item_destroy(col_item); 688 | } 689 | 690 | free(item2_uid); 691 | free(item1_uid); 692 | 693 | etebase_account_logout(etebase); 694 | 695 | etebase_item_destroy(item2); 696 | etebase_item_destroy(item1); 697 | etebase_item_manager_destroy(item_mgr); 698 | etebase_collection_destroy(col); 699 | etebase_collection_manager_destroy(col_mgr); 700 | etebase_account_destroy(etebase); 701 | return 0; 702 | } 703 | 704 | int 705 | test_item_revisions() { 706 | EtebaseClient *client = etebase_client_new("libetebase-test", get_test_url()); 707 | EtebaseAccount *etebase = etebase_account_restore(client, TEST_USER_SESSION, NULL, 0); 708 | etebase_client_destroy(client); 709 | 710 | etebase_account_force_server_url(etebase, get_test_url()); 711 | etebase_account_fetch_token(etebase); 712 | 713 | EtebaseCollectionManager *col_mgr = etebase_account_get_collection_manager(etebase); 714 | EtebaseItemMetadata *col_meta = etebase_item_metadata_new(); 715 | etebase_item_metadata_set_name(col_meta, "Name"); 716 | EtebaseCollection *col = etebase_collection_manager_create(col_mgr, COL_TYPE, col_meta, "", 0); 717 | etebase_item_metadata_destroy(col_meta); 718 | 719 | etebase_collection_manager_upload(col_mgr, col, NULL); 720 | 721 | EtebaseItemManager *item_mgr = etebase_collection_manager_get_item_manager(col_mgr, col); 722 | 723 | EtebaseItemMetadata *item_meta = etebase_item_metadata_new(); 724 | etebase_item_metadata_set_item_type(item_meta, "file"); 725 | const char item_content[] = "First draft"; 726 | EtebaseItem *item = etebase_item_manager_create(item_mgr, item_meta, item_content, strlen(item_content)); 727 | etebase_item_metadata_destroy(item_meta); 728 | 729 | const EtebaseItem *items[] = { item }; 730 | fail_if(etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL)); 731 | 732 | { 733 | const char tmp[] = "Second draft"; 734 | etebase_item_set_content(item, tmp, strlen(tmp)); 735 | 736 | const EtebaseItem *items[] = { item }; 737 | fail_if(etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL)); 738 | } 739 | 740 | EtebaseItemRevisionsListResponse *revisions = etebase_item_manager_item_revisions(item_mgr, item, NULL); 741 | 742 | uintptr_t list_len = etebase_item_revisions_list_response_get_data_length(revisions); 743 | assert_int_eq(2, list_len); 744 | 745 | { 746 | // Revisions are normal items so we can use them as such 747 | const EtebaseItem *list_items[list_len]; 748 | fail_if(etebase_item_revisions_list_response_get_data(revisions, list_items)); 749 | char content2[100]; 750 | intptr_t content2_size = etebase_item_get_content(list_items[1], &content2, sizeof(content2)); 751 | content2[content2_size] = 0; 752 | assert_str_eq(content2, "First draft"); 753 | } 754 | 755 | etebase_item_revisions_list_response_destroy(revisions); 756 | 757 | etebase_account_logout(etebase); 758 | 759 | etebase_item_destroy(item); 760 | etebase_item_manager_destroy(item_mgr); 761 | etebase_collection_destroy(col); 762 | etebase_collection_manager_destroy(col_mgr); 763 | etebase_account_destroy(etebase); 764 | return 0; 765 | } 766 | 767 | int 768 | test_basic_invitations() { 769 | EtebaseClient *client = etebase_client_new("libetebase-test", get_test_url()); 770 | EtebaseAccount *etebase = etebase_account_restore(client, TEST_USER_SESSION, NULL, 0); 771 | etebase_client_destroy(client); 772 | 773 | etebase_account_force_server_url(etebase, get_test_url()); 774 | etebase_account_fetch_token(etebase); 775 | 776 | EtebaseCollectionManager *col_mgr = etebase_account_get_collection_manager(etebase); 777 | EtebaseItemMetadata *col_meta = etebase_item_metadata_new(); 778 | etebase_item_metadata_set_name(col_meta, "Name"); 779 | EtebaseCollection *col = etebase_collection_manager_create(col_mgr, COL_TYPE, col_meta, "", 0); 780 | etebase_item_metadata_destroy(col_meta); 781 | 782 | etebase_collection_manager_upload(col_mgr, col, NULL); 783 | 784 | EtebaseCollectionInvitationManager *invitation_manager = etebase_account_get_invitation_manager(etebase); 785 | 786 | // Fetch their public key 787 | EtebaseUserProfile *user2 = etebase_invitation_manager_fetch_user_profile(invitation_manager, "test_user2"); 788 | fail_if(!user2); 789 | 790 | // Verify user2.pubkey is indeed the pubkey you expect. 791 | // This is done in a secure channel (e.g. encrypted chat or in person) 792 | 793 | // Assuming the pubkey is as expected, send the invitation 794 | fail_if(etebase_invitation_manager_invite(invitation_manager, col, "test_user2", etebase_user_profile_get_pubkey(user2), etebase_user_profile_get_pubkey_size(user2), ETEBASE_COLLECTION_ACCESS_LEVEL_READ_ONLY)); 795 | 796 | { 797 | EtebaseCollectionMemberManager *member_manager = etebase_collection_manager_get_member_manager(col_mgr, col); 798 | 799 | EtebaseMemberListResponse *members = etebase_collection_member_manager_list(member_manager, NULL); 800 | 801 | uintptr_t data_len = etebase_member_list_response_get_data_length(members); 802 | const EtebaseCollectionMember *data[data_len]; 803 | etebase_member_list_response_get_data(members, data); 804 | 805 | // Print the users and their access levels 806 | const EtebaseCollectionMember *member = data[0]; 807 | assert_str_eq("test_user", etebase_collection_member_get_username(member)); 808 | assert_int_eq(ETEBASE_COLLECTION_ACCESS_LEVEL_ADMIN, etebase_collection_member_get_access_level(member)); 809 | 810 | etebase_member_list_response_destroy(members); 811 | etebase_collection_member_manager_destroy(member_manager); 812 | } 813 | 814 | etebase_user_profile_destroy(user2); 815 | etebase_invitation_manager_destroy(invitation_manager); 816 | 817 | etebase_account_logout(etebase); 818 | 819 | etebase_collection_destroy(col); 820 | etebase_collection_manager_destroy(col_mgr); 821 | etebase_account_destroy(etebase); 822 | return 0; 823 | } 824 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ansi_term" 7 | version = "0.12.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 10 | dependencies = [ 11 | "winapi", 12 | ] 13 | 14 | [[package]] 15 | name = "atty" 16 | version = "0.2.14" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 19 | dependencies = [ 20 | "hermit-abi", 21 | "libc", 22 | "winapi", 23 | ] 24 | 25 | [[package]] 26 | name = "autocfg" 27 | version = "1.1.0" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 30 | 31 | [[package]] 32 | name = "base64" 33 | version = "0.13.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 36 | 37 | [[package]] 38 | name = "bitflags" 39 | version = "1.3.2" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 42 | 43 | [[package]] 44 | name = "bumpalo" 45 | version = "3.9.1" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" 48 | 49 | [[package]] 50 | name = "byteorder" 51 | version = "1.4.3" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 54 | 55 | [[package]] 56 | name = "bytes" 57 | version = "1.1.0" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" 60 | 61 | [[package]] 62 | name = "cbindgen" 63 | version = "0.14.3" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "e6e03a705df2e735cc5486f104a48e25a8f72ae06eaea5b7753a81270ed00859" 66 | dependencies = [ 67 | "clap", 68 | "heck", 69 | "log", 70 | "proc-macro2", 71 | "quote", 72 | "serde", 73 | "serde_json", 74 | "syn", 75 | "tempfile", 76 | "toml", 77 | ] 78 | 79 | [[package]] 80 | name = "cc" 81 | version = "1.0.73" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 84 | 85 | [[package]] 86 | name = "cfg-if" 87 | version = "1.0.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 90 | 91 | [[package]] 92 | name = "clap" 93 | version = "2.34.0" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 96 | dependencies = [ 97 | "ansi_term", 98 | "atty", 99 | "bitflags", 100 | "strsim", 101 | "textwrap", 102 | "unicode-width", 103 | "vec_map", 104 | ] 105 | 106 | [[package]] 107 | name = "core-foundation" 108 | version = "0.9.3" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 111 | dependencies = [ 112 | "core-foundation-sys", 113 | "libc", 114 | ] 115 | 116 | [[package]] 117 | name = "core-foundation-sys" 118 | version = "0.8.3" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 121 | 122 | [[package]] 123 | name = "crossbeam-channel" 124 | version = "0.5.4" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" 127 | dependencies = [ 128 | "cfg-if", 129 | "crossbeam-utils", 130 | ] 131 | 132 | [[package]] 133 | name = "crossbeam-deque" 134 | version = "0.8.1" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" 137 | dependencies = [ 138 | "cfg-if", 139 | "crossbeam-epoch", 140 | "crossbeam-utils", 141 | ] 142 | 143 | [[package]] 144 | name = "crossbeam-epoch" 145 | version = "0.9.8" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" 148 | dependencies = [ 149 | "autocfg", 150 | "cfg-if", 151 | "crossbeam-utils", 152 | "lazy_static", 153 | "memoffset", 154 | "scopeguard", 155 | ] 156 | 157 | [[package]] 158 | name = "crossbeam-utils" 159 | version = "0.8.8" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" 162 | dependencies = [ 163 | "cfg-if", 164 | "lazy_static", 165 | ] 166 | 167 | [[package]] 168 | name = "ed25519" 169 | version = "1.4.1" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "3d5c4b5e5959dc2c2b89918d8e2cc40fcdd623cef026ed09d2f0ee05199dc8e4" 172 | dependencies = [ 173 | "signature", 174 | ] 175 | 176 | [[package]] 177 | name = "either" 178 | version = "1.6.1" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 181 | 182 | [[package]] 183 | name = "encoding_rs" 184 | version = "0.8.30" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" 187 | dependencies = [ 188 | "cfg-if", 189 | ] 190 | 191 | [[package]] 192 | name = "etebase" 193 | version = "0.5.3" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "dcbba1b6712a7e6f6c7a3b2c2aea88d4bd3fc3e3e1a642ebc4b8a97de6753a27" 196 | dependencies = [ 197 | "libsodium-sys", 198 | "remove_dir_all 0.6.1", 199 | "reqwest", 200 | "rmp-serde", 201 | "serde", 202 | "serde_bytes", 203 | "serde_repr", 204 | "sodiumoxide", 205 | "url", 206 | ] 207 | 208 | [[package]] 209 | name = "fastrand" 210 | version = "1.7.0" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" 213 | dependencies = [ 214 | "instant", 215 | ] 216 | 217 | [[package]] 218 | name = "fnv" 219 | version = "1.0.7" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 222 | 223 | [[package]] 224 | name = "foreign-types" 225 | version = "0.3.2" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 228 | dependencies = [ 229 | "foreign-types-shared", 230 | ] 231 | 232 | [[package]] 233 | name = "foreign-types-shared" 234 | version = "0.1.1" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 237 | 238 | [[package]] 239 | name = "form_urlencoded" 240 | version = "1.0.1" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 243 | dependencies = [ 244 | "matches", 245 | "percent-encoding", 246 | ] 247 | 248 | [[package]] 249 | name = "futures-channel" 250 | version = "0.3.21" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" 253 | dependencies = [ 254 | "futures-core", 255 | ] 256 | 257 | [[package]] 258 | name = "futures-core" 259 | version = "0.3.21" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" 262 | 263 | [[package]] 264 | name = "futures-io" 265 | version = "0.3.21" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" 268 | 269 | [[package]] 270 | name = "futures-sink" 271 | version = "0.3.21" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" 274 | 275 | [[package]] 276 | name = "futures-task" 277 | version = "0.3.21" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" 280 | 281 | [[package]] 282 | name = "futures-util" 283 | version = "0.3.21" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" 286 | dependencies = [ 287 | "futures-core", 288 | "futures-io", 289 | "futures-task", 290 | "memchr", 291 | "pin-project-lite", 292 | "pin-utils", 293 | "slab", 294 | ] 295 | 296 | [[package]] 297 | name = "h2" 298 | version = "0.3.12" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "62eeb471aa3e3c9197aa4bfeabfe02982f6dc96f750486c0bb0009ac58b26d2b" 301 | dependencies = [ 302 | "bytes", 303 | "fnv", 304 | "futures-core", 305 | "futures-sink", 306 | "futures-util", 307 | "http", 308 | "indexmap", 309 | "slab", 310 | "tokio", 311 | "tokio-util", 312 | "tracing", 313 | ] 314 | 315 | [[package]] 316 | name = "hashbrown" 317 | version = "0.11.2" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 320 | 321 | [[package]] 322 | name = "heck" 323 | version = "0.3.3" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 326 | dependencies = [ 327 | "unicode-segmentation", 328 | ] 329 | 330 | [[package]] 331 | name = "hermit-abi" 332 | version = "0.1.19" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 335 | dependencies = [ 336 | "libc", 337 | ] 338 | 339 | [[package]] 340 | name = "http" 341 | version = "0.2.6" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" 344 | dependencies = [ 345 | "bytes", 346 | "fnv", 347 | "itoa", 348 | ] 349 | 350 | [[package]] 351 | name = "http-body" 352 | version = "0.4.4" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" 355 | dependencies = [ 356 | "bytes", 357 | "http", 358 | "pin-project-lite", 359 | ] 360 | 361 | [[package]] 362 | name = "httparse" 363 | version = "1.6.0" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" 366 | 367 | [[package]] 368 | name = "httpdate" 369 | version = "1.0.2" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 372 | 373 | [[package]] 374 | name = "hyper" 375 | version = "0.14.18" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" 378 | dependencies = [ 379 | "bytes", 380 | "futures-channel", 381 | "futures-core", 382 | "futures-util", 383 | "h2", 384 | "http", 385 | "http-body", 386 | "httparse", 387 | "httpdate", 388 | "itoa", 389 | "pin-project-lite", 390 | "socket2", 391 | "tokio", 392 | "tower-service", 393 | "tracing", 394 | "want", 395 | ] 396 | 397 | [[package]] 398 | name = "hyper-tls" 399 | version = "0.5.0" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" 402 | dependencies = [ 403 | "bytes", 404 | "hyper", 405 | "native-tls", 406 | "tokio", 407 | "tokio-native-tls", 408 | ] 409 | 410 | [[package]] 411 | name = "idna" 412 | version = "0.2.3" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 415 | dependencies = [ 416 | "matches", 417 | "unicode-bidi", 418 | "unicode-normalization", 419 | ] 420 | 421 | [[package]] 422 | name = "indexmap" 423 | version = "1.8.1" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" 426 | dependencies = [ 427 | "autocfg", 428 | "hashbrown", 429 | ] 430 | 431 | [[package]] 432 | name = "instant" 433 | version = "0.1.12" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 436 | dependencies = [ 437 | "cfg-if", 438 | ] 439 | 440 | [[package]] 441 | name = "ipnet" 442 | version = "2.4.0" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c" 445 | 446 | [[package]] 447 | name = "itoa" 448 | version = "1.0.1" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" 451 | 452 | [[package]] 453 | name = "js-sys" 454 | version = "0.3.56" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" 457 | dependencies = [ 458 | "wasm-bindgen", 459 | ] 460 | 461 | [[package]] 462 | name = "lazy_static" 463 | version = "1.4.0" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 466 | 467 | [[package]] 468 | name = "libc" 469 | version = "0.2.121" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" 472 | 473 | [[package]] 474 | name = "libetebase" 475 | version = "0.5.3" 476 | dependencies = [ 477 | "cbindgen", 478 | "etebase", 479 | ] 480 | 481 | [[package]] 482 | name = "libsodium-sys" 483 | version = "0.2.7" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "6b779387cd56adfbc02ea4a668e704f729be8d6a6abd2c27ca5ee537849a92fd" 486 | dependencies = [ 487 | "cc", 488 | "libc", 489 | "pkg-config", 490 | "walkdir", 491 | ] 492 | 493 | [[package]] 494 | name = "log" 495 | version = "0.4.16" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" 498 | dependencies = [ 499 | "cfg-if", 500 | ] 501 | 502 | [[package]] 503 | name = "matches" 504 | version = "0.1.9" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" 507 | 508 | [[package]] 509 | name = "memchr" 510 | version = "2.4.1" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 513 | 514 | [[package]] 515 | name = "memoffset" 516 | version = "0.6.5" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 519 | dependencies = [ 520 | "autocfg", 521 | ] 522 | 523 | [[package]] 524 | name = "mime" 525 | version = "0.3.16" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 528 | 529 | [[package]] 530 | name = "mio" 531 | version = "0.8.2" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" 534 | dependencies = [ 535 | "libc", 536 | "log", 537 | "miow", 538 | "ntapi", 539 | "wasi", 540 | "winapi", 541 | ] 542 | 543 | [[package]] 544 | name = "miow" 545 | version = "0.3.7" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 548 | dependencies = [ 549 | "winapi", 550 | ] 551 | 552 | [[package]] 553 | name = "native-tls" 554 | version = "0.2.10" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" 557 | dependencies = [ 558 | "lazy_static", 559 | "libc", 560 | "log", 561 | "openssl", 562 | "openssl-probe", 563 | "openssl-sys", 564 | "schannel", 565 | "security-framework", 566 | "security-framework-sys", 567 | "tempfile", 568 | ] 569 | 570 | [[package]] 571 | name = "ntapi" 572 | version = "0.3.7" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" 575 | dependencies = [ 576 | "winapi", 577 | ] 578 | 579 | [[package]] 580 | name = "num-traits" 581 | version = "0.2.14" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 584 | dependencies = [ 585 | "autocfg", 586 | ] 587 | 588 | [[package]] 589 | name = "num_cpus" 590 | version = "1.13.1" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 593 | dependencies = [ 594 | "hermit-abi", 595 | "libc", 596 | ] 597 | 598 | [[package]] 599 | name = "once_cell" 600 | version = "1.10.0" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" 603 | 604 | [[package]] 605 | name = "openssl" 606 | version = "0.10.38" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" 609 | dependencies = [ 610 | "bitflags", 611 | "cfg-if", 612 | "foreign-types", 613 | "libc", 614 | "once_cell", 615 | "openssl-sys", 616 | ] 617 | 618 | [[package]] 619 | name = "openssl-probe" 620 | version = "0.1.5" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 623 | 624 | [[package]] 625 | name = "openssl-sys" 626 | version = "0.9.72" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" 629 | dependencies = [ 630 | "autocfg", 631 | "cc", 632 | "libc", 633 | "pkg-config", 634 | "vcpkg", 635 | ] 636 | 637 | [[package]] 638 | name = "percent-encoding" 639 | version = "2.1.0" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 642 | 643 | [[package]] 644 | name = "pin-project-lite" 645 | version = "0.2.8" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" 648 | 649 | [[package]] 650 | name = "pin-utils" 651 | version = "0.1.0" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 654 | 655 | [[package]] 656 | name = "pkg-config" 657 | version = "0.3.25" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" 660 | 661 | [[package]] 662 | name = "proc-macro2" 663 | version = "1.0.36" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 666 | dependencies = [ 667 | "unicode-xid", 668 | ] 669 | 670 | [[package]] 671 | name = "quote" 672 | version = "1.0.17" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" 675 | dependencies = [ 676 | "proc-macro2", 677 | ] 678 | 679 | [[package]] 680 | name = "rayon" 681 | version = "1.5.1" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" 684 | dependencies = [ 685 | "autocfg", 686 | "crossbeam-deque", 687 | "either", 688 | "rayon-core", 689 | ] 690 | 691 | [[package]] 692 | name = "rayon-core" 693 | version = "1.9.1" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" 696 | dependencies = [ 697 | "crossbeam-channel", 698 | "crossbeam-deque", 699 | "crossbeam-utils", 700 | "lazy_static", 701 | "num_cpus", 702 | ] 703 | 704 | [[package]] 705 | name = "redox_syscall" 706 | version = "0.2.13" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" 709 | dependencies = [ 710 | "bitflags", 711 | ] 712 | 713 | [[package]] 714 | name = "remove_dir_all" 715 | version = "0.5.3" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 718 | dependencies = [ 719 | "winapi", 720 | ] 721 | 722 | [[package]] 723 | name = "remove_dir_all" 724 | version = "0.6.1" 725 | source = "registry+https://github.com/rust-lang/crates.io-index" 726 | checksum = "d7b19f5c2df95a07275e7224924cc62f76f04525f4fda801473f85e325e81977" 727 | dependencies = [ 728 | "log", 729 | "num_cpus", 730 | "rayon", 731 | "winapi", 732 | ] 733 | 734 | [[package]] 735 | name = "reqwest" 736 | version = "0.11.10" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb" 739 | dependencies = [ 740 | "base64", 741 | "bytes", 742 | "encoding_rs", 743 | "futures-core", 744 | "futures-util", 745 | "h2", 746 | "http", 747 | "http-body", 748 | "hyper", 749 | "hyper-tls", 750 | "ipnet", 751 | "js-sys", 752 | "lazy_static", 753 | "log", 754 | "mime", 755 | "native-tls", 756 | "percent-encoding", 757 | "pin-project-lite", 758 | "serde", 759 | "serde_json", 760 | "serde_urlencoded", 761 | "tokio", 762 | "tokio-native-tls", 763 | "url", 764 | "wasm-bindgen", 765 | "wasm-bindgen-futures", 766 | "web-sys", 767 | "winreg", 768 | ] 769 | 770 | [[package]] 771 | name = "rmp" 772 | version = "0.8.10" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "4f55e5fa1446c4d5dd1f5daeed2a4fe193071771a2636274d0d7a3b082aa7ad6" 775 | dependencies = [ 776 | "byteorder", 777 | "num-traits", 778 | ] 779 | 780 | [[package]] 781 | name = "rmp-serde" 782 | version = "1.0.0" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | checksum = "f3eedffbfcc6a428f230c04baf8f59bd73c1781361e4286111fe900849aaddaf" 785 | dependencies = [ 786 | "byteorder", 787 | "rmp", 788 | "serde", 789 | ] 790 | 791 | [[package]] 792 | name = "ryu" 793 | version = "1.0.9" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" 796 | 797 | [[package]] 798 | name = "same-file" 799 | version = "1.0.6" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 802 | dependencies = [ 803 | "winapi-util", 804 | ] 805 | 806 | [[package]] 807 | name = "schannel" 808 | version = "0.1.19" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 811 | dependencies = [ 812 | "lazy_static", 813 | "winapi", 814 | ] 815 | 816 | [[package]] 817 | name = "scopeguard" 818 | version = "1.1.0" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 821 | 822 | [[package]] 823 | name = "security-framework" 824 | version = "2.6.1" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" 827 | dependencies = [ 828 | "bitflags", 829 | "core-foundation", 830 | "core-foundation-sys", 831 | "libc", 832 | "security-framework-sys", 833 | ] 834 | 835 | [[package]] 836 | name = "security-framework-sys" 837 | version = "2.6.1" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" 840 | dependencies = [ 841 | "core-foundation-sys", 842 | "libc", 843 | ] 844 | 845 | [[package]] 846 | name = "serde" 847 | version = "1.0.136" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" 850 | dependencies = [ 851 | "serde_derive", 852 | ] 853 | 854 | [[package]] 855 | name = "serde_bytes" 856 | version = "0.11.5" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" 859 | dependencies = [ 860 | "serde", 861 | ] 862 | 863 | [[package]] 864 | name = "serde_derive" 865 | version = "1.0.136" 866 | source = "registry+https://github.com/rust-lang/crates.io-index" 867 | checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" 868 | dependencies = [ 869 | "proc-macro2", 870 | "quote", 871 | "syn", 872 | ] 873 | 874 | [[package]] 875 | name = "serde_json" 876 | version = "1.0.79" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" 879 | dependencies = [ 880 | "itoa", 881 | "ryu", 882 | "serde", 883 | ] 884 | 885 | [[package]] 886 | name = "serde_repr" 887 | version = "0.1.7" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" 890 | dependencies = [ 891 | "proc-macro2", 892 | "quote", 893 | "syn", 894 | ] 895 | 896 | [[package]] 897 | name = "serde_urlencoded" 898 | version = "0.7.1" 899 | source = "registry+https://github.com/rust-lang/crates.io-index" 900 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 901 | dependencies = [ 902 | "form_urlencoded", 903 | "itoa", 904 | "ryu", 905 | "serde", 906 | ] 907 | 908 | [[package]] 909 | name = "signature" 910 | version = "1.5.0" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4" 913 | 914 | [[package]] 915 | name = "slab" 916 | version = "0.4.5" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" 919 | 920 | [[package]] 921 | name = "socket2" 922 | version = "0.4.4" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" 925 | dependencies = [ 926 | "libc", 927 | "winapi", 928 | ] 929 | 930 | [[package]] 931 | name = "sodiumoxide" 932 | version = "0.2.7" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "e26be3acb6c2d9a7aac28482586a7856436af4cfe7100031d219de2d2ecb0028" 935 | dependencies = [ 936 | "ed25519", 937 | "libc", 938 | "libsodium-sys", 939 | "serde", 940 | ] 941 | 942 | [[package]] 943 | name = "strsim" 944 | version = "0.8.0" 945 | source = "registry+https://github.com/rust-lang/crates.io-index" 946 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 947 | 948 | [[package]] 949 | name = "syn" 950 | version = "1.0.90" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f" 953 | dependencies = [ 954 | "proc-macro2", 955 | "quote", 956 | "unicode-xid", 957 | ] 958 | 959 | [[package]] 960 | name = "tempfile" 961 | version = "3.3.0" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 964 | dependencies = [ 965 | "cfg-if", 966 | "fastrand", 967 | "libc", 968 | "redox_syscall", 969 | "remove_dir_all 0.5.3", 970 | "winapi", 971 | ] 972 | 973 | [[package]] 974 | name = "textwrap" 975 | version = "0.11.0" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 978 | dependencies = [ 979 | "unicode-width", 980 | ] 981 | 982 | [[package]] 983 | name = "tinyvec" 984 | version = "1.5.1" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" 987 | dependencies = [ 988 | "tinyvec_macros", 989 | ] 990 | 991 | [[package]] 992 | name = "tinyvec_macros" 993 | version = "0.1.0" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 996 | 997 | [[package]] 998 | name = "tokio" 999 | version = "1.17.0" 1000 | source = "registry+https://github.com/rust-lang/crates.io-index" 1001 | checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" 1002 | dependencies = [ 1003 | "bytes", 1004 | "libc", 1005 | "memchr", 1006 | "mio", 1007 | "num_cpus", 1008 | "pin-project-lite", 1009 | "socket2", 1010 | "winapi", 1011 | ] 1012 | 1013 | [[package]] 1014 | name = "tokio-native-tls" 1015 | version = "0.3.0" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" 1018 | dependencies = [ 1019 | "native-tls", 1020 | "tokio", 1021 | ] 1022 | 1023 | [[package]] 1024 | name = "tokio-util" 1025 | version = "0.6.9" 1026 | source = "registry+https://github.com/rust-lang/crates.io-index" 1027 | checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" 1028 | dependencies = [ 1029 | "bytes", 1030 | "futures-core", 1031 | "futures-sink", 1032 | "log", 1033 | "pin-project-lite", 1034 | "tokio", 1035 | ] 1036 | 1037 | [[package]] 1038 | name = "toml" 1039 | version = "0.5.8" 1040 | source = "registry+https://github.com/rust-lang/crates.io-index" 1041 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 1042 | dependencies = [ 1043 | "serde", 1044 | ] 1045 | 1046 | [[package]] 1047 | name = "tower-service" 1048 | version = "0.3.1" 1049 | source = "registry+https://github.com/rust-lang/crates.io-index" 1050 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 1051 | 1052 | [[package]] 1053 | name = "tracing" 1054 | version = "0.1.32" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" 1057 | dependencies = [ 1058 | "cfg-if", 1059 | "pin-project-lite", 1060 | "tracing-core", 1061 | ] 1062 | 1063 | [[package]] 1064 | name = "tracing-core" 1065 | version = "0.1.23" 1066 | source = "registry+https://github.com/rust-lang/crates.io-index" 1067 | checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" 1068 | dependencies = [ 1069 | "lazy_static", 1070 | ] 1071 | 1072 | [[package]] 1073 | name = "try-lock" 1074 | version = "0.2.3" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1077 | 1078 | [[package]] 1079 | name = "unicode-bidi" 1080 | version = "0.3.7" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" 1083 | 1084 | [[package]] 1085 | name = "unicode-normalization" 1086 | version = "0.1.19" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" 1089 | dependencies = [ 1090 | "tinyvec", 1091 | ] 1092 | 1093 | [[package]] 1094 | name = "unicode-segmentation" 1095 | version = "1.9.0" 1096 | source = "registry+https://github.com/rust-lang/crates.io-index" 1097 | checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" 1098 | 1099 | [[package]] 1100 | name = "unicode-width" 1101 | version = "0.1.9" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 1104 | 1105 | [[package]] 1106 | name = "unicode-xid" 1107 | version = "0.2.2" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1110 | 1111 | [[package]] 1112 | name = "url" 1113 | version = "2.2.2" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 1116 | dependencies = [ 1117 | "form_urlencoded", 1118 | "idna", 1119 | "matches", 1120 | "percent-encoding", 1121 | ] 1122 | 1123 | [[package]] 1124 | name = "vcpkg" 1125 | version = "0.2.15" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1128 | 1129 | [[package]] 1130 | name = "vec_map" 1131 | version = "0.8.2" 1132 | source = "registry+https://github.com/rust-lang/crates.io-index" 1133 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 1134 | 1135 | [[package]] 1136 | name = "walkdir" 1137 | version = "2.3.2" 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" 1139 | checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" 1140 | dependencies = [ 1141 | "same-file", 1142 | "winapi", 1143 | "winapi-util", 1144 | ] 1145 | 1146 | [[package]] 1147 | name = "want" 1148 | version = "0.3.0" 1149 | source = "registry+https://github.com/rust-lang/crates.io-index" 1150 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1151 | dependencies = [ 1152 | "log", 1153 | "try-lock", 1154 | ] 1155 | 1156 | [[package]] 1157 | name = "wasi" 1158 | version = "0.11.0+wasi-snapshot-preview1" 1159 | source = "registry+https://github.com/rust-lang/crates.io-index" 1160 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1161 | 1162 | [[package]] 1163 | name = "wasm-bindgen" 1164 | version = "0.2.79" 1165 | source = "registry+https://github.com/rust-lang/crates.io-index" 1166 | checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" 1167 | dependencies = [ 1168 | "cfg-if", 1169 | "wasm-bindgen-macro", 1170 | ] 1171 | 1172 | [[package]] 1173 | name = "wasm-bindgen-backend" 1174 | version = "0.2.79" 1175 | source = "registry+https://github.com/rust-lang/crates.io-index" 1176 | checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" 1177 | dependencies = [ 1178 | "bumpalo", 1179 | "lazy_static", 1180 | "log", 1181 | "proc-macro2", 1182 | "quote", 1183 | "syn", 1184 | "wasm-bindgen-shared", 1185 | ] 1186 | 1187 | [[package]] 1188 | name = "wasm-bindgen-futures" 1189 | version = "0.4.29" 1190 | source = "registry+https://github.com/rust-lang/crates.io-index" 1191 | checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" 1192 | dependencies = [ 1193 | "cfg-if", 1194 | "js-sys", 1195 | "wasm-bindgen", 1196 | "web-sys", 1197 | ] 1198 | 1199 | [[package]] 1200 | name = "wasm-bindgen-macro" 1201 | version = "0.2.79" 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" 1203 | checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" 1204 | dependencies = [ 1205 | "quote", 1206 | "wasm-bindgen-macro-support", 1207 | ] 1208 | 1209 | [[package]] 1210 | name = "wasm-bindgen-macro-support" 1211 | version = "0.2.79" 1212 | source = "registry+https://github.com/rust-lang/crates.io-index" 1213 | checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" 1214 | dependencies = [ 1215 | "proc-macro2", 1216 | "quote", 1217 | "syn", 1218 | "wasm-bindgen-backend", 1219 | "wasm-bindgen-shared", 1220 | ] 1221 | 1222 | [[package]] 1223 | name = "wasm-bindgen-shared" 1224 | version = "0.2.79" 1225 | source = "registry+https://github.com/rust-lang/crates.io-index" 1226 | checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" 1227 | 1228 | [[package]] 1229 | name = "web-sys" 1230 | version = "0.3.56" 1231 | source = "registry+https://github.com/rust-lang/crates.io-index" 1232 | checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" 1233 | dependencies = [ 1234 | "js-sys", 1235 | "wasm-bindgen", 1236 | ] 1237 | 1238 | [[package]] 1239 | name = "winapi" 1240 | version = "0.3.9" 1241 | source = "registry+https://github.com/rust-lang/crates.io-index" 1242 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1243 | dependencies = [ 1244 | "winapi-i686-pc-windows-gnu", 1245 | "winapi-x86_64-pc-windows-gnu", 1246 | ] 1247 | 1248 | [[package]] 1249 | name = "winapi-i686-pc-windows-gnu" 1250 | version = "0.4.0" 1251 | source = "registry+https://github.com/rust-lang/crates.io-index" 1252 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1253 | 1254 | [[package]] 1255 | name = "winapi-util" 1256 | version = "0.1.5" 1257 | source = "registry+https://github.com/rust-lang/crates.io-index" 1258 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1259 | dependencies = [ 1260 | "winapi", 1261 | ] 1262 | 1263 | [[package]] 1264 | name = "winapi-x86_64-pc-windows-gnu" 1265 | version = "0.4.0" 1266 | source = "registry+https://github.com/rust-lang/crates.io-index" 1267 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1268 | 1269 | [[package]] 1270 | name = "winreg" 1271 | version = "0.10.1" 1272 | source = "registry+https://github.com/rust-lang/crates.io-index" 1273 | checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" 1274 | dependencies = [ 1275 | "winapi", 1276 | ] 1277 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2020 Etebase Authors 2 | // SPDX-License-Identifier: LGPL-2.1-only 3 | 4 | #![allow(non_camel_case_types)] 5 | 6 | use std::cell::RefCell; 7 | use std::os::raw::{c_char, c_void}; 8 | use std::ffi::{CString, CStr}; 9 | use std::path::PathBuf; 10 | 11 | use etebase::{ 12 | DEFAULT_SERVER_URL, 13 | 14 | Client, 15 | User, 16 | Account, 17 | 18 | Collection, 19 | Item, 20 | ItemMetadata, 21 | 22 | CollectionAccessLevel, 23 | SignedInvitation, 24 | CollectionMember, 25 | RemovedCollection, 26 | 27 | UserProfile, 28 | 29 | fs_cache::FileSystemCache, 30 | 31 | error::Error, 32 | managers::{ 33 | CollectionManager, 34 | ItemManager, 35 | CollectionInvitationManager, 36 | CollectionMemberManager, 37 | }, 38 | }; 39 | 40 | macro_rules! try_or_null { 41 | ($x:expr) => { 42 | match $x { 43 | Ok(val) => val, 44 | Err(err) => { 45 | update_last_error(Error::from(err)); 46 | return std::ptr::null_mut(); 47 | } 48 | }; 49 | }; 50 | } 51 | 52 | macro_rules! try_or_int { 53 | ($x:expr) => { 54 | match $x { 55 | Ok(val) => val, 56 | Err(err) => { 57 | update_last_error(Error::from(err)); 58 | return -1; 59 | } 60 | }; 61 | }; 62 | } 63 | 64 | fn ptr_to_option(val: *const T) -> Option<*const T> { 65 | if val.is_null() { 66 | None 67 | } else { 68 | Some(val) 69 | } 70 | } 71 | 72 | thread_local! { 73 | static LAST_ERROR: RefCell> = RefCell::new(None); 74 | } 75 | 76 | fn update_last_error(err: Error) { 77 | LAST_ERROR.with(|prev| { 78 | *prev.borrow_mut() = Some(err); 79 | }); 80 | } 81 | 82 | #[repr(u32)] 83 | pub enum ErrorCode { 84 | NoError, 85 | 86 | Generic, 87 | UrlParse, 88 | MsgPack, 89 | ProgrammingError, 90 | MissingContent, 91 | Padding, 92 | Base64, 93 | Encryption, 94 | Unauthorized, 95 | Conflict, 96 | PermissionDenied, 97 | NotFound, 98 | 99 | Connection, 100 | TemporaryServerError, 101 | ServerError, 102 | Http, 103 | } 104 | 105 | /// Get the error code 106 | /// 107 | /// Call this immediately after a failed API call 108 | #[no_mangle] 109 | pub extern fn etebase_error_get_code() -> ErrorCode { 110 | LAST_ERROR.with(|prev| { 111 | match *prev.borrow() { 112 | Some(ref err) => match err { 113 | Error::Generic(_) => ErrorCode::Generic, 114 | Error::UrlParse(_) => ErrorCode::UrlParse, 115 | Error::MsgPack(_) => ErrorCode::MsgPack, 116 | Error::ProgrammingError(_) => ErrorCode::ProgrammingError, 117 | Error::MissingContent(_) => ErrorCode::MissingContent, 118 | Error::Padding(_) => ErrorCode::Padding, 119 | Error::Base64(_) => ErrorCode::Base64, 120 | Error::Encryption(_) => ErrorCode::Encryption, 121 | Error::Unauthorized(_) => ErrorCode::Unauthorized, 122 | Error::Conflict(_) => ErrorCode::Conflict, 123 | Error::PermissionDenied(_) => ErrorCode::PermissionDenied, 124 | Error::NotFound(_) => ErrorCode::NotFound, 125 | 126 | Error::Connection(_) => ErrorCode::Connection, 127 | Error::TemporaryServerError(_) => ErrorCode::TemporaryServerError, 128 | Error::ServerError(_) => ErrorCode::ServerError, 129 | Error::Http(_) => ErrorCode::Http, 130 | }, 131 | None => ErrorCode::NoError, 132 | } 133 | }) 134 | } 135 | 136 | /// Get the error message 137 | /// 138 | /// Call this immediately after a failed API call 139 | #[no_mangle] 140 | pub extern fn etebase_error_get_message() -> *const c_char { 141 | thread_local! { 142 | static LAST: RefCell> = RefCell::new(None); 143 | } 144 | LAST_ERROR.with(|prev| { 145 | match *prev.borrow() { 146 | Some(ref err) => { 147 | let err = CString::new(err.to_string()).ok(); 148 | LAST.with(|ret| { 149 | *ret.borrow_mut() = err; 150 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 151 | }) 152 | }, 153 | None => std::ptr::null(), 154 | } 155 | }) 156 | } 157 | 158 | // Class Utils { 159 | 160 | /// The URL of the main hosted server 161 | #[no_mangle] 162 | pub extern fn etebase_get_default_server_url() -> *const c_char { 163 | thread_local! { 164 | static LAST: RefCell> = RefCell::new(None); 165 | } 166 | LAST.with(|ret| { 167 | *ret.borrow_mut() = CString::new(DEFAULT_SERVER_URL).ok(); 168 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 169 | }) 170 | } 171 | 172 | /// Convert a Base64 URL encoded string to a buffer 173 | /// 174 | /// @param string the Base64 URL encoded string 175 | /// @param[out] buf the output byte buffer 176 | /// @param buf_maxlen the maximum number of bytes to be written to buf 177 | /// @param[out] buf_len variable to store the buffer length in 178 | #[no_mangle] 179 | pub unsafe extern fn etebase_utils_from_base64(string: *const c_char, buf: *mut c_void, buf_maxlen: usize, buf_len: *mut usize) -> i32 { 180 | let string = CStr::from_ptr(string).to_str().unwrap(); 181 | let buf_inner = try_or_int!(etebase::utils::from_base64(string)); 182 | if buf_inner.len() > buf_maxlen { 183 | try_or_int!(Err(Error::ProgrammingError("buf_maxlen is too small for output"))); 184 | return -1; // Never actually called, try_or_int returns already. 185 | } 186 | buf.copy_from_nonoverlapping(buf_inner.as_ptr() as *const c_void, buf_inner.len()); 187 | if !buf_len.is_null() { 188 | *buf_len = buf_inner.len(); 189 | } 190 | 0 191 | } 192 | 193 | /// Convert a buffer to a Base64 URL encoded string 194 | /// 195 | /// @param bytes the buffer to convert 196 | /// @param bytes_size the size of the input buffer 197 | /// @param[out] out the output string 198 | /// @param out_maxlen the maximum length of string to be written 199 | #[no_mangle] 200 | pub unsafe extern fn etebase_utils_to_base64(bytes: *const c_void, bytes_size: usize, out: *mut c_char, out_maxlen: usize) -> i32 { 201 | let bytes = std::slice::from_raw_parts(bytes as *const u8, bytes_size); 202 | let b64 = try_or_int!(etebase::utils::to_base64(bytes)); 203 | if b64.len() > out_maxlen { 204 | try_or_int!(Err(Error::ProgrammingError("out_maxlen is too small for output"))); 205 | return -1; // Never actually called, try_or_int returns already. 206 | } 207 | out.copy_from_nonoverlapping(b64.as_ptr() as *const c_char, b64.len()); 208 | *out.offset(b64.len() as isize) = 0; 209 | 0 210 | } 211 | 212 | /// Return a buffer filled with cryptographically random bytes 213 | /// 214 | /// @param[out] buf the output byte buffer 215 | /// @param size the size of the returned buffer 216 | #[no_mangle] 217 | pub unsafe extern fn etebase_utils_randombytes(buf: *mut c_void, size: usize) -> i32 { 218 | let bytes = etebase::utils::randombytes(size); 219 | buf.copy_from_nonoverlapping(bytes.as_ptr() as *const c_void, size); 220 | 0 221 | } 222 | 223 | #[no_mangle] 224 | pub static ETEBASE_UTILS_PRETTY_FINGERPRINT_SIZE: usize = 225 | 1 + // Null 226 | 4 + // Newlines 227 | (3 * 12) + // Spacing 228 | (5 * 16); // Digits 229 | 230 | /// Return a pretty formatted fingerprint of the content 231 | /// 232 | /// For example: 233 | /// ``` 234 | /// 45680 71497 88570 93128 235 | /// 19189 84243 25687 20837 236 | /// 47924 46071 54113 18789 237 | /// ``` 238 | /// 239 | /// @param content the content to create a fingerprint for 240 | /// @param content_size the size of the content buffer 241 | /// @param[out] buf the output byte buffer 242 | #[no_mangle] 243 | pub unsafe extern fn etebase_utils_pretty_fingerprint(content: *const c_void, content_size: usize, buf: *mut c_char) -> i32 { 244 | let content = std::slice::from_raw_parts(content as *const u8, content_size); 245 | let fingerprint = etebase::pretty_fingerprint(content); 246 | buf.copy_from_nonoverlapping(fingerprint.as_ptr() as *const c_char, ETEBASE_UTILS_PRETTY_FINGERPRINT_SIZE); 247 | *buf.offset(ETEBASE_UTILS_PRETTY_FINGERPRINT_SIZE as isize) = 0; 248 | 0 249 | } 250 | 251 | // } 252 | 253 | 254 | // Class Client { 255 | 256 | #[no_mangle] 257 | pub unsafe extern fn etebase_client_new(client_name: *const c_char, server_url: *const c_char) -> *mut Client { 258 | let client_name = CStr::from_ptr(client_name).to_str().unwrap(); 259 | let server_url = CStr::from_ptr(server_url).to_str().unwrap(); 260 | Box::into_raw( 261 | Box::new( 262 | try_or_null!(Client::new(client_name, server_url)) 263 | ) 264 | ) 265 | } 266 | 267 | #[allow(non_snake_case)] 268 | #[no_mangle] 269 | unsafe extern "C" fn vec_u8_from_size(size: u32) -> *mut Vec { 270 | let vec = Vec::with_capacity(size as usize); 271 | return Box::into_raw(Box::new(vec)); 272 | } 273 | 274 | #[allow(non_snake_case)] 275 | #[no_mangle] 276 | unsafe extern "C" fn vec_u8_size(vec: &mut Vec) -> u32 { 277 | return vec.len() as u32; 278 | } 279 | 280 | #[allow(non_snake_case)] 281 | #[no_mangle] 282 | unsafe extern "C" fn vec_u8_buf(vec: &mut Vec) -> *const u8 { 283 | let ret = vec.as_ptr(); 284 | return ret; 285 | } 286 | 287 | 288 | #[no_mangle] 289 | pub unsafe extern fn etebase_client_set_server_url(this: &mut Client, server_url: *const c_char) -> i32 { 290 | let server_url = CStr::from_ptr(server_url).to_str().unwrap(); 291 | try_or_int!(this.set_server_url(server_url)); 292 | 0 293 | } 294 | 295 | /// Returns 0 if client is pointing an etebase server, 1 if not, -1 on error 296 | /// 297 | /// @param client the object handle 298 | #[no_mangle] 299 | pub unsafe extern fn etebase_client_check_etebase_server(client: &Client) -> i32 { 300 | let ret = try_or_int!(Account::is_etebase_server(client)); 301 | if ret { 0 } else { 1 } 302 | } 303 | 304 | /// Destroy the object 305 | /// 306 | /// @param this_ the object handle 307 | #[no_mangle] 308 | pub unsafe extern fn etebase_client_destroy(this: *mut Client) { 309 | let this = Box::from_raw(this); 310 | drop(this); 311 | } 312 | 313 | // } 314 | 315 | 316 | // Class User { 317 | 318 | /// Return a new user instance 319 | /// 320 | /// Should be destroyed with `etebase_user_destroy` 321 | /// 322 | /// @param username the user's username 323 | /// @param email the user's email 324 | #[no_mangle] 325 | pub unsafe extern fn etebase_user_new(username: *const c_char, email: *const c_char) -> *mut User { 326 | let username = CStr::from_ptr(username).to_str().unwrap(); 327 | let email = CStr::from_ptr(email).to_str().unwrap(); 328 | Box::into_raw( 329 | Box::new( 330 | User::new(username, email) 331 | ) 332 | ) 333 | } 334 | 335 | /// Set the username 336 | /// 337 | /// @param this_ the object handle 338 | /// @param username the user's username 339 | #[no_mangle] 340 | pub unsafe extern fn etebase_user_set_username(this: &mut User, username: *const c_char) { 341 | let username = CStr::from_ptr(username).to_str().unwrap(); 342 | this.set_username(username); 343 | } 344 | 345 | /// Get the username 346 | /// 347 | /// @param this_ the object handle 348 | #[no_mangle] 349 | pub unsafe extern fn etebase_user_get_username(this: &User) -> *const c_char { 350 | thread_local! { 351 | static LAST: RefCell> = RefCell::new(None); 352 | } 353 | LAST.with(|ret| { 354 | *ret.borrow_mut() = CString::new(this.username()).ok(); 355 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 356 | }) 357 | } 358 | 359 | /// Set the email address 360 | /// 361 | /// @param this_ the object handle 362 | /// @param email the user's email address 363 | #[no_mangle] 364 | pub unsafe extern fn etebase_user_set_email(this: &mut User, email: *const c_char) { 365 | let email = CStr::from_ptr(email).to_str().unwrap(); 366 | this.set_email(email); 367 | } 368 | 369 | /// Get the email address 370 | /// 371 | /// @param this_ the object handle 372 | #[no_mangle] 373 | pub unsafe extern fn etebase_user_get_email(this: &User) -> *const c_char { 374 | thread_local! { 375 | static LAST: RefCell> = RefCell::new(None); 376 | } 377 | LAST.with(|ret| { 378 | *ret.borrow_mut() = CString::new(this.email()).ok(); 379 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 380 | }) 381 | } 382 | 383 | /// Destroy the object 384 | /// 385 | /// @param this_ the object handle 386 | #[no_mangle] 387 | pub unsafe extern fn etebase_user_destroy(this: *mut User) { 388 | let this = Box::from_raw(this); 389 | drop(this); 390 | } 391 | 392 | // } 393 | 394 | 395 | // Class Account { 396 | 397 | /// Login a user and return a handle to an `EtebaseAccount` object 398 | /// 399 | /// @param client the already setup `EtebaseClient` object 400 | /// @param username the user's username. This is not the same as the user's email. 401 | /// @param password the user's password 402 | #[no_mangle] 403 | pub unsafe extern fn etebase_account_login(client: &Client, username: *const c_char, password: *const c_char) -> *mut Account { 404 | let username = CStr::from_ptr(username).to_str().unwrap(); 405 | let password = CStr::from_ptr(password).to_str().unwrap(); 406 | Box::into_raw( 407 | Box::new( 408 | try_or_null!(Account::login(client.clone(), username, password)) 409 | ) 410 | ) 411 | } 412 | 413 | /// Signup a new user account and return a handle to it 414 | /// 415 | /// @param client the already setup `EtebaseClient` object 416 | /// @param user the already setup `EtebaseUser` object 417 | /// @param password the password to signup with 418 | #[no_mangle] 419 | pub unsafe extern fn etebase_account_signup(client: &Client, user: &User, password: *const c_char) -> *mut Account { 420 | let password = CStr::from_ptr(password).to_str().unwrap(); 421 | Box::into_raw( 422 | Box::new( 423 | try_or_null!(Account::signup(client.clone(), user, password)) 424 | ) 425 | ) 426 | } 427 | 428 | /// Fetch a new auth token for the account and update the `EtebaseAccount` object with it 429 | /// 430 | /// @param this_ the object handle 431 | #[no_mangle] 432 | pub unsafe extern fn etebase_account_fetch_token(this: &mut Account) -> i32 { 433 | try_or_int!(this.fetch_token()); 434 | 0 435 | } 436 | 437 | /// Fetch the link to the user dashboard of the account 438 | /// 439 | /// @param this_ the object handle 440 | #[no_mangle] 441 | pub unsafe extern fn etebase_account_fetch_dashboard_url(this: &Account) -> *mut c_char { 442 | let url = try_or_null!(this.fetch_dashboard_url()); 443 | try_or_null!(CString::new(url)).into_raw() 444 | } 445 | 446 | /// Change the server URL for this account handle 447 | /// 448 | /// @param this_ the object handle 449 | /// @param server_url the new server URL to be set 450 | #[no_mangle] 451 | pub unsafe extern fn etebase_account_force_server_url(this: &mut Account, server_url: *const c_char) -> i32 { 452 | let server_url = CStr::from_ptr(server_url).to_str().unwrap(); 453 | try_or_int!(this.force_server_url(server_url)); 454 | 0 455 | } 456 | 457 | /// Change the user's login password 458 | /// 459 | /// @param this_ the object handle 460 | /// @param password the new password to be set 461 | #[no_mangle] 462 | pub unsafe extern fn etebase_account_change_password(this: &mut Account, password: *const c_char) -> i32 { 463 | let password = CStr::from_ptr(password).to_str().unwrap(); 464 | try_or_int!(this.change_password(password)); 465 | 0 466 | } 467 | 468 | /// Logout the user from the current session and invalidate the authentication token 469 | /// 470 | /// @param this_ the object handle 471 | #[no_mangle] 472 | pub unsafe extern fn etebase_account_logout(this: &mut Account) -> i32 { 473 | try_or_int!(this.logout()); 474 | 0 475 | } 476 | 477 | /// Return a `EtebaseCollectionManager` for creating, fetching and uploading collections 478 | /// 479 | /// @param this_ the object handle 480 | #[no_mangle] 481 | pub unsafe extern fn etebase_account_get_collection_manager(this: &Account) -> *mut CollectionManager { 482 | Box::into_raw( 483 | Box::new( 484 | try_or_null!(this.collection_manager()) 485 | ) 486 | ) 487 | } 488 | 489 | /// Return a `EtebaseCollectionInvitationManager` for managing collection invitations 490 | /// 491 | /// @param this_ the object handle 492 | #[no_mangle] 493 | pub unsafe extern fn etebase_account_get_invitation_manager(this: &Account) -> *mut CollectionInvitationManager { 494 | Box::into_raw( 495 | Box::new( 496 | try_or_null!(this.invitation_manager()) 497 | ) 498 | ) 499 | } 500 | 501 | /// Save the account object to a string for restoring it later using `etebase_account_restore` 502 | /// 503 | /// @param this_ the object handle 504 | /// @param encryption_key used to encrypt the returned account string to enhance security 505 | /// @param encryption_key_size size of the encryption_key 506 | #[no_mangle] 507 | pub unsafe extern fn etebase_account_save(this: &Account, encryption_key: *const c_void, encryption_key_size: usize) -> *mut c_char { 508 | let encryption_key = if encryption_key.is_null() { 509 | None 510 | } else { 511 | Some(std::slice::from_raw_parts(encryption_key as *const u8, encryption_key_size)) 512 | }; 513 | let saved = try_or_null!(this.save(encryption_key)); 514 | try_or_null!(CString::new(saved)).into_raw() 515 | } 516 | 517 | /// Restore and return the account object from the string obtained using `etebase_account_save` 518 | /// 519 | /// @param client the already setup `EtebaseClient` object 520 | /// @param account_data_stored the stored account string 521 | /// @param encryption_key the same encryption key passed to `etebase_account_save` while saving the account 522 | /// @param encryption_key_size size of the encryption_key 523 | #[no_mangle] 524 | pub unsafe extern fn etebase_account_restore(client: &Client, account_data_stored: *const c_char, encryption_key: *const c_void, encryption_key_size: usize) -> *mut Account { 525 | let account_data_stored = CStr::from_ptr(account_data_stored).to_str().unwrap(); 526 | let encryption_key = if encryption_key.is_null() { 527 | None 528 | } else { 529 | Some(std::slice::from_raw_parts(encryption_key as *const u8, encryption_key_size)) 530 | }; 531 | Box::into_raw( 532 | Box::new( 533 | try_or_null!(Account::restore(client.clone(), account_data_stored, encryption_key)) 534 | ) 535 | ) 536 | } 537 | 538 | /// Destroy the object 539 | /// 540 | /// @param this_ the object handle 541 | #[no_mangle] 542 | pub unsafe extern fn etebase_account_destroy(this: *mut Account) { 543 | let this = Box::from_raw(this); 544 | drop(this); 545 | } 546 | 547 | // } 548 | 549 | 550 | // Class RemovedCollection { 551 | 552 | /// The uid of the removed collection 553 | /// 554 | /// @param this_ the object handle 555 | #[no_mangle] 556 | pub unsafe extern fn etebase_removed_collection_get_uid(this: &RemovedCollection) -> *const c_char { 557 | thread_local! { 558 | static LAST: RefCell> = RefCell::new(None); 559 | } 560 | LAST.with(|ret| { 561 | *ret.borrow_mut() = CString::new(this.uid()).ok(); 562 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 563 | }) 564 | } 565 | 566 | /// Destroy the object 567 | /// 568 | /// @param this_ the object handle 569 | #[no_mangle] 570 | pub unsafe extern fn etebase_removed_collection_destroy(this: *mut RemovedCollection) { 571 | let this = Box::from_raw(this); 572 | drop(this); 573 | } 574 | 575 | // } 576 | 577 | 578 | // Class CollectionListResponse { 579 | 580 | type CollectionListResponse = etebase::CollectionListResponse; 581 | 582 | /// Sync token for the list response 583 | /// 584 | /// @param this_ the object handle 585 | #[no_mangle] 586 | pub unsafe extern fn etebase_collection_list_response_get_stoken(this: &CollectionListResponse) -> *const c_char { 587 | thread_local! { 588 | static LAST: RefCell> = RefCell::new(None); 589 | } 590 | LAST.with(|ret| { 591 | *ret.borrow_mut() = this.stoken().map(|x| CString::new(x).unwrap()); 592 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 593 | }) 594 | } 595 | 596 | /// List of collections included in the response 597 | /// 598 | /// @param this_ the object handle 599 | /// @param[out] data the array to store the collections in 600 | #[no_mangle] 601 | pub unsafe extern fn etebase_collection_list_response_get_data(this: &CollectionListResponse, data: *mut *const Collection) -> i32 { 602 | let ret: Vec<&Collection> = this.data().iter().collect(); 603 | data.copy_from_nonoverlapping(ret.as_ptr() as *mut *const Collection, ret.len()); 604 | 0 605 | } 606 | 607 | /// The number of collections included in the response 608 | /// 609 | /// @param this_ the object handle 610 | #[no_mangle] 611 | pub unsafe extern fn etebase_collection_list_response_get_data_length(this: &CollectionListResponse) -> usize { 612 | this.data().len() 613 | } 614 | 615 | /// Indicates whether there are no more collections to fetch 616 | /// 617 | /// @param this_ the object handle 618 | #[no_mangle] 619 | pub unsafe extern fn etebase_collection_list_response_is_done(this: &CollectionListResponse) -> bool { 620 | this.done() 621 | } 622 | 623 | /// The list of collections to which the user lost access 624 | /// 625 | /// @param this_ the object handle 626 | /// @param[out] data the array to store the collections in 627 | #[no_mangle] 628 | pub unsafe extern fn etebase_collection_list_response_get_removed_memberships(this: &CollectionListResponse, data: *mut *const RemovedCollection) -> i32 { 629 | let removed_memberships = this.removed_memberships(); 630 | if removed_memberships.is_none() { 631 | return 0; 632 | } 633 | 634 | let ret: Vec<&RemovedCollection> = this.removed_memberships().unwrap().iter().collect(); 635 | data.copy_from_nonoverlapping(ret.as_ptr() as *mut *const RemovedCollection, ret.len()); 636 | 0 637 | } 638 | 639 | /// The number of collections to which the user lost access 640 | /// 641 | /// @param this_ the object handle 642 | #[no_mangle] 643 | pub unsafe extern fn etebase_collection_list_response_get_removed_memberships_length(this: &CollectionListResponse) -> usize { 644 | if let Some(removed_memberships) = this.removed_memberships() { 645 | removed_memberships.len() 646 | } else { 647 | 0 648 | } 649 | } 650 | 651 | /// Destroy the object 652 | /// 653 | /// @param this_ the object handle 654 | #[no_mangle] 655 | pub unsafe extern fn etebase_collection_list_response_destroy(this: *mut CollectionListResponse) { 656 | let this = Box::from_raw(this); 657 | drop(this); 658 | } 659 | 660 | // } 661 | 662 | 663 | // Class ItemListResponse { 664 | 665 | type ItemListResponse = etebase::ItemListResponse; 666 | 667 | /// Sync token for the list response 668 | /// 669 | /// @param this_ the object handle 670 | #[no_mangle] 671 | pub unsafe extern fn etebase_item_list_response_get_stoken(this: &ItemListResponse) -> *const c_char { 672 | thread_local! { 673 | static LAST: RefCell> = RefCell::new(None); 674 | } 675 | LAST.with(|ret| { 676 | *ret.borrow_mut() = this.stoken().map(|x| CString::new(x).unwrap()); 677 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 678 | }) 679 | } 680 | 681 | /// List of items included in the response 682 | /// 683 | /// @param this_ the object handle 684 | /// @param[out] data the array to store the items in 685 | #[no_mangle] 686 | pub unsafe extern fn etebase_item_list_response_get_data(this: &ItemListResponse, data: *mut *const Item) -> i32 { 687 | let ret: Vec<&Item> = this.data().iter().collect(); 688 | data.copy_from_nonoverlapping(ret.as_ptr() as *mut *const Item, ret.len()); 689 | 0 690 | } 691 | 692 | /// The number of items included in the response 693 | /// 694 | /// @param this_ the object handle 695 | #[no_mangle] 696 | pub unsafe extern fn etebase_item_list_response_get_data_length(this: &ItemListResponse) -> usize { 697 | this.data().len() 698 | } 699 | 700 | /// Indicates whether there are no more items to fetch 701 | /// 702 | /// @param this_ the object handle 703 | #[no_mangle] 704 | pub unsafe extern fn etebase_item_list_response_is_done(this: &ItemListResponse) -> bool { 705 | this.done() 706 | } 707 | 708 | /// Destroy the object 709 | /// 710 | /// @param this_ the object handle 711 | #[no_mangle] 712 | pub unsafe extern fn etebase_item_list_response_destroy(this: *mut ItemListResponse) { 713 | let this = Box::from_raw(this); 714 | drop(this); 715 | } 716 | 717 | // } 718 | 719 | 720 | // Class ItemRevisionsListResponse { 721 | 722 | type ItemRevisionsListResponse = etebase::IteratorListResponse; 723 | 724 | /// Iterator for the list response 725 | /// 726 | /// @param this_ the object handle 727 | #[no_mangle] 728 | pub unsafe extern fn etebase_item_revisions_list_response_get_iterator(this: &ItemRevisionsListResponse) -> *const c_char { 729 | thread_local! { 730 | static LAST: RefCell> = RefCell::new(None); 731 | } 732 | LAST.with(|ret| { 733 | *ret.borrow_mut() = this.iterator().map(|x| CString::new(x).unwrap()); 734 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 735 | }) 736 | } 737 | 738 | /// List of item revisions included in the response 739 | /// 740 | /// @param this_ the object handle 741 | /// @param[out] data the array to store the items in 742 | #[no_mangle] 743 | pub unsafe extern fn etebase_item_revisions_list_response_get_data(this: &ItemRevisionsListResponse, data: *mut *const Item) -> i32 { 744 | let ret: Vec<&Item> = this.data().iter().collect(); 745 | data.copy_from_nonoverlapping(ret.as_ptr() as *mut *const Item, ret.len()); 746 | 0 747 | } 748 | 749 | /// The number of item revisions included in the response 750 | /// 751 | /// @param this_ the object handle 752 | #[no_mangle] 753 | pub unsafe extern fn etebase_item_revisions_list_response_get_data_length(this: &ItemRevisionsListResponse) -> usize { 754 | this.data().len() 755 | } 756 | 757 | /// Indicates whether there is no more data to fetch 758 | /// 759 | /// @param this_ the object handle 760 | #[no_mangle] 761 | pub unsafe extern fn etebase_item_revisions_list_response_is_done(this: &ItemRevisionsListResponse) -> bool { 762 | this.done() 763 | } 764 | 765 | /// Destroy the object 766 | /// 767 | /// @param this_ the object handle 768 | #[no_mangle] 769 | pub unsafe extern fn etebase_item_revisions_list_response_destroy(this: *mut ItemRevisionsListResponse) { 770 | let this = Box::from_raw(this); 771 | drop(this); 772 | } 773 | 774 | // } 775 | 776 | 777 | // Enum PrefetchOption { 778 | 779 | /// Dictates how much data to prefetch when passed to `EtebaseFetchOptions` 780 | #[repr(u32)] 781 | pub enum PrefetchOption { 782 | /// Automatically decide based on the size of the data fetched 783 | Auto, 784 | /// Attempt to fetch a more lightweight (medium) amount of data 785 | Medium, 786 | } 787 | 788 | // } 789 | 790 | 791 | // Class FetchOptions { 792 | 793 | /// Configuration options for data fetching 794 | pub struct FetchOptions { 795 | limit: Option, 796 | stoken: Option, 797 | iterator: Option, 798 | prefetch: Option, 799 | with_collection: Option, 800 | } 801 | 802 | impl FetchOptions { 803 | pub fn new() -> Self { 804 | Self { 805 | limit: None, 806 | stoken: None, 807 | iterator: None, 808 | prefetch: None, 809 | with_collection: None, 810 | } 811 | } 812 | 813 | pub fn limit(&mut self, limit: usize) { 814 | self.limit = Some(limit); 815 | } 816 | 817 | pub fn prefetch(&mut self, prefetch: PrefetchOption) { 818 | let prefetch = match prefetch { 819 | PrefetchOption::Auto => etebase::PrefetchOption::Auto, 820 | PrefetchOption::Medium => etebase::PrefetchOption::Medium, 821 | }; 822 | self.prefetch = Some(prefetch); 823 | } 824 | 825 | pub fn with_collection(&mut self, with_collection: bool) { 826 | self.with_collection = Some(with_collection); 827 | } 828 | 829 | pub fn iterator(&mut self, iterator: Option<&str>) { 830 | self.iterator = iterator.map(str::to_string); 831 | } 832 | 833 | pub fn stoken(&mut self, stoken: Option<&str>) { 834 | self.stoken = stoken.map(str::to_string); 835 | } 836 | 837 | pub fn to_fetch_options<'a>(&'a self) -> etebase::FetchOptions<'a> { 838 | let mut ret = etebase::FetchOptions::new(); 839 | if let Some(limit) = self.limit { 840 | ret = ret.limit(limit); 841 | } 842 | if let Some(prefetch) = &self.prefetch { 843 | ret = ret.prefetch(prefetch); 844 | } 845 | if let Some(with_collection) = self.with_collection { 846 | ret = ret.with_collection(with_collection); 847 | } 848 | ret = ret.iterator(self.iterator.as_deref()); 849 | ret = ret.stoken(self.stoken.as_deref()); 850 | ret 851 | } 852 | } 853 | 854 | /// Return a new fetch options object 855 | /// 856 | /// Should be destroyed with `etebase_fetch_options_destroy` 857 | #[no_mangle] 858 | pub unsafe extern fn etebase_fetch_options_new() -> *mut FetchOptions { 859 | Box::into_raw( 860 | Box::new( 861 | FetchOptions::new() 862 | ) 863 | ) 864 | } 865 | 866 | /// Limit the amount of items returned 867 | /// 868 | /// @param this_ the object handle 869 | /// @param limit the limit to set 870 | #[no_mangle] 871 | pub unsafe extern fn etebase_fetch_options_set_limit(this: &mut FetchOptions, limit: usize) { 872 | this.limit(limit); 873 | } 874 | 875 | /// How much data to prefetech 876 | /// 877 | /// @param this_ the object handle 878 | /// @param prefetch the prefetch option to set 879 | #[no_mangle] 880 | pub unsafe extern fn etebase_fetch_options_set_prefetch(this: &mut FetchOptions, prefetch: PrefetchOption) { 881 | this.prefetch(prefetch); 882 | } 883 | 884 | /// Toggle fetching the collection's item 885 | /// 886 | /// @param this_ the object handle 887 | /// @param with_collection set whether to fetch the collection's item 888 | #[no_mangle] 889 | pub unsafe extern fn etebase_fetch_options_set_with_collection(this: &mut FetchOptions, with_collection: bool) { 890 | this.with_collection(with_collection); 891 | } 892 | 893 | /// The current iterator to start from (when iterating lists) 894 | /// 895 | /// @param this_ the object handle 896 | /// @param iterator the iterator to start from 897 | #[no_mangle] 898 | pub unsafe extern fn etebase_fetch_options_set_iterator(this: &mut FetchOptions, iterator: *const c_char) { 899 | let iterator = ptr_to_option(iterator).map(|x| CStr::from_ptr(x).to_str().unwrap()); 900 | this.iterator(iterator); 901 | } 902 | 903 | /// The sync token to fetch with 904 | /// 905 | /// @param this_ the object handle 906 | /// @param stoken the sync token to set 907 | #[no_mangle] 908 | pub unsafe extern fn etebase_fetch_options_set_stoken(this: &mut FetchOptions, stoken: *const c_char) { 909 | let stoken = ptr_to_option(stoken).map(|x| CStr::from_ptr(x).to_str().unwrap()); 910 | this.stoken(stoken); 911 | } 912 | 913 | /// Destroy the object 914 | /// 915 | /// @param this_ the object handle 916 | #[no_mangle] 917 | pub unsafe extern fn etebase_fetch_options_destroy(this: *mut FetchOptions) { 918 | let this = Box::from_raw(this); 919 | drop(this); 920 | } 921 | 922 | // } 923 | 924 | 925 | // Class ItemMetadata { 926 | 927 | /// Create a new metadata object 928 | /// 929 | /// Should be destroyed with `etebase_item_metadata_destroy` 930 | #[no_mangle] 931 | pub unsafe extern fn etebase_item_metadata_new() -> *mut ItemMetadata { 932 | Box::into_raw( 933 | Box::new( 934 | ItemMetadata::new() 935 | ) 936 | ) 937 | } 938 | 939 | /// Set the item type 940 | /// 941 | /// @param this_ the object handle 942 | /// @param item_type the type to be set 943 | #[no_mangle] 944 | pub unsafe extern fn etebase_item_metadata_set_item_type(this: &mut ItemMetadata, item_type: *const c_char) { 945 | let item_type = ptr_to_option(item_type).map(|x| CStr::from_ptr(x).to_str().unwrap()); 946 | this.set_item_type(item_type); 947 | } 948 | 949 | /// The item type 950 | /// 951 | /// @param this_ the object handle 952 | #[no_mangle] 953 | pub unsafe extern fn etebase_item_metadata_get_item_type(this: &ItemMetadata) -> *const c_char { 954 | thread_local! { 955 | static LAST: RefCell> = RefCell::new(None); 956 | } 957 | LAST.with(|ret| { 958 | *ret.borrow_mut() = this.item_type().map(|x| CString::new(x).unwrap()); 959 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 960 | }) 961 | } 962 | 963 | /// Set the item name 964 | /// 965 | /// For example, you can set it to "Secret Note" or "todo.txt" 966 | /// 967 | /// @param this_ the object handle 968 | /// @param name the name to be set 969 | #[no_mangle] 970 | pub unsafe extern fn etebase_item_metadata_set_name(this: &mut ItemMetadata, name: *const c_char) { 971 | let name = ptr_to_option(name).map(|x| CStr::from_ptr(x).to_str().unwrap()); 972 | this.set_name(name); 973 | } 974 | 975 | /// The item name 976 | /// 977 | /// @param this_ the object handle 978 | #[no_mangle] 979 | pub unsafe extern fn etebase_item_metadata_get_name(this: &ItemMetadata) -> *const c_char { 980 | thread_local! { 981 | static LAST: RefCell> = RefCell::new(None); 982 | } 983 | LAST.with(|ret| { 984 | *ret.borrow_mut() = this.name().map(|x| CString::new(x).unwrap()); 985 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 986 | }) 987 | } 988 | 989 | /// Set the modification time of the item 990 | /// 991 | /// @param this_ the object handle 992 | /// @param mtime the modification time in milliseconds since epoch 993 | #[no_mangle] 994 | pub unsafe extern fn etebase_item_metadata_set_mtime(this: &mut ItemMetadata, mtime: *const i64) { 995 | let mtime = if mtime.is_null() { 996 | None 997 | } else { 998 | Some(*mtime) 999 | }; 1000 | this.set_mtime(mtime); 1001 | } 1002 | 1003 | /// Modification time of the item 1004 | /// 1005 | /// @param this_ the object handle 1006 | #[no_mangle] 1007 | pub unsafe extern fn etebase_item_metadata_get_mtime(this: &ItemMetadata) -> *const i64 { 1008 | thread_local! { 1009 | static LAST: RefCell> = RefCell::new(None); 1010 | } 1011 | LAST.with(|ret| { 1012 | *ret.borrow_mut() = this.mtime(); 1013 | ret.borrow().as_ref().map(|x| x as *const i64).unwrap_or(std::ptr::null()) 1014 | }) 1015 | } 1016 | 1017 | /// Set a description for the item 1018 | /// 1019 | /// @param this_ the object handle 1020 | /// @param description the description to be set 1021 | #[no_mangle] 1022 | pub unsafe extern fn etebase_item_metadata_set_description(this: &mut ItemMetadata, description: *const c_char) { 1023 | let description = ptr_to_option(description).map(|x| CStr::from_ptr(x).to_str().unwrap()); 1024 | this.set_description(description); 1025 | } 1026 | 1027 | /// The item description 1028 | /// 1029 | /// @param this_ the object handle 1030 | #[no_mangle] 1031 | pub unsafe extern fn etebase_item_metadata_get_description(this: &ItemMetadata) -> *const c_char { 1032 | thread_local! { 1033 | static LAST: RefCell> = RefCell::new(None); 1034 | } 1035 | LAST.with(|ret| { 1036 | *ret.borrow_mut() = this.description().map(|x| CString::new(x).unwrap()); 1037 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 1038 | }) 1039 | } 1040 | 1041 | /// Set a color for the item 1042 | /// 1043 | /// @param this_ the object handle 1044 | /// @param color the color to be set in `#RRGGBB` or `#RRGGBBAA` format 1045 | #[no_mangle] 1046 | pub unsafe extern fn etebase_item_metadata_set_color(this: &mut ItemMetadata, color: *const c_char) { 1047 | let color = ptr_to_option(color).map(|x| CStr::from_ptr(x).to_str().unwrap()); 1048 | this.set_color(color); 1049 | } 1050 | 1051 | /// The item color in `#RRGGBB` or `#RRGGBBAA` format 1052 | /// 1053 | /// @param this_ the object handle 1054 | #[no_mangle] 1055 | pub unsafe extern fn etebase_item_metadata_get_color(this: &ItemMetadata) -> *const c_char { 1056 | thread_local! { 1057 | static LAST: RefCell> = RefCell::new(None); 1058 | } 1059 | LAST.with(|ret| { 1060 | *ret.borrow_mut() = this.color().map(|x| CString::new(x).unwrap()); 1061 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 1062 | }) 1063 | } 1064 | 1065 | /// Destroy the object 1066 | /// 1067 | /// @param this_ the object handle 1068 | #[no_mangle] 1069 | pub unsafe extern fn etebase_item_metadata_destroy(this: *mut ItemMetadata) { 1070 | let this = Box::from_raw(this); 1071 | drop(this); 1072 | } 1073 | 1074 | // } 1075 | 1076 | 1077 | // Class CollectionManager { 1078 | 1079 | /// Fetch a single collection from the server using its UID 1080 | /// 1081 | /// @param this_ the object handle 1082 | /// @param col_uid the UID of the collection to be fetched 1083 | /// @param fetch_options the `EtebaseFetchOptions` to fetch with 1084 | #[no_mangle] 1085 | pub unsafe extern fn etebase_collection_manager_fetch(this: &CollectionManager, col_uid: *const c_char, fetch_options: Option<&FetchOptions>) -> *mut Collection { 1086 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 1087 | let col_uid = CStr::from_ptr(col_uid).to_str().unwrap(); 1088 | Box::into_raw( 1089 | Box::new( 1090 | try_or_null!(this.fetch(col_uid, fetch_options.as_ref())) 1091 | ) 1092 | ) 1093 | } 1094 | 1095 | /// Create a new collection 1096 | /// 1097 | /// Should be destroyed with `etebase_collection_destroy` 1098 | /// 1099 | /// @param this_ the object handle 1100 | /// @param collection_type the type of [Item]s stored in the collection 1101 | /// @param meta the [ItemMetadata] for the collection 1102 | /// @param content the collection's content as a byte array. This is unrelated to the [Item]s in the collection. 1103 | /// @param content_size the content size 1104 | #[no_mangle] 1105 | pub unsafe extern fn etebase_collection_manager_create(this: &CollectionManager, collection_type: *const c_char, meta: &ItemMetadata, content: *const c_void, content_size: usize) -> *mut Collection { 1106 | let collection_type = CStr::from_ptr(collection_type).to_str().unwrap(); 1107 | let content = std::slice::from_raw_parts(content as *const u8, content_size); 1108 | Box::into_raw( 1109 | Box::new( 1110 | try_or_null!(this.create(collection_type, meta, content)) 1111 | ) 1112 | ) 1113 | } 1114 | 1115 | /// Create a new collection using raw metadata 1116 | /// 1117 | /// Unlike `etebase_collection_manager_create`, this receives the metadata as valid `EtebaseItemMetadata`-like struct encoded using `msgpack`. 1118 | /// This can be used to create collections with custom metadata types. 1119 | /// 1120 | /// Should be destroyed with `etebase_collection_destroy` 1121 | /// 1122 | /// @param this_ the object handle 1123 | /// @param collection_type the type of [Item]s stored in the collection 1124 | /// @param meta the metadata for the collection as a byte array 1125 | /// @param meta_size the metadata size 1126 | /// @param content the collection's content as a byte array. This is unrelated to the [Item]s in the collection. 1127 | /// @param content_size the content size 1128 | #[no_mangle] 1129 | pub unsafe extern fn etebase_collection_manager_create_raw(this: &CollectionManager, collection_type: *const c_char, meta: *const c_void, meta_size: usize, content: *const c_void, content_size: usize) -> *mut Collection { 1130 | let collection_type = CStr::from_ptr(collection_type).to_str().unwrap(); 1131 | let meta = std::slice::from_raw_parts(meta as *const u8, meta_size); 1132 | let content = std::slice::from_raw_parts(content as *const u8, content_size); 1133 | Box::into_raw( 1134 | Box::new( 1135 | try_or_null!(this.create_raw(collection_type, meta, content)) 1136 | ) 1137 | ) 1138 | } 1139 | 1140 | /// Return the item manager for the supplied collection 1141 | /// 1142 | /// @param this_ the object handle 1143 | /// @param col the collection for which the item manager is required 1144 | #[no_mangle] 1145 | pub unsafe extern fn etebase_collection_manager_get_item_manager(this: &CollectionManager, col: &Collection) -> *mut ItemManager { 1146 | Box::into_raw( 1147 | Box::new( 1148 | try_or_null!(this.item_manager(col)) 1149 | ) 1150 | ) 1151 | } 1152 | 1153 | /// Fetch all collections of a specific type from the server and return a list response 1154 | /// 1155 | /// @param this_ the object handle 1156 | /// @param collection_type the type of items stored in the collection 1157 | /// @param fetch_options the `EtebaseFetchOptions` to fetch with 1158 | #[no_mangle] 1159 | pub unsafe extern fn etebase_collection_manager_list(this: &CollectionManager, collection_type: *const c_char, fetch_options: Option<&FetchOptions>) -> *mut CollectionListResponse { 1160 | let collection_type = CStr::from_ptr(collection_type).to_str().unwrap(); 1161 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 1162 | Box::into_raw( 1163 | Box::new( 1164 | try_or_null!(this.list(collection_type, fetch_options.as_ref())) 1165 | ) 1166 | ) 1167 | } 1168 | 1169 | /// Fetch all collections of the supplied types from the server and return a list response 1170 | /// 1171 | /// @param this_ the object handle 1172 | /// @param collection_types array of strings denoting the collection types 1173 | /// @param collection_types_size size of the collection_types array 1174 | /// @param fetch_options the `EtebaseFetchOptions` to fetch with 1175 | #[no_mangle] 1176 | pub unsafe extern fn etebase_collection_manager_list_multi(this: &CollectionManager, collection_types: *const *const c_char, collection_types_size: usize, fetch_options: Option<&FetchOptions>) -> *mut CollectionListResponse { 1177 | let collection_types = std::slice::from_raw_parts(collection_types, collection_types_size).into_iter().map(|x| CStr::from_ptr(*x).to_str().unwrap()); 1178 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 1179 | Box::into_raw( 1180 | Box::new( 1181 | try_or_null!(this.list_multi(collection_types, fetch_options.as_ref())) 1182 | ) 1183 | ) 1184 | } 1185 | 1186 | /// Upload a collection 1187 | /// 1188 | /// @param this_ the object handle 1189 | /// @param collection the collection object to be uploaded 1190 | /// @param fetch_options the `EtebaseFetchOptions` to upload with 1191 | #[no_mangle] 1192 | pub unsafe extern fn etebase_collection_manager_upload(this: &CollectionManager, collection: &Collection, fetch_options: Option<&FetchOptions>) -> i32 { 1193 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 1194 | try_or_int!(this.upload(collection, fetch_options.as_ref())); 1195 | 0 1196 | } 1197 | 1198 | /// Upload a collection using a transaction 1199 | /// 1200 | /// This call ensures that the collection hasn't changed since we last fetched it 1201 | /// 1202 | /// @param this_ the object handle 1203 | /// @param collection the collection object to be uploaded 1204 | /// @param fetch_options the `EtebaseFetchOptions` to upload with 1205 | #[no_mangle] 1206 | pub unsafe extern fn etebase_collection_manager_transaction(this: &CollectionManager, collection: &Collection, fetch_options: Option<&FetchOptions>) -> i32 { 1207 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 1208 | try_or_int!(this.transaction(collection, fetch_options.as_ref())); 1209 | 0 1210 | } 1211 | 1212 | /// Load and return a cached collection object from a byte buffer 1213 | /// 1214 | /// @param this_ the object handle 1215 | /// @param cached the byte buffer holding the cached collection obtained using [cache_save] 1216 | /// @param cached_size size of the buffer 1217 | #[no_mangle] 1218 | pub unsafe extern fn etebase_collection_manager_cache_load(this: &CollectionManager, cached: *const c_void, cached_size: usize) -> *mut Collection { 1219 | let cached = std::slice::from_raw_parts(cached as *const u8, cached_size); 1220 | Box::into_raw( 1221 | Box::new( 1222 | try_or_null!(this.cache_load(cached)) 1223 | ) 1224 | ) 1225 | } 1226 | 1227 | /// Save the collection object to a byte buffer for caching 1228 | /// 1229 | /// The collection can later be loaded using `etebase_collection_manager_cache_load` 1230 | /// 1231 | /// @param this_ the object handle 1232 | /// @param collection the collection object to be cached 1233 | /// @param[out] ret_size to hold the size of the returned buffer 1234 | #[no_mangle] 1235 | pub unsafe extern fn etebase_collection_manager_cache_save(this: &CollectionManager, collection: &Collection, ret_size: *mut usize) -> *mut c_void { 1236 | let mut ret = try_or_null!(this.cache_save(collection)); 1237 | if !ret_size.is_null() { 1238 | *ret_size = ret.len(); 1239 | } 1240 | let ret_raw = ret.as_mut_ptr() as *mut c_void; 1241 | std::mem::forget(ret); 1242 | ret_raw 1243 | } 1244 | 1245 | /// Save the collection object and its content to a byte buffer for caching 1246 | /// 1247 | /// The collection can later be loaded using `etebase_collection_manager_cache_load` 1248 | /// 1249 | /// @param this_ the object handle 1250 | /// @param collection the collection object to be cached 1251 | /// @param[out] ret_size to hold the size of the returned buffer 1252 | #[no_mangle] 1253 | pub unsafe extern fn etebase_collection_manager_cache_save_with_content(this: &CollectionManager, collection: &Collection, ret_size: *mut usize) ->*mut c_void { 1254 | let mut ret = try_or_null!(this.cache_save_with_content(collection)); 1255 | if !ret_size.is_null() { 1256 | *ret_size = ret.len(); 1257 | } 1258 | let ret_raw = ret.as_mut_ptr() as *mut c_void; 1259 | std::mem::forget(ret); 1260 | ret_raw 1261 | } 1262 | 1263 | /// Return the collection member manager for the supplied collection 1264 | /// 1265 | /// @param this_ the object handle 1266 | /// @param col the collection for which the manager is required 1267 | #[no_mangle] 1268 | pub unsafe extern fn etebase_collection_manager_get_member_manager(this: &CollectionManager, col: &Collection) -> *mut CollectionMemberManager { 1269 | Box::into_raw( 1270 | Box::new( 1271 | try_or_null!(this.member_manager(col)) 1272 | ) 1273 | ) 1274 | } 1275 | 1276 | /// Destroy the object 1277 | /// 1278 | /// @param this_ the object handle 1279 | #[no_mangle] 1280 | pub unsafe extern fn etebase_collection_manager_destroy(this: *mut CollectionManager) { 1281 | let this = Box::from_raw(this); 1282 | drop(this); 1283 | } 1284 | 1285 | // } 1286 | 1287 | 1288 | // Class ItemManager { 1289 | 1290 | /// Fetch a single item from the server using its UID 1291 | /// 1292 | /// @param this_ the object handle 1293 | /// @param item_uid the UID of the item to be fetched 1294 | /// @param fetch_options the `EtebaseFetchOptions` to fetch with 1295 | #[no_mangle] 1296 | pub unsafe extern fn etebase_item_manager_fetch(this: &ItemManager, item_uid: *const c_char, fetch_options: Option<&FetchOptions>) -> *mut Item { 1297 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 1298 | let item_uid = CStr::from_ptr(item_uid).to_str().unwrap(); 1299 | Box::into_raw( 1300 | Box::new( 1301 | try_or_null!(this.fetch(item_uid, fetch_options.as_ref())) 1302 | ) 1303 | ) 1304 | } 1305 | 1306 | /// Create a new item 1307 | /// 1308 | /// Should be destroyed with `etebase_item_destroy` 1309 | /// 1310 | /// @param this_ the object handle 1311 | /// @param meta the [ItemMetadata] for the item 1312 | /// @param content the item's content as a byte array 1313 | /// @param content_size the content size 1314 | #[no_mangle] 1315 | pub unsafe extern fn etebase_item_manager_create(this: &ItemManager, meta: &ItemMetadata, content: *const c_void, content_size: usize) -> *mut Item { 1316 | let content = std::slice::from_raw_parts(content as *const u8, content_size); 1317 | Box::into_raw( 1318 | Box::new( 1319 | try_or_null!(this.create(meta, content)) 1320 | ) 1321 | ) 1322 | } 1323 | 1324 | /// Create a new item using raw metadata 1325 | /// 1326 | /// Unlike `etebase_item_manager_create`, this receives the metadata as valid `EtebaseItemMetadata`-like struct encoded using `msgpack`. 1327 | /// This can be used to create collections with custom metadata types. 1328 | /// 1329 | /// Should be destroyed with `etebase_item_destroy` 1330 | /// 1331 | /// @param this_ the object handle 1332 | /// @param meta the metadata for the item as a byte array 1333 | /// @param meta_size the metadata size 1334 | /// @param content the item's content as a byte array 1335 | /// @param content_size the content size 1336 | #[no_mangle] 1337 | pub unsafe extern fn etebase_item_manager_create_raw(this: &ItemManager, meta: *const c_void, meta_size: usize, content: *const c_void, content_size: usize) -> *mut Item { 1338 | let meta = std::slice::from_raw_parts(meta as *const u8, meta_size); 1339 | let content = std::slice::from_raw_parts(content as *const u8, content_size); 1340 | Box::into_raw( 1341 | Box::new( 1342 | try_or_null!(this.create_raw(meta, content)) 1343 | ) 1344 | ) 1345 | } 1346 | 1347 | /// Fetch all items of a collection and return a list response 1348 | /// 1349 | /// @param this_ the object handle 1350 | /// @param fetch_options the `EtebaseFetchOptions` to fetch with 1351 | #[no_mangle] 1352 | pub unsafe extern fn etebase_item_manager_list(this: &ItemManager, fetch_options: Option<&FetchOptions>) -> *mut ItemListResponse { 1353 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 1354 | Box::into_raw( 1355 | Box::new( 1356 | try_or_null!(this.list(fetch_options.as_ref())) 1357 | ) 1358 | ) 1359 | } 1360 | 1361 | /// Fetch and return a list response of items with each item as the revision 1362 | /// 1363 | /// @param this_ the object handle 1364 | /// @param item the item for which to fetch the revision history 1365 | /// @param fetch_options the `EtebaseFetchOptions` to fetch with 1366 | #[no_mangle] 1367 | pub unsafe extern fn etebase_item_manager_item_revisions(this: &ItemManager, item: &Item, fetch_options: Option<&FetchOptions>) -> *mut ItemRevisionsListResponse { 1368 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 1369 | Box::into_raw( 1370 | Box::new( 1371 | try_or_null!(this.item_revisions(item, fetch_options.as_ref())) 1372 | ) 1373 | ) 1374 | } 1375 | 1376 | /// Fetch the latest revision of the supplied items from the server and return a list response 1377 | /// 1378 | /// @param this_ the object handle 1379 | /// @param items the list of items to be fetched 1380 | /// @param items_size the number of items 1381 | /// @param fetch_options the `EtebaseFetchOptions` to fetch with 1382 | #[no_mangle] 1383 | pub unsafe extern fn etebase_item_manager_fetch_updates(this: &ItemManager, items: *const &Item, items_size: usize, fetch_options: Option<&FetchOptions>) -> *mut ItemListResponse { 1384 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 1385 | let items = std::slice::from_raw_parts(items, items_size).into_iter().map(|x| *x); 1386 | Box::into_raw( 1387 | Box::new( 1388 | try_or_null!(this.fetch_updates(items, fetch_options.as_ref())) 1389 | ) 1390 | ) 1391 | } 1392 | 1393 | /// Fetch multiple Items using their UID 1394 | /// 1395 | /// See etebase_item_manager_fetch for fetching a single item 1396 | /// 1397 | /// @param this_ the object handle 1398 | /// @param items the list of item uids to be fetched 1399 | /// @param items_size the number of items 1400 | /// @param fetch_options the `EtebaseFetchOptions` to fetch with 1401 | #[no_mangle] 1402 | pub unsafe extern fn etebase_item_manager_fetch_multi(this: &ItemManager, items: *const *const c_char, items_size: usize, fetch_options: Option<&FetchOptions>) -> *mut ItemListResponse { 1403 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 1404 | let items = std::slice::from_raw_parts(items, items_size).into_iter().map(|x| CStr::from_ptr(*x).to_str().unwrap()); 1405 | Box::into_raw( 1406 | Box::new( 1407 | try_or_null!(this.fetch_multi(items, fetch_options.as_ref())) 1408 | ) 1409 | ) 1410 | } 1411 | 1412 | /// Upload the supplied items to the server 1413 | /// 1414 | /// @param this_ the object handle 1415 | /// @param items the list of items to be uploaded 1416 | /// @param items_size the number of items 1417 | /// @param fetch_options the `EtebaseFetchOptions` to upload with 1418 | #[no_mangle] 1419 | pub unsafe extern fn etebase_item_manager_batch(this: &ItemManager, items: *const &Item, items_size: usize, fetch_options: Option<&FetchOptions>) -> i32 { 1420 | etebase_item_manager_batch_deps(this, items, items_size, std::ptr::null(), 0, fetch_options) 1421 | } 1422 | 1423 | /// Upload the supplied items to the server with a list of items as dependencies 1424 | /// 1425 | /// This will fail if the dependencies have changed remotely 1426 | /// 1427 | /// @param this_ the object handle 1428 | /// @param items the list of items to be uploaded 1429 | /// @param items_size the number of items 1430 | /// @param deps the list of items to be treated as dependencies 1431 | /// @param deps_size the number of dependencies 1432 | /// @param fetch_options the `EtebaseFetchOptions` to upload with 1433 | #[no_mangle] 1434 | pub unsafe extern fn etebase_item_manager_batch_deps(this: &ItemManager, items: *const &Item, items_size: usize, deps: *const &Item, deps_size: usize, fetch_options: Option<&FetchOptions>) -> i32 { 1435 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 1436 | let items = std::slice::from_raw_parts(items, items_size).into_iter().map(|x| *x); 1437 | let deps = ptr_to_option(deps); 1438 | if let Some(deps) = deps { 1439 | let deps = std::slice::from_raw_parts(deps, deps_size).into_iter().map(|x| *x); 1440 | try_or_int!(this.batch_deps(items, deps, fetch_options.as_ref())); 1441 | } else { 1442 | try_or_int!(this.batch(items, fetch_options.as_ref())); 1443 | } 1444 | 0 1445 | } 1446 | 1447 | /// Upload items using a transaction 1448 | /// 1449 | /// This call ensures that the items haven't changed since we last fetched them 1450 | /// 1451 | /// @param this_ the object handle 1452 | /// @param items the list of items to be uploaded 1453 | /// @param items_size the number of items 1454 | /// @param fetch_options the `EtebaseFetchOptions` to upload with 1455 | #[no_mangle] 1456 | pub unsafe extern fn etebase_item_manager_transaction(this: &ItemManager, items: *const &Item, items_size: usize, fetch_options: Option<&FetchOptions>) -> i32 { 1457 | etebase_item_manager_transaction_deps(this, items, items_size, std::ptr::null(), 0, fetch_options) 1458 | } 1459 | 1460 | /// Upload items using a transaction with a list of items as dependencies 1461 | /// 1462 | /// @param this_ the object handle 1463 | /// @param items the list of items to be uploaded 1464 | /// @param items_size the number of items 1465 | /// @param deps the list of items to be treated as dependencies 1466 | /// @param deps_size the number of dependencies 1467 | /// @param fetch_options the `EtebaseFetchOptions` to upload with 1468 | #[no_mangle] 1469 | pub unsafe extern fn etebase_item_manager_transaction_deps(this: &ItemManager, items: *const &Item, items_size: usize, deps: *const &Item, deps_size: usize, fetch_options: Option<&FetchOptions>) -> i32 { 1470 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 1471 | let items = std::slice::from_raw_parts(items, items_size).into_iter().map(|x| *x); 1472 | let deps = ptr_to_option(deps); 1473 | if let Some(deps) = deps { 1474 | let deps = std::slice::from_raw_parts(deps, deps_size).into_iter().map(|x| *x); 1475 | try_or_int!(this.transaction_deps(items, deps, fetch_options.as_ref())); 1476 | } else { 1477 | try_or_int!(this.transaction(items, fetch_options.as_ref())); 1478 | } 1479 | 0 1480 | } 1481 | 1482 | /// Load and return a cached item from a byte buffer 1483 | /// 1484 | /// @param this_ the object handle 1485 | /// @param cached the byte buffer holding the cached item obtained using [cache_save] 1486 | /// @param cached_size size of the buffer 1487 | #[no_mangle] 1488 | pub unsafe extern fn etebase_item_manager_cache_load(this: &ItemManager, cached: *const c_void, cached_size: usize) -> *mut Item { 1489 | let cached = std::slice::from_raw_parts(cached as *const u8, cached_size); 1490 | Box::into_raw( 1491 | Box::new( 1492 | try_or_null!(this.cache_load(cached)) 1493 | ) 1494 | ) 1495 | } 1496 | 1497 | /// Save the item object to a byte buffer for caching 1498 | /// 1499 | /// The item can later be loaded using `etebase_item_manager_cache_load` 1500 | /// 1501 | /// @param this_ the object handle 1502 | /// @param item the item object to be cached 1503 | /// @param[out] ret_size to hold the size of the returned buffer 1504 | #[no_mangle] 1505 | pub unsafe extern fn etebase_item_manager_cache_save(this: &ItemManager, item: &Item, ret_size: *mut usize) -> *mut c_void { 1506 | let mut ret = try_or_null!(this.cache_save(item)); 1507 | if !ret_size.is_null() { 1508 | *ret_size = ret.len(); 1509 | } 1510 | let ret_raw = ret.as_mut_ptr() as *mut c_void; 1511 | std::mem::forget(ret); 1512 | ret_raw 1513 | } 1514 | 1515 | /// Save the item object and its content to a byte buffer for caching 1516 | /// 1517 | /// The item can later be loaded using `etebase_item_manager_cache_load` 1518 | /// 1519 | /// @param this_ the object handle 1520 | /// @param item the item object to be cached 1521 | /// @param[out] ret_size to hold the size of the returned buffer 1522 | #[no_mangle] 1523 | pub unsafe extern fn etebase_item_manager_cache_save_with_content(this: &ItemManager, item: &Item, ret_size: *mut usize) ->*mut c_void { 1524 | let mut ret = try_or_null!(this.cache_save_with_content(item)); 1525 | if !ret_size.is_null() { 1526 | *ret_size = ret.len(); 1527 | } 1528 | let ret_raw = ret.as_mut_ptr() as *mut c_void; 1529 | std::mem::forget(ret); 1530 | ret_raw 1531 | } 1532 | 1533 | /// Destroy the object 1534 | /// 1535 | /// @param this_ the object handle 1536 | #[no_mangle] 1537 | pub unsafe extern fn etebase_item_manager_destroy(this: *mut ItemManager) { 1538 | let this = Box::from_raw(this); 1539 | drop(this); 1540 | } 1541 | 1542 | // } 1543 | 1544 | 1545 | // Class Collection { 1546 | 1547 | /// Clone a collection object 1548 | /// 1549 | /// @param this_ the object handle 1550 | #[no_mangle] 1551 | pub unsafe extern fn etebase_collection_clone(this: &Collection) -> *mut Collection { 1552 | Box::into_raw( 1553 | Box::new( 1554 | this.clone() 1555 | ) 1556 | ) 1557 | } 1558 | 1559 | /// Manually verify the integrity of the collection 1560 | /// 1561 | /// This is also done automatically by the API 1562 | /// 1563 | /// @param this_ the object handle 1564 | #[no_mangle] 1565 | pub unsafe extern fn etebase_collection_verify(this: &Collection) -> bool { 1566 | this.verify().unwrap_or(false) 1567 | } 1568 | 1569 | /// Set metadata for the collection object 1570 | /// 1571 | /// @param this_ the object handle 1572 | /// @param meta the metadata object to be set for the collection 1573 | #[no_mangle] 1574 | pub unsafe extern fn etebase_collection_set_meta(this: &mut Collection, meta: &ItemMetadata) -> i32 { 1575 | try_or_int!(this.set_meta(meta)); 1576 | 0 1577 | } 1578 | 1579 | /// Return the metadata of the collection 1580 | /// 1581 | /// @param this_ the object handle 1582 | #[no_mangle] 1583 | pub unsafe extern fn etebase_collection_get_meta(this: &Collection) -> *mut ItemMetadata { 1584 | Box::into_raw( 1585 | Box::new( 1586 | try_or_null!(this.meta()) 1587 | ) 1588 | ) 1589 | } 1590 | 1591 | /// Set metadata for the collection object from a byte array 1592 | /// 1593 | /// @param this_ the object handle 1594 | /// @param meta the metadata for the collection. This needs to be a valid `EtebaseItemMetadata`-like struct encoded using `msgpack`. 1595 | /// @param meta_size the metadata size 1596 | #[no_mangle] 1597 | pub unsafe extern fn etebase_collection_set_meta_raw(this: &mut Collection, meta: *const c_void, meta_size: usize) -> i32 { 1598 | let meta = std::slice::from_raw_parts(meta as *const u8, meta_size); 1599 | try_or_int!(this.set_meta_raw(meta)); 1600 | 0 1601 | } 1602 | 1603 | /// Write the metadata of the collection to a byte array and return its length 1604 | /// 1605 | /// @param this_ the object handle 1606 | /// @param[out] buf the output byte buffer 1607 | /// @param buf_size the maximum number of bytes to be written to buf 1608 | #[no_mangle] 1609 | pub unsafe extern fn etebase_collection_get_meta_raw(this: &Collection, buf: *mut c_void, buf_size: usize) -> isize { 1610 | let ret = try_or_int!(this.meta_raw()); 1611 | let size = std::cmp::min(buf_size, ret.len()); 1612 | buf.copy_from_nonoverlapping(ret.as_ptr() as *const c_void, size); 1613 | size as isize 1614 | } 1615 | 1616 | /// Set the content of the collection 1617 | /// 1618 | /// @param this_ the object handle 1619 | /// @param content the content of the collection as a byte array 1620 | /// @param content_size the content size 1621 | #[no_mangle] 1622 | pub unsafe extern fn etebase_collection_set_content(this: &mut Collection, content: *const c_void, content_size: usize) -> i32 { 1623 | let content = std::slice::from_raw_parts(content as *const u8, content_size); 1624 | try_or_int!(this.set_content(content)); 1625 | 0 1626 | } 1627 | 1628 | /// Write the content of the collection to a byte array and return its length 1629 | /// 1630 | /// @param this_ the object handle 1631 | /// @param[out] buf the output byte buffer 1632 | /// @param buf_size the maximum number of bytes to be written to buf 1633 | #[no_mangle] 1634 | pub unsafe extern fn etebase_collection_get_content(this: &Collection, buf: *mut c_void, buf_size: usize) -> isize { 1635 | let ret = try_or_int!(this.content()); 1636 | let size = std::cmp::min(buf_size, ret.len()); 1637 | buf.copy_from_nonoverlapping(ret.as_ptr() as *const c_void, size); 1638 | ret.len() as isize 1639 | } 1640 | 1641 | /// Mark the collection as deleted 1642 | /// 1643 | /// The collection needs to be \ref uploaded `etebase_collection_manager_upload` for this to take effect 1644 | /// 1645 | /// @param this_ the object handle 1646 | #[no_mangle] 1647 | pub unsafe extern fn etebase_collection_delete(this: &mut Collection) -> i32 { 1648 | try_or_int!(this.delete()); 1649 | 0 1650 | } 1651 | 1652 | /// Check whether the collection is marked as deleted 1653 | /// 1654 | /// @param this_ the object handle 1655 | #[no_mangle] 1656 | pub unsafe extern fn etebase_collection_is_deleted(this: &Collection) -> bool { 1657 | this.is_deleted() 1658 | } 1659 | 1660 | /// The UID of the collection 1661 | /// 1662 | /// @param this_ the object handle 1663 | #[no_mangle] 1664 | pub unsafe extern fn etebase_collection_get_uid(this: &Collection) -> *const c_char { 1665 | thread_local! { 1666 | static LAST: RefCell> = RefCell::new(None); 1667 | } 1668 | LAST.with(|ret| { 1669 | *ret.borrow_mut() = CString::new(this.uid()).ok(); 1670 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 1671 | }) 1672 | } 1673 | 1674 | /// The etag of the collection 1675 | /// 1676 | /// @param this_ the object handle 1677 | #[no_mangle] 1678 | pub unsafe extern fn etebase_collection_get_etag(this: &Collection) -> *const c_char { 1679 | thread_local! { 1680 | static LAST: RefCell> = RefCell::new(None); 1681 | } 1682 | LAST.with(|ret| { 1683 | *ret.borrow_mut() = CString::new(this.etag()).ok(); 1684 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 1685 | }) 1686 | } 1687 | 1688 | /// The sync token for the collection 1689 | /// 1690 | /// The sync token reflects changes to the collection properties or its items on the server 1691 | /// 1692 | /// @param this_ the object handle 1693 | #[no_mangle] 1694 | pub unsafe extern fn etebase_collection_get_stoken(this: &Collection) -> *const c_char { 1695 | thread_local! { 1696 | static LAST: RefCell> = RefCell::new(None); 1697 | } 1698 | LAST.with(|ret| { 1699 | *ret.borrow_mut() = this.stoken().map(|x| CString::new(x).unwrap()); 1700 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 1701 | }) 1702 | } 1703 | 1704 | /// Return the collection as an item 1705 | /// 1706 | /// @param this_ the object handle 1707 | #[no_mangle] 1708 | pub unsafe extern fn etebase_collection_as_item(this: &Collection) -> *mut Item { 1709 | Box::into_raw( 1710 | Box::new( 1711 | try_or_null!(this.item()) 1712 | ) 1713 | ) 1714 | } 1715 | 1716 | /// The type of the collection 1717 | /// 1718 | /// @param this_ the object handle 1719 | #[no_mangle] 1720 | pub unsafe extern fn etebase_collection_get_collection_type(this: &Collection) -> *mut c_char { 1721 | CString::new(try_or_null!(this.collection_type())).unwrap().into_raw() 1722 | } 1723 | 1724 | /// Return the access level of the collection for the current user 1725 | /// 1726 | /// @param this_ the object handle 1727 | #[no_mangle] 1728 | pub unsafe extern fn etebase_collection_get_access_level(this: &Collection) -> CollectionAccessLevel { 1729 | this.access_level() 1730 | } 1731 | 1732 | /// Destroy the object 1733 | /// 1734 | /// @param this_ the object handle 1735 | #[no_mangle] 1736 | pub unsafe extern fn etebase_collection_destroy(this: *mut Collection) { 1737 | let this = Box::from_raw(this); 1738 | drop(this); 1739 | } 1740 | 1741 | // } 1742 | 1743 | 1744 | // Class Item { 1745 | 1746 | /// Clone an item object 1747 | /// 1748 | /// @param this_ the object handle 1749 | #[no_mangle] 1750 | pub unsafe extern fn etebase_item_clone(this: &Item) -> *mut Item { 1751 | Box::into_raw( 1752 | Box::new( 1753 | this.clone() 1754 | ) 1755 | ) 1756 | } 1757 | 1758 | /// Manually verify the integrity of the item 1759 | /// 1760 | /// This is also done automatically by the API 1761 | /// 1762 | /// @param this_ the object handle 1763 | #[no_mangle] 1764 | pub unsafe extern fn etebase_item_verify(this: &Item) -> bool { 1765 | this.verify().unwrap_or(false) 1766 | } 1767 | 1768 | /// Set metadata for the item object 1769 | /// 1770 | /// @param this_ the object handle 1771 | /// @param meta the metadata object to be set for the item 1772 | #[no_mangle] 1773 | pub unsafe extern fn etebase_item_set_meta(this: &mut Item, meta: &ItemMetadata) -> i32 { 1774 | try_or_int!(this.set_meta(meta)); 1775 | 0 1776 | } 1777 | 1778 | /// Return the metadata of the item 1779 | /// 1780 | /// @param this_ the object handle 1781 | #[no_mangle] 1782 | pub unsafe extern fn etebase_item_get_meta(this: &Item) -> *mut ItemMetadata { 1783 | Box::into_raw( 1784 | Box::new( 1785 | try_or_null!(this.meta()) 1786 | ) 1787 | ) 1788 | } 1789 | 1790 | /// Set metadata for the item object from a byte array 1791 | /// 1792 | /// @param this_ the object handle 1793 | /// @param meta the metadata for the item. This needs to be a valid `EtebaseItemMetadata`-like struct encoded using `msgpack`. 1794 | /// @param meta_size the metadata size 1795 | #[no_mangle] 1796 | pub unsafe extern fn etebase_item_set_meta_raw(this: &mut Item, meta: *const c_void, meta_size: usize) -> i32 { 1797 | let meta = std::slice::from_raw_parts(meta as *const u8, meta_size); 1798 | try_or_int!(this.set_meta_raw(meta)); 1799 | 0 1800 | } 1801 | 1802 | /// Write the metadata of the item to a byte array and return its length 1803 | /// 1804 | /// @param this_ the object handle 1805 | /// @param[out] buf the output byte buffer 1806 | /// @param buf_size the maximum number of bytes to be written to buf 1807 | #[no_mangle] 1808 | pub unsafe extern fn etebase_item_get_meta_raw(this: &Item, buf: *mut c_void, buf_size: usize) -> isize { 1809 | let ret = try_or_int!(this.meta_raw()); 1810 | let size = std::cmp::min(buf_size, ret.len()); 1811 | buf.copy_from_nonoverlapping(ret.as_ptr() as *const c_void, size); 1812 | size as isize 1813 | } 1814 | 1815 | 1816 | /// Set the content of the item 1817 | /// 1818 | /// @param this_ the object handle 1819 | /// @param content the content of the item as a byte array 1820 | /// @param content_size the content size 1821 | #[no_mangle] 1822 | pub unsafe extern fn etebase_item_set_content(this: &mut Item, content: *const c_void, content_size: usize) -> i32 { 1823 | let content = std::slice::from_raw_parts(content as *const u8, content_size); 1824 | try_or_int!(this.set_content(content)); 1825 | 0 1826 | } 1827 | 1828 | /// Write the content of the item to a byte array and return its length 1829 | /// 1830 | /// @param this_ the object handle 1831 | /// @param[out] buf the output byte buffer 1832 | /// @param buf_size the maximum number of bytes to be written to buf 1833 | #[no_mangle] 1834 | pub unsafe extern fn etebase_item_get_content(this: &Item, buf: *mut c_void, buf_size: usize) -> isize { 1835 | let ret = try_or_int!(this.content()); 1836 | let size = std::cmp::min(buf_size, ret.len()); 1837 | buf.copy_from_nonoverlapping(ret.as_ptr() as *const c_void, size); 1838 | ret.len() as isize 1839 | } 1840 | 1841 | /// Mark the item as deleted 1842 | /// 1843 | /// The item needs to be \ref uploaded `etebase_item_manager_batch` for this to take effect 1844 | /// 1845 | /// @param this_ the object handle 1846 | #[no_mangle] 1847 | pub unsafe extern fn etebase_item_delete(this: &mut Item) -> i32 { 1848 | try_or_int!(this.delete()); 1849 | 0 1850 | } 1851 | 1852 | /// Check whether the item is marked as deleted 1853 | /// 1854 | /// @param this_ the object handle 1855 | #[no_mangle] 1856 | pub unsafe extern fn etebase_item_is_deleted(this: &Item) -> bool { 1857 | this.is_deleted() 1858 | } 1859 | 1860 | /// The UID of the item 1861 | /// 1862 | /// @param this_ the object handle 1863 | #[no_mangle] 1864 | pub unsafe extern fn etebase_item_get_uid(this: &Item) -> *const c_char { 1865 | thread_local! { 1866 | static LAST: RefCell> = RefCell::new(None); 1867 | } 1868 | LAST.with(|ret| { 1869 | *ret.borrow_mut() = CString::new(this.uid()).ok(); 1870 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 1871 | }) 1872 | } 1873 | 1874 | /// The etag of the item 1875 | /// 1876 | /// @param this_ the object handle 1877 | #[no_mangle] 1878 | pub unsafe extern fn etebase_item_get_etag(this: &Item) -> *const c_char { 1879 | thread_local! { 1880 | static LAST: RefCell> = RefCell::new(None); 1881 | } 1882 | LAST.with(|ret| { 1883 | *ret.borrow_mut() = CString::new(this.etag()).ok(); 1884 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 1885 | }) 1886 | } 1887 | 1888 | /// Destroy the object 1889 | /// 1890 | /// @param this_ the object handle 1891 | #[no_mangle] 1892 | pub unsafe extern fn etebase_item_destroy(this: *mut Item) { 1893 | let this = Box::from_raw(this); 1894 | drop(this); 1895 | } 1896 | 1897 | // } 1898 | 1899 | 1900 | // Class UserProfile { 1901 | 1902 | /// The user's identity public key 1903 | /// 1904 | /// This is used for identifying the user and safely sending them data (such as \ref invitations EtebaseSignedInvitation). 1905 | #[no_mangle] 1906 | pub unsafe extern fn etebase_user_profile_get_pubkey(this: &UserProfile) -> *const c_void { 1907 | this.pubkey().as_ptr() as *const c_void 1908 | } 1909 | 1910 | /// The size of the user's identity public key 1911 | /// 1912 | /// @param this_ the object handle 1913 | #[no_mangle] 1914 | pub unsafe extern fn etebase_user_profile_get_pubkey_size(this: &UserProfile) -> usize { 1915 | this.pubkey().len() 1916 | } 1917 | 1918 | /// Destroy the object 1919 | /// 1920 | /// @param this_ the object handle 1921 | #[no_mangle] 1922 | pub unsafe extern fn etebase_user_profile_destroy(this: *mut UserProfile) { 1923 | let this = Box::from_raw(this); 1924 | drop(this); 1925 | } 1926 | 1927 | // } 1928 | 1929 | 1930 | // class InvitationListResponse { 1931 | 1932 | type InvitationListResponse = etebase::IteratorListResponse; 1933 | 1934 | /// Iterator for the list response 1935 | /// 1936 | /// @param this_ the object handle 1937 | #[no_mangle] 1938 | pub unsafe extern fn etebase_invitation_list_response_get_iterator(this: &InvitationListResponse) -> *const c_char { 1939 | thread_local! { 1940 | static LAST: RefCell> = RefCell::new(None); 1941 | } 1942 | LAST.with(|ret| { 1943 | *ret.borrow_mut() = this.iterator().map(|x| CString::new(x).unwrap()); 1944 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 1945 | }) 1946 | } 1947 | 1948 | /// List of invitations included in the response 1949 | /// 1950 | /// @param this_ the object handle 1951 | /// @param[out] data the array to store the items in 1952 | #[no_mangle] 1953 | pub unsafe extern fn etebase_invitation_list_response_get_data(this: &InvitationListResponse, data: *mut *const SignedInvitation) -> i32 { 1954 | let ret: Vec<&SignedInvitation> = this.data().iter().collect(); 1955 | data.copy_from_nonoverlapping(ret.as_ptr() as *mut *const SignedInvitation, ret.len()); 1956 | 0 1957 | } 1958 | 1959 | /// The number of invitations included in the response 1960 | /// 1961 | /// @param this_ the object handle 1962 | #[no_mangle] 1963 | pub unsafe extern fn etebase_invitation_list_response_get_data_length(this: &InvitationListResponse) -> usize { 1964 | this.data().len() 1965 | } 1966 | 1967 | /// Indicates whether there is no more data to fetch 1968 | /// 1969 | /// @param this_ the object handle 1970 | #[no_mangle] 1971 | pub unsafe extern fn etebase_invitation_list_response_is_done(this: &InvitationListResponse) -> bool { 1972 | this.done() 1973 | } 1974 | 1975 | /// Destroy the object 1976 | /// 1977 | /// @param this_ the object handle 1978 | #[no_mangle] 1979 | pub unsafe extern fn etebase_invitation_list_response_destroy(this: *mut InvitationListResponse) { 1980 | let this = Box::from_raw(this); 1981 | drop(this); 1982 | } 1983 | 1984 | // } 1985 | 1986 | 1987 | // Class InvitationManager { 1988 | 1989 | /// List the incoming collection invitations for the account 1990 | /// 1991 | /// @param this_ the object handle 1992 | /// @param fetch_options the `EtebaseFetchOptions` to fetch with 1993 | #[no_mangle] 1994 | pub unsafe extern fn etebase_invitation_manager_list_incoming(this: &CollectionInvitationManager, fetch_options: Option<&FetchOptions>) -> *mut InvitationListResponse { 1995 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 1996 | Box::into_raw( 1997 | Box::new( 1998 | try_or_null!(this.list_incoming(fetch_options.as_ref())) 1999 | ) 2000 | ) 2001 | } 2002 | 2003 | /// List the outgoing collection invitations for the account 2004 | /// 2005 | /// @param this_ the object handle 2006 | /// @param fetch_options the `EtebaseFetchOptions` to fetch with 2007 | #[no_mangle] 2008 | pub unsafe extern fn etebase_invitation_manager_list_outgoing(this: &CollectionInvitationManager, fetch_options: Option<&FetchOptions>) -> *mut InvitationListResponse { 2009 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 2010 | Box::into_raw( 2011 | Box::new( 2012 | try_or_null!(this.list_outgoing(fetch_options.as_ref())) 2013 | ) 2014 | ) 2015 | } 2016 | 2017 | /// Accept an invitation 2018 | /// 2019 | /// @param this_ the object handle 2020 | /// @param invitation the invitation to accept 2021 | #[no_mangle] 2022 | pub unsafe extern fn etebase_invitation_manager_accept(this: &CollectionInvitationManager, invitation: &SignedInvitation) -> i32 { 2023 | try_or_int!(this.accept(invitation)); 2024 | 0 2025 | } 2026 | 2027 | /// Reject an invitation 2028 | /// 2029 | /// @param this_ the object handle 2030 | /// @param invitation the invitation to reject 2031 | #[no_mangle] 2032 | pub unsafe extern fn etebase_invitation_manager_reject(this: &CollectionInvitationManager, invitation: &SignedInvitation) -> i32 { 2033 | try_or_int!(this.accept(invitation)); 2034 | 0 2035 | } 2036 | 2037 | /// Fetch and return a user's profile 2038 | /// 2039 | /// @param this_ the object handle 2040 | /// @param username the username of the user to fetch 2041 | #[no_mangle] 2042 | pub unsafe extern fn etebase_invitation_manager_fetch_user_profile(this: &CollectionInvitationManager, username: *const c_char) -> *mut UserProfile { 2043 | let username = CStr::from_ptr(username).to_str().unwrap(); 2044 | Box::into_raw( 2045 | Box::new( 2046 | try_or_null!(this.fetch_user_profile(username)) 2047 | ) 2048 | ) 2049 | } 2050 | 2051 | /// Invite a user to a collection 2052 | /// 2053 | /// @param this_ the object handle 2054 | /// @param collection the collection to invite to 2055 | /// @param username the username of the user to invite 2056 | /// @param pubkey the public key of the user to invite 2057 | /// @param pubkey_size the size of the public key 2058 | /// @param access_level the level of access to give to user 2059 | #[no_mangle] 2060 | pub unsafe extern fn etebase_invitation_manager_invite(this: &CollectionInvitationManager, collection: &Collection, username: *const c_char, pubkey: *const c_void, pubkey_size: usize, access_level: CollectionAccessLevel) -> i32 { 2061 | let username = CStr::from_ptr(username).to_str().unwrap(); 2062 | let pubkey = std::slice::from_raw_parts(pubkey as *const u8, pubkey_size); 2063 | try_or_int!(this.invite(collection, username, pubkey, access_level)); 2064 | 0 2065 | } 2066 | 2067 | /// Cancel an invitation (disinvite) 2068 | /// 2069 | /// @param this_ the object handle 2070 | /// @param invitation the invitation to cancel 2071 | #[no_mangle] 2072 | pub unsafe extern fn etebase_invitation_manager_disinvite(this: &CollectionInvitationManager, invitation: &SignedInvitation) -> i32 { 2073 | try_or_int!(this.disinvite(invitation)); 2074 | 0 2075 | } 2076 | 2077 | /// Our identity's public key 2078 | /// 2079 | /// This is the key users see when we send invitations. 2080 | /// Can be pretty printed with `etebase_utils_pretty_fingerprint`. 2081 | /// 2082 | /// @param this_ the object handle 2083 | #[no_mangle] 2084 | pub unsafe extern fn etebase_invitation_manager_get_pubkey(this: &CollectionInvitationManager) -> *const c_void { 2085 | this.pubkey().as_ptr() as *const c_void 2086 | } 2087 | 2088 | /// The size of our identity's public key 2089 | /// 2090 | /// @param this_ the object handle 2091 | #[no_mangle] 2092 | pub unsafe extern fn etebase_invitation_manager_get_pubkey_size(this: &CollectionInvitationManager) -> usize { 2093 | this.pubkey().len() 2094 | } 2095 | 2096 | /// Destroy the object 2097 | /// 2098 | /// @param this_ the object handle 2099 | #[no_mangle] 2100 | pub unsafe extern fn etebase_invitation_manager_destroy(this: *mut CollectionInvitationManager) { 2101 | let this = Box::from_raw(this); 2102 | drop(this); 2103 | } 2104 | 2105 | // } 2106 | 2107 | 2108 | // Class SignedInvitation { 2109 | 2110 | /// Clone the invitation object 2111 | /// 2112 | /// @param this_ the object handle 2113 | #[no_mangle] 2114 | pub unsafe extern fn etebase_signed_invitation_clone(this: &SignedInvitation) -> *mut SignedInvitation { 2115 | Box::into_raw( 2116 | Box::new( 2117 | this.clone() 2118 | ) 2119 | ) 2120 | } 2121 | 2122 | /// The uid of the invitation 2123 | /// 2124 | /// @param this_ the object handle 2125 | #[no_mangle] 2126 | pub unsafe extern fn etebase_signed_invitation_get_uid(this: &SignedInvitation) -> *const c_char { 2127 | thread_local! { 2128 | static LAST: RefCell> = RefCell::new(None); 2129 | } 2130 | LAST.with(|ret| { 2131 | *ret.borrow_mut() = CString::new(this.uid()).ok(); 2132 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 2133 | }) 2134 | } 2135 | 2136 | /// The username this invitation is for 2137 | /// 2138 | /// @param this_ the object handle 2139 | #[no_mangle] 2140 | pub unsafe extern fn etebase_signed_invitation_get_username(this: &SignedInvitation) -> *const c_char { 2141 | thread_local! { 2142 | static LAST: RefCell> = RefCell::new(None); 2143 | } 2144 | LAST.with(|ret| { 2145 | *ret.borrow_mut() = CString::new(this.username()).ok(); 2146 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 2147 | }) 2148 | } 2149 | 2150 | /// The uid of the collection this invitation is for 2151 | /// 2152 | /// @param this_ the object handle 2153 | #[no_mangle] 2154 | pub unsafe extern fn etebase_signed_invitation_get_collection(this: &SignedInvitation) -> *const c_char { 2155 | thread_local! { 2156 | static LAST: RefCell> = RefCell::new(None); 2157 | } 2158 | LAST.with(|ret| { 2159 | *ret.borrow_mut() = CString::new(this.collection()).ok(); 2160 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 2161 | }) 2162 | } 2163 | 2164 | /// The access level offered in this invitation 2165 | /// 2166 | /// @param this_ the object handle 2167 | #[no_mangle] 2168 | pub unsafe extern fn etebase_signed_invitation_get_access_level(this: &SignedInvitation) -> CollectionAccessLevel { 2169 | this.access_level() 2170 | } 2171 | 2172 | /// The username this invitation is from 2173 | /// 2174 | /// @param this_ the object handle 2175 | #[no_mangle] 2176 | pub unsafe extern fn etebase_signed_invitation_get_from_username(this: &SignedInvitation) -> *const c_void { 2177 | this.from_username().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) as *const c_void 2178 | } 2179 | 2180 | /// The public key of the inviting user 2181 | /// 2182 | /// @param this_ the object handle 2183 | #[no_mangle] 2184 | pub unsafe extern fn etebase_signed_invitation_get_from_pubkey(this: &SignedInvitation) -> *const c_void { 2185 | this.from_pubkey().as_ptr() as *const c_void 2186 | } 2187 | 2188 | /// The size of the public key of the inviting user 2189 | /// 2190 | /// @param this_ the object handle 2191 | #[no_mangle] 2192 | pub unsafe extern fn etebase_signed_invitation_get_from_pubkey_size(this: &SignedInvitation) -> usize { 2193 | this.from_pubkey().len() 2194 | } 2195 | 2196 | /// Destroy the object 2197 | /// 2198 | /// @param this_ the object handle 2199 | #[no_mangle] 2200 | pub unsafe extern fn etebase_signed_invitation_destroy(this: *mut SignedInvitation) { 2201 | let this = Box::from_raw(this); 2202 | drop(this); 2203 | } 2204 | 2205 | // } 2206 | 2207 | 2208 | // Class CollectionMember { 2209 | 2210 | /// Clone the object 2211 | /// 2212 | /// @param this_ the object handle 2213 | #[no_mangle] 2214 | pub unsafe extern fn etebase_collection_member_clone(this: &CollectionMember) -> *mut CollectionMember { 2215 | Box::into_raw( 2216 | Box::new( 2217 | this.clone() 2218 | ) 2219 | ) 2220 | } 2221 | 2222 | /// The username of a member 2223 | /// 2224 | /// @param this_ the object handle 2225 | #[no_mangle] 2226 | pub unsafe extern fn etebase_collection_member_get_username(this: &CollectionMember) -> *const c_char { 2227 | thread_local! { 2228 | static LAST: RefCell> = RefCell::new(None); 2229 | } 2230 | LAST.with(|ret| { 2231 | *ret.borrow_mut() = CString::new(this.username()).ok(); 2232 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 2233 | }) 2234 | } 2235 | 2236 | /// The access_level of the member 2237 | /// 2238 | /// @param this_ the object handle 2239 | #[no_mangle] 2240 | pub unsafe extern fn etebase_collection_member_get_access_level(this: &CollectionMember) -> CollectionAccessLevel { 2241 | this.access_level() 2242 | } 2243 | 2244 | /// Destroy the object 2245 | /// 2246 | /// @param this_ the object handle 2247 | #[no_mangle] 2248 | pub unsafe extern fn etebase_collection_member_destroy(this: *mut CollectionMember) { 2249 | let this = Box::from_raw(this); 2250 | drop(this); 2251 | } 2252 | 2253 | // } 2254 | 2255 | 2256 | // Class MemberListResponse { 2257 | 2258 | type MemberListResponse = etebase::IteratorListResponse; 2259 | 2260 | /// Iterator for the list response 2261 | /// 2262 | /// @param this_ the object handle 2263 | #[no_mangle] 2264 | pub unsafe extern fn etebase_member_list_response_get_iterator(this: &MemberListResponse) -> *const c_char { 2265 | thread_local! { 2266 | static LAST: RefCell> = RefCell::new(None); 2267 | } 2268 | LAST.with(|ret| { 2269 | *ret.borrow_mut() = this.iterator().map(|x| CString::new(x).unwrap()); 2270 | ret.borrow().as_ref().map(|x| x.as_ptr()).unwrap_or(std::ptr::null()) 2271 | }) 2272 | } 2273 | 2274 | /// List of collection members included in the response 2275 | /// 2276 | /// @param this_ the object handle 2277 | /// @param[out] data the array to store the collection members in 2278 | #[no_mangle] 2279 | pub unsafe extern fn etebase_member_list_response_get_data(this: &MemberListResponse, data: *mut *const CollectionMember) -> i32 { 2280 | let ret: Vec<&CollectionMember> = this.data().iter().collect(); 2281 | data.copy_from_nonoverlapping(ret.as_ptr() as *mut *const CollectionMember, ret.len()); 2282 | 0 2283 | } 2284 | 2285 | /// The number of collection members included in the response 2286 | /// 2287 | /// @param this_ the object handle 2288 | #[no_mangle] 2289 | pub unsafe extern fn etebase_member_list_response_get_data_length(this: &MemberListResponse) -> usize { 2290 | this.data().len() 2291 | } 2292 | 2293 | /// Indicates whether there is no more data to fetch 2294 | /// 2295 | /// @param this_ the object handle 2296 | #[no_mangle] 2297 | pub unsafe extern fn etebase_member_list_response_is_done(this: &MemberListResponse) -> bool { 2298 | this.done() 2299 | } 2300 | 2301 | /// Destroy the object 2302 | /// 2303 | /// @param this_ the object handle 2304 | #[no_mangle] 2305 | pub unsafe extern fn etebase_member_list_response_destroy(this: *mut MemberListResponse) { 2306 | let this = Box::from_raw(this); 2307 | drop(this); 2308 | } 2309 | 2310 | // } 2311 | 2312 | 2313 | // Class CollectionMemberManager { 2314 | 2315 | /// List the members of a collection 2316 | /// 2317 | /// @param this_ the object handle 2318 | /// @param fetch_options the `EtebaseFetchOptions` to fetch with 2319 | #[no_mangle] 2320 | pub unsafe extern fn etebase_collection_member_manager_list(this: &CollectionMemberManager, fetch_options: Option<&FetchOptions>) -> *mut MemberListResponse { 2321 | let fetch_options = fetch_options.map(|x| x.to_fetch_options()); 2322 | Box::into_raw( 2323 | Box::new( 2324 | try_or_null!(this.list(fetch_options.as_ref())) 2325 | ) 2326 | ) 2327 | } 2328 | 2329 | /// Remove a member from the collection 2330 | /// 2331 | /// @param this_ the object handle 2332 | /// @param username the member's username 2333 | #[no_mangle] 2334 | pub unsafe extern fn etebase_collection_member_manager_remove(this: &CollectionMemberManager, username: *const c_char) -> i32 { 2335 | let username = CStr::from_ptr(username).to_str().unwrap(); 2336 | try_or_int!(this.remove(username)); 2337 | 0 2338 | } 2339 | 2340 | /// Leave a collection the user is a member of 2341 | /// 2342 | /// @param this_ the object handle 2343 | #[no_mangle] 2344 | pub unsafe extern fn etebase_collection_member_manager_leave(this: &CollectionMemberManager) -> i32 { 2345 | try_or_int!(this.leave()); 2346 | 0 2347 | } 2348 | 2349 | /// Modify the access level of a member 2350 | /// 2351 | /// @param this_ the object handle 2352 | /// @param username the member's username 2353 | /// @param access_level the new `EtebaseCollectionAccessLevel` 2354 | #[no_mangle] 2355 | pub unsafe extern fn etebase_collection_member_manager_modify_access_level(this: &CollectionMemberManager, username: *const c_char, access_level: CollectionAccessLevel) -> i32 { 2356 | let username = CStr::from_ptr(username).to_str().unwrap(); 2357 | try_or_int!(this.modify_access_level(username, access_level)); 2358 | 0 2359 | } 2360 | 2361 | /// Destroy the object 2362 | /// 2363 | /// @param this_ the object handle 2364 | #[no_mangle] 2365 | pub unsafe extern fn etebase_collection_member_manager_destroy(this: *mut CollectionMemberManager) { 2366 | let this = Box::from_raw(this); 2367 | drop(this); 2368 | } 2369 | 2370 | // } 2371 | 2372 | 2373 | // Class FileSystemCache { 2374 | 2375 | /// Initialize a file system cache object 2376 | /// 2377 | /// Should be destroyed with `etebase_fs_cache_destroy` 2378 | /// 2379 | /// @param path the path to a directory to store cache in 2380 | /// @param username username of the user to cache data for 2381 | #[no_mangle] 2382 | pub unsafe extern fn etebase_fs_cache_new(path: *const c_char, username: *const c_char) -> *mut FileSystemCache { 2383 | let path = PathBuf::from(CStr::from_ptr(path).to_str().unwrap()); 2384 | let username = CStr::from_ptr(username).to_str().unwrap(); 2385 | Box::into_raw( 2386 | Box::new( 2387 | try_or_null!(FileSystemCache::new(path.as_path(), username)) 2388 | ) 2389 | ) 2390 | } 2391 | 2392 | /// Clear all cache for the user 2393 | /// 2394 | /// @param this_ the object handle 2395 | #[no_mangle] 2396 | pub unsafe extern fn etebase_fs_cache_clear_user(this: &FileSystemCache) -> i32 { 2397 | try_or_int!(this.clear_user_cache()); 2398 | 0 2399 | } 2400 | 2401 | /// Save the user account 2402 | /// 2403 | /// Load it later using `etebase_fs_cache_load_account` 2404 | /// 2405 | /// @param this_ the object handle 2406 | /// @param etebase the account to save 2407 | /// @param encryption_key used to encrypt the saved account string to enhance security 2408 | /// @param encryption_key_size the size of the encryption_key 2409 | #[no_mangle] 2410 | pub unsafe extern fn etebase_fs_cache_save_account(this: &FileSystemCache, etebase: &Account, encryption_key: *const c_void, encryption_key_size: usize) -> i32 { 2411 | let encryption_key = if encryption_key.is_null() { 2412 | None 2413 | } else { 2414 | Some(std::slice::from_raw_parts(encryption_key as *const u8, encryption_key_size)) 2415 | }; 2416 | try_or_int!(this.save_account(etebase, encryption_key)); 2417 | 0 2418 | } 2419 | 2420 | /// Load the account object from cache 2421 | /// 2422 | /// @param this_ the object handle 2423 | /// @param client the already setup [Client] object 2424 | /// @param encryption_key the same encryption key passed to [Self::save_account] while saving the account 2425 | /// @param encryption_key_size the size of the encryption_key 2426 | #[no_mangle] 2427 | pub unsafe extern fn etebase_fs_cache_load_account(this: &FileSystemCache, client: &Client, encryption_key: *const c_void, encryption_key_size: usize) -> *mut Account { 2428 | let encryption_key = if encryption_key.is_null() { 2429 | None 2430 | } else { 2431 | Some(std::slice::from_raw_parts(encryption_key as *const u8, encryption_key_size)) 2432 | }; 2433 | Box::into_raw( 2434 | Box::new( 2435 | try_or_null!(this.load_account(client, encryption_key)) 2436 | ) 2437 | ) 2438 | } 2439 | 2440 | /// Save the collection list sync token 2441 | /// 2442 | /// @param this_ the object handle 2443 | /// @param stoken the sync token to be saved 2444 | #[no_mangle] 2445 | pub unsafe extern fn etebase_fs_cache_save_stoken(this: &FileSystemCache, stoken: *const c_char) -> i32 { 2446 | let stoken = CStr::from_ptr(stoken).to_str().unwrap(); 2447 | try_or_int!(this.save_stoken(stoken)); 2448 | 0 2449 | } 2450 | 2451 | /// Load the collection list sync token from cache 2452 | /// 2453 | /// @param this_ the object handle 2454 | #[no_mangle] 2455 | pub unsafe extern fn etebase_fs_cache_load_stoken(this: &FileSystemCache) -> *mut c_char { 2456 | let stoken = try_or_null!(this.load_stoken()); 2457 | match stoken { 2458 | Some(stoken) => try_or_null!(CString::new(stoken)).into_raw(), 2459 | None => std::ptr::null_mut() 2460 | } 2461 | } 2462 | 2463 | /// Save a collection's sync token 2464 | /// 2465 | /// @param this_ the object handle 2466 | /// @param col_uid the UID of the collection 2467 | /// @param stoken the sync token to be saved 2468 | #[no_mangle] 2469 | pub unsafe extern fn etebase_fs_cache_collection_save_stoken(this: &FileSystemCache, col_uid: *const c_char, stoken: *const c_char) -> i32 { 2470 | let col_uid = CStr::from_ptr(col_uid).to_str().unwrap(); 2471 | let stoken = CStr::from_ptr(stoken).to_str().unwrap(); 2472 | try_or_int!(this.collection_save_stoken(col_uid, stoken)); 2473 | 0 2474 | } 2475 | 2476 | /// Load the sync token for a collection 2477 | /// 2478 | /// @param this_ the object handle 2479 | /// @param col_uid the UID of the collection 2480 | #[no_mangle] 2481 | pub unsafe extern fn etebase_fs_cache_collection_load_stoken(this: &FileSystemCache, col_uid: *const c_char) -> *mut c_char { 2482 | let col_uid = CStr::from_ptr(col_uid).to_str().unwrap(); 2483 | let stoken = try_or_null!(this.collection_load_stoken(col_uid)); 2484 | match stoken { 2485 | Some(stoken) => try_or_null!(CString::new(stoken)).into_raw(), 2486 | None => std::ptr::null_mut() 2487 | } 2488 | } 2489 | 2490 | /// Save a collection to cache 2491 | /// 2492 | /// @param this_ the object handle 2493 | /// @param col_mgr collection manager for the account 2494 | /// @param col the collection to be saved 2495 | #[no_mangle] 2496 | pub unsafe extern fn etebase_fs_cache_collection_set(this: &FileSystemCache, col_mgr: &CollectionManager, col: &Collection) -> i32 { 2497 | try_or_int!(this.collection_set(col_mgr, col)); 2498 | 0 2499 | } 2500 | 2501 | /// Remove a collection from cache 2502 | /// 2503 | /// @param this_ the object handle 2504 | /// @param col_mgr collection manager for the account 2505 | /// @param col_uid the UID of the collection to remove 2506 | #[no_mangle] 2507 | pub unsafe extern fn etebase_fs_cache_collection_unset(this: &FileSystemCache, col_mgr: &CollectionManager, col_uid: *const c_char) -> i32 { 2508 | let col_uid = CStr::from_ptr(col_uid).to_str().unwrap(); 2509 | try_or_int!(this.collection_unset(col_mgr, col_uid)); 2510 | 0 2511 | } 2512 | 2513 | /// Load a collection from cache 2514 | /// 2515 | /// @param this_ the object handle 2516 | /// @param col_mgr collection manager for the account 2517 | /// @param col_uid the UID of the collection 2518 | #[no_mangle] 2519 | pub unsafe extern fn etebase_fs_cache_collection_get(this: &FileSystemCache, col_mgr: &CollectionManager, col_uid: *const c_char) -> *mut Collection { 2520 | let col_uid = CStr::from_ptr(col_uid).to_str().unwrap(); 2521 | Box::into_raw( 2522 | Box::new( 2523 | try_or_null!(this.collection(col_mgr, col_uid)) 2524 | ) 2525 | ) 2526 | } 2527 | 2528 | /// Save an item to cache 2529 | /// 2530 | /// @param this_ the object handle 2531 | /// @param item_mgr item manager for the parent collection 2532 | /// @param col_uid the UID of the parent collection 2533 | /// @param item the item to be saved 2534 | #[no_mangle] 2535 | pub unsafe extern fn etebase_fs_cache_item_set(this: &FileSystemCache, item_mgr: &ItemManager, col_uid: *const c_char, item: &Item) -> i32 { 2536 | let col_uid = CStr::from_ptr(col_uid).to_str().unwrap(); 2537 | try_or_int!(this.item_set(item_mgr, col_uid, item)); 2538 | 0 2539 | } 2540 | 2541 | /// Remove an item from cache 2542 | /// 2543 | /// @param this_ the object handle 2544 | /// @param item_mgr item manager for the parent collection 2545 | /// @param col_uid the UID of the parent collection 2546 | /// @param item_uid the UID of the item 2547 | #[no_mangle] 2548 | pub unsafe extern fn etebase_fs_cache_item_unset(this: &FileSystemCache, item_mgr: &ItemManager, col_uid: *const c_char, item_uid: *const c_char) -> i32 { 2549 | let col_uid = CStr::from_ptr(col_uid).to_str().unwrap(); 2550 | let item_uid = CStr::from_ptr(item_uid).to_str().unwrap(); 2551 | try_or_int!(this.item_unset(item_mgr, col_uid, item_uid)); 2552 | 0 2553 | } 2554 | 2555 | /// Load an item from cache 2556 | /// 2557 | /// @param this_ the object handle 2558 | /// @param item_mgr item manager for the parent collection 2559 | /// @param col_uid the UID of the parent collection 2560 | /// @param item_uid the UID of the item 2561 | #[no_mangle] 2562 | pub unsafe extern fn etebase_fs_cache_item_get(this: &FileSystemCache, item_mgr: &ItemManager, col_uid: *const c_char, item_uid: *const c_char) -> *mut Item { 2563 | let col_uid = CStr::from_ptr(col_uid).to_str().unwrap(); 2564 | let item_uid = CStr::from_ptr(item_uid).to_str().unwrap(); 2565 | Box::into_raw( 2566 | Box::new( 2567 | try_or_null!(this.item(item_mgr, col_uid, item_uid)) 2568 | ) 2569 | ) 2570 | } 2571 | 2572 | /// Destroy the object 2573 | /// 2574 | /// @param this_ the object handle 2575 | #[no_mangle] 2576 | pub unsafe extern fn etebase_fs_cache_destroy(this: *mut FileSystemCache) { 2577 | let this = Box::from_raw(this); 2578 | drop(this); 2579 | } 2580 | 2581 | // } 2582 | --------------------------------------------------------------------------------