Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add recipe for sqlite3mc #21354

Merged
merged 10 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions recipes/sqlite3mc/all/conandata.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
sources:
"1.8.0":
url: "https://github.com/utelle/SQLite3MultipleCiphers/archive/refs/tags/v1.8.0.tar.gz"
sha256: "13D9B939BEF7C7371D58A3874F83B18CF330EB2171205B3680ACDDB2215BE0E5"
225 changes: 225 additions & 0 deletions recipes/sqlite3mc/all/conanfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import os
from conan import ConanFile
from conan.tools.cmake import cmake_layout, CMakeDeps, CMakeToolchain, CMake
from conan.tools.files import get, copy

required_conan_version = ">=1.53.0"


class sqlite3mc(ConanFile):
name = "sqlite3mc"
package_type = "library"

license = "MIT"
homepage = "https://github.com/utelle/SQLite3MultipleCiphers"
url = "https://github.com/conan-io/conan-center-index"
description = "The project SQLite3 Multiple Ciphers implements an encryption extension for SQLite with support for multiple ciphers."
topics = ("sqlite", "sqlite3", "sqlite3-encryption", "database-encryption", "sqlite3-extension")

settings = "os", "compiler", "build_type", "arch"
options = {
"shared": [True, False],
"fPIC": [True, False],
"require_zlib": [True, False],
"static_runtime_link": [True, False],
"build_shell": [True, False],
"with_icu": [True, False],
"enable_debug": [True, False],
"soundex": [True, False],
"enable_column_metadata": [True, False],
"secure_delete": [True, False],
"enable_fts3": [True, False],
"enable_fts3_paranthesis": [True, False],
"enable_fts4": [True, False],
"enable_fts5": [True, False],
"enable_carray": [True, False],
"enable_csv": [True, False],
"enable_extfunc": [True, False],
"enable_geopoly": [True, False],
"enable_json1": [True, False],
"enable_rtree": [True, False],
"enable_uuid": [True, False],
"use_uri": [True, False],
"user_authentication": [True, False],
"enable_preupdate_hook": [True, False],
"enable_session": [True, False],
"shell_is_utf8": [True, False],
"enable_fileio": [True, False],
"enable_regexp": [True, False],
"enable_series": [True, False],
"enable_sha3": [True, False],
"enable_explain_comments": [True, False],
"enable_dbpage_vtab": [True, False],
"enable_dbstat_vtab": [True, False],
"enable_stmtvtab": [True, False],
"enable_unknown_sql_function": [True, False],
"use_miniz": [True, False],
"enable_compress": [True, False],
"enable_sqlar": [True, False],
"enable_zipfile": [True, False],
"use_sqleet_legacy": [True, False],
"use_sqlcipher_legacy": [True, False],
"secure_memory": [True, False],
"use_random_fill_memory": [True, False],
"omit_aes_hardware_support": [True, False]
}
default_options = {
"shared": False,
"fPIC": True,
"require_zlib": False,
"static_runtime_link": False,
"build_shell": False,
"with_icu": False,
"enable_debug": False,
"soundex": True,
"enable_column_metadata": True,
"secure_delete": True,
"enable_fts3": True,
"enable_fts3_paranthesis": True,
"enable_fts4": True,
"enable_fts5": True,
"enable_carray": True,
"enable_csv": True,
"enable_extfunc": True,
"enable_geopoly": True,
"enable_json1": True,
"enable_rtree": True,
"enable_uuid": True,
"use_uri": True,
"user_authentication": True,
"enable_preupdate_hook": False,
"enable_session": False,
"shell_is_utf8": True,
"enable_fileio": True,
"enable_regexp": True,
"enable_series": True,
"enable_sha3": True,
"enable_explain_comments": True,
"enable_dbpage_vtab": True,
"enable_dbstat_vtab": True,
"enable_stmtvtab": True,
"enable_unknown_sql_function": True,
"use_miniz": False,
"enable_compress": False,
"enable_sqlar": False,
"enable_zipfile": False,
"use_sqleet_legacy": False,
"use_sqlcipher_legacy": False,
"secure_memory": False,
"use_random_fill_memory": False,
"omit_aes_hardware_support": False
}

def build_requirements(self):
self.tool_requires("cmake/[>=3.24 <4]")

def config_options(self):
if self.settings.os == "Windows":
del self.options.fPIC

def configure(self):
if self.options.shared:
self.options.rm_safe("fPIC")
self.settings.rm_safe("compiler.cppstd")
self.settings.rm_safe("compiler.libcxx")

def source(self):
get(self, **self.conan_data["sources"][self.version], strip_root=True)

def layout(self):
cmake_layout(self, src_folder="src")

def requirements(self):
if self.options.require_zlib:
self.requires("zlib/[>=1.2.11 <2]")
if self.options.with_icu:
self.requires("icu/74.1")

def generate(self):
deps = CMakeDeps(self)
deps.generate()

tc = CMakeToolchain(self)
tc.variables["_SQLITE3MC_REQUIRE_ZLIB"] = self.options.require_zlib
tc.variables["SQLITE3MC_STATIC_RUNTIME_LINK"] = self.options.static_runtime_link
tc.variables["SQLITE3MC_STATIC"] = not self.options.shared
tc.variables["SQLITE3MC_BUILD_SHELL"] = self.options.build_shell
tc.variables["SQLITE3MC_WITH_ICU"] = self.options.with_icu

tc.variables["SQLITE_ENABLE_DEBUG"] = self.options.enable_debug
tc.variables["SQLITE_SOUNDEX"] = self.options.soundex
tc.variables["SQLITE_ENABLE_COLUMN_METADATA"] = self.options.enable_column_metadata
tc.variables["SQLITE_SECURE_DELETE"] = self.options.secure_delete
tc.variables["SQLITE_ENABLE_FTS3"] = self.options.enable_fts3
tc.variables["SQLITE_ENABLE_FTS3_PARENTHESIS"] = self.options.enable_fts3_paranthesis
tc.variables["SQLITE_ENABLE_FTS4"] = self.options.enable_fts4
tc.variables["SQLITE_ENABLE_FTS5"] = self.options.enable_fts5

tc.variables["SQLITE_ENABLE_CARRAY"] = self.options.enable_carray
tc.variables["SQLITE_ENABLE_CSV"] = self.options.enable_csv
tc.variables["SQLITE_ENABLE_EXTFUNC"] = self.options.enable_extfunc
tc.variables["SQLITE_ENABLE_GEOPOLY"] = self.options.enable_geopoly
tc.variables["SQLITE_ENABLE_JSON1"] = self.options.enable_json1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to find an usage for this option, but I failed to find it. I see it in the CMakeLists, but I don't see it in the code, I mean, a required definition like ifdef:

https://github.com/search?q=repo%3Autelle%2FSQLite3MultipleCiphers%20SQLITE_ENABLE_JSON1&type=code

Could you please explain a bit more how does it work, same to other options?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to find an usage for this option, but I failed to find it. I see it in the CMakeLists, but I don't see it in the code, I mean, a required definition like ifdef:

https://github.com/search?q=repo%3Autelle%2FSQLite3MultipleCiphers%20SQLITE_ENABLE_JSON1&type=code

Could you please explain a bit more how does it work, same to other options?

Several SQLite extensions can be included optionally at compile time using #ifdef statements. Mostly this happens in the main C file sqlite3mc.c (like for SQLITE_ENABLE_CSV and SQLITE_ENABLE_EXTFUNC).

But there are also extension that are already included in the original (resp patched) SQLite source amalgamation C file sqlite3patched.c (like for SQLITE_ENABLE_GEOPOLY).

The symbol SQLITE_ENABLE_JSON1 is different. This symbol does indeed not exist any more. In prior SQLite versions it was available, but in recent versions the JSON extension is included per default, but it could be explicitly excluded by specifying the symbol SQLITE_OMIT_JSON.

For the original project SQLite3 Multiple Ciphers I decided to expose many common SQLite configuration options to give users a high degree of freedom to choose the options and extensions most appropriate for their project. Per default many extensions are enabled, but in some projects there exist memory restrictions, so that users want to be able to disable unneeded extensions.

tc.variables["SQLITE_ENABLE_RTREE"] = self.options.enable_rtree
tc.variables["SQLITE_ENABLE_UUID"] = self.options.enable_uuid
tc.variables["SQLITE_USE_URI"] = self.options.use_uri
tc.variables["SQLITE_USER_AUTHENTICATION"] = self.options.user_authentication
tc.variables["SQLITE_ENABLE_PREUPDATE_HOOK"] = self.options.enable_preupdate_hook
tc.variables["SQLITE_ENABLE_SESSION"] = self.options.enable_session
tc.variables["SQLITE_SHELL_IS_UTF8"] = self.options.shell_is_utf8

# Options for library only
tc.variables["SQLITE_ENABLE_FILEIO"] = self.options.enable_fileio
tc.variables["SQLITE_ENABLE_REGEXP"] = self.options.enable_regexp
tc.variables["SQLITE_ENABLE_SERIES"] = self.options.enable_series
tc.variables["SQLITE_ENABLE_SHA3"] = self.options.enable_sha3

# Options for shell only (compatibility with official SQLite shell)
tc.variables["SQLITE_ENABLE_EXPLAIN_COMMENTS"] = self.options.enable_explain_comments
tc.variables["SQLITE_ENABLE_DBPAGE_VTAB"] = self.options.enable_dbpage_vtab
tc.variables["SQLITE_ENABLE_DBSTAT_VTAB"] = self.options.enable_dbstat_vtab
tc.variables["SQLITE_ENABLE_STMTVTAB"] = self.options.enable_stmtvtab
tc.variables["SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION"] = self.options.enable_unknown_sql_function

# Embedded Compression
tc.variables["SQLITE3MC_USE_MINIZ"] = self.options.use_miniz

# Compression/Options that require ZLIB
tc.variables["SQLITE_ENABLE_COMPRESS"] = self.options.enable_compress
tc.variables["SQLITE_ENABLE_SQLAR"] = self.options.enable_sqlar
tc.variables["SQLITE_ENABLE_ZIPFILE"] = self.options.enable_zipfile

# Legacy Encryption Extensions
tc.variables["SQLITE3MC_USE_SQLEET_LEGACY"] = self.options.use_sqleet_legacy
tc.variables["SQLITE3MC_USE_SQLCIPHER_LEGACY"] = self.options.use_sqlcipher_legacy

# Additional memory security (filling freed memory allocations with zeros or random data)
tc.variables["SQLITE3MC_SECURE_MEMORY"] = self.options.secure_memory
tc.variables["SQLITE3MC_USE_RANDOM_FILL_MEMORY"] = self.options.use_random_fill_memory

# Omit AES hardware support
tc.variables["SQLITE3MC_OMIT_AES_HARDWARE_SUPPORT"] = self.options.omit_aes_hardware_support

tc.generate()

def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()

def package(self):
cmake = CMake(self)
cmake.install()
copy(self, "LICENSE*", src=self.source_folder, dst=os.path.join(self.package_folder, "licenses"), keep_path=False)

def package_info(self):
if self.options.shared:
self.cpp_info.libs = ["sqlite3mc"]
else:
self.cpp_info.libs = ["sqlite3mc_static"]
if self.settings.os in ("Linux", "Macos"):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if self.settings.os in ("Linux", "Macos"):
if self.settings.os in ("Linux", "FreeBSD"):

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please explain, why "Macos" should be replaced by "FreeBSD"? IMHO this can't be correct, because

  1. the libraries pthread, dl, and m are required for Macos, too, and
  2. in line 224 self.settings.os is explicitly checked for "Macos" again, because an additional attribute is required under Macos.

I could understand, if you would want the following change:

 if self.settings.os in ("Linux", "FreeBSD", "Macos"):

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that these are indeed linked against by the project on macOS: https://github.com/utelle/SQLite3MultipleCiphers/blob/7e8ecba9a82ea1336b118a21ccb733f4ed292efa/CMakeLists.txt#L262-L266

Excuse the dumb question (I'm not that familiar with the platform): do m and dl even exist on macOS? It's the first time I'm seeing these being linked against outside of Linux/FreeBSD. As far as I know, these are only provided by glibc and similar libc implementations, which is not available on macOS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that these are indeed linked against by the project on macOS: https://github.com/utelle/SQLite3MultipleCiphers/blob/7e8ecba9a82ea1336b118a21ccb733f4ed292efa/CMakeLists.txt#L262-L266

Excuse the dumb question (I'm not that familiar with the platform): do m and dl even exist on macOS? It's the first time I'm seeing these being linked against outside of Linux/FreeBSD. As far as I know, these are only provided by glibc and similar libc implementations, which is not available on macOS.

Actually, I'm not very familiar with macOS either. However, the GitHub CI action (for example this run) runs without errors on macOS, and up to now I haven't received any complaints from users of my component.

self.cpp_info.system_libs.append("pthread")
self.cpp_info.system_libs.append("dl")
self.cpp_info.system_libs.append("m")
if self.settings.os == "Macos":
self.cpp_info.frameworks.append("Security")
12 changes: 12 additions & 0 deletions recipes/sqlite3mc/all/test_package/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.15)
project(test_sqlite3mc C)

find_package(sqlite3mc REQUIRED)

add_executable(${PROJECT_NAME}
main.c
)

target_link_libraries(${PROJECT_NAME}
sqlite3mc::sqlite3mc
)
25 changes: 25 additions & 0 deletions recipes/sqlite3mc/all/test_package/conanfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import os
from conan import ConanFile
from conan.tools.cmake import CMake, cmake_layout
from conan.tools.build import can_run


class sqlite3mcTest(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeDeps", "CMakeToolchain", "VirtualRunEnv"

def requirements(self):
self.requires(self.tested_reference_str)

def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()

def layout(self):
cmake_layout(self)

def test(self):
if can_run(self):
cmd = os.path.join(self.cpp.build.bindir, "test_sqlite3mc")
self.run(cmd, env="conanrun")
73 changes: 73 additions & 0 deletions recipes/sqlite3mc/all/test_package/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include <sqlite3mc/sqlite3mc.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
sqlite3 *db;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! Could we get a simplified version of this? It's not in CCI's policy to have extensive tests in the test_package, it should be as minimal as possible while testing the correct package of the library :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! Could we get a simplified version of this? It's not in CCI's policy to have extensive tests in the test_package, it should be as minimal as possible while testing the correct package of the library :)

Well, this is the most simple test I could think of. To verify that the package was build correctly it is necessary to create an encrypted database with some content and to access it with the right passphrase and a wrong passphrase afterwards.

If that test is too complex, we don't need any test at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the configuration option SQLITE_ENABLE_JSON1, because this option is no longer used by SQLite. And I added FreeBSD to the os list (instead of replacing Macos by FreeBSD), because this seems to be the only logical modification.

Regarding the "extensive" test I have no idea what you expect. What could and/or should be tested, if not the proper build of the package?

Copy link

@Myroendan Myroendan Feb 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,
we could remove the error handlings from the test, use logical OR on the result during operations, and check it once in the end. That would make it 35-40 lines shorter. Would that work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could remove the error handlings from the test, use logical OR on the result during operations, and check it once in the end. That would make it 35-40 lines shorter. Would that work?

It is certainly possible to reduce the number of lines of code by improving the error handling code (for example, by introducing a small function that checks the return code and emits an error message if necessary), but I'm not sure if that is what @RubenRBS intends.

const char *key = "password";
const char *wrongKey = "wrongPassword";

// Create database
int rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK)
{
fprintf(stderr, "Failed to open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}

// Encrypt
sqlite3_key(db, key, strlen(key));

// Fill db with some data and close it
rc = sqlite3_exec(db, "CREATE TABLE users (name TEXT NOT NULL, ID INTEGER PRIMARY KEY UNIQUE)", NULL, NULL, NULL);
if (rc != SQLITE_OK)
{
fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
rc = sqlite3_exec(db, "INSERT INTO users (name, ID) VALUES ('testUser', '12345')", NULL, NULL, NULL);
if (rc != SQLITE_OK)
{
fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
rc = sqlite3_close(db);
if (rc != SQLITE_OK)
{
fprintf(stderr, "Failed to close database: %s\n", sqlite3_errmsg(db));
return 1;
}

// Reopen and provide wrong key
rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK)
{
fprintf(stderr, "Failed to open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
sqlite3_key(db, wrongKey, strlen(wrongKey));

// Try to access the database, should fail
rc = sqlite3_exec(db, "SELECT name FROM users WHERE ID = '12345'", NULL, NULL, NULL);
if (rc == SQLITE_OK)
{
fprintf(stderr, "Access was provided without the proper key\n");
sqlite3_close(db);
return 1;
}
rc = sqlite3_close(db);
if (rc != SQLITE_OK)
{
fprintf(stderr, "Failed to close database: %s\n", sqlite3_errmsg(db));
return 1;
}


fprintf(stdout, "Test successful\n");
return 0;
}
3 changes: 3 additions & 0 deletions recipes/sqlite3mc/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
versions:
"1.8.0":
folder: all
Loading