sqlce 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqlce-0.1.2/.github/workflows/release.yml +162 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/PKG-INFO +25 -6
- {sqlce-0.1.0 → sqlce-0.1.2}/README.md +18 -5
- {sqlce-0.1.0 → sqlce-0.1.2}/bindings/src/module.cpp +39 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/CMakeLists.txt +21 -2
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/application/SdfDatabase.hpp +6 -0
- sqlce-0.1.2/core/include/sdf/domain/EncryptionMode.hpp +48 -0
- sqlce-0.1.2/core/include/sdf/domain/IPageCipher.hpp +23 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/domain/PageLayout.hpp +8 -0
- sqlce-0.1.2/core/include/sdf/infrastructure/AesCbc.hpp +30 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/infrastructure/FileStorage.hpp +7 -0
- sqlce-0.1.2/core/include/sdf/infrastructure/Sha256.hpp +16 -0
- sqlce-0.1.2/core/include/sdf/infrastructure/Sha512.hpp +16 -0
- sqlce-0.1.2/core/include/sdf/parsing/SdfPageCipher.hpp +41 -0
- {sqlce-0.1.0/core/app → sqlce-0.1.2/core}/main.cpp +67 -27
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/application/SdfDatabase.cpp +23 -2
- sqlce-0.1.2/core/src/infrastructure/AesCbc.cpp +230 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/infrastructure/FileStorage.cpp +43 -0
- sqlce-0.1.2/core/src/infrastructure/Sha256.cpp +126 -0
- sqlce-0.1.2/core/src/infrastructure/Sha512.cpp +144 -0
- sqlce-0.1.2/core/src/parsing/SdfPageCipher.cpp +151 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/docs/build.txt +5 -2
- sqlce-0.1.2/docs/en/cpp.md +47 -0
- sqlce-0.1.2/docs/en/python.md +51 -0
- sqlce-0.1.2/docs/ru/cpp.md +47 -0
- sqlce-0.1.2/docs/ru/python.md +51 -0
- sqlce-0.1.2/main.py +112 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/pyproject.toml +18 -2
- sqlce-0.1.2/python/sqlce/__init__.py +6 -0
- sqlce-0.1.2/python/sqlce/__init__.pyi +7 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/python/sqlce/_sdf_native.pyi +13 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/requirements.txt +1 -0
- sqlce-0.1.2/research/sdf_d3.py +90 -0
- sqlce-0.1.2/research/sdf_d4.py +94 -0
- sqlce-0.1.0/.github/workflows/release.yml +0 -78
- sqlce-0.1.0/docs/4.0/ru/00-overview.md +0 -50
- sqlce-0.1.0/docs/4.0/ru/01-rows.md +0 -119
- sqlce-0.1.0/docs/4.0/ru/02-lob.md +0 -69
- sqlce-0.1.0/docs/4.0/ru/03-column-types.md +0 -80
- sqlce-0.1.0/docs/4.0/ru/04-catalog.md +0 -66
- sqlce-0.1.0/docs/4.0/ru/05-limitations.md +0 -50
- sqlce-0.1.0/docs/4.0/ru/catalog/00-schema.md +0 -94
- sqlce-0.1.0/docs/4.0/ru/catalog/01-row-layout.md +0 -107
- sqlce-0.1.0/docs/4.0/ru/catalog/02-classification.md +0 -32
- sqlce-0.1.0/docs/4.0/ru/catalog/03-status.md +0 -72
- sqlce-0.1.0/main.py +0 -76
- sqlce-0.1.0/python/sqlce/__init__.py +0 -4
- sqlce-0.1.0/python/sqlce/__init__.pyi +0 -5
- {sqlce-0.1.0 → sqlce-0.1.2}/.gitignore +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/CMakeLists.txt +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/LICENSE +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/bindings/CMakeLists.txt +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/bindings/src/value_convert.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/bindings/src/value_convert.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/application/ColumnSchema.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/domain/ColumnDef.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/domain/ColumnType.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/domain/ColumnValue.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/domain/DateTimeValue.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/domain/Guid.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/domain/IPageStorage.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/domain/NumericValue.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/domain/Row.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/domain/TableDef.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/infrastructure/BinaryReader.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/infrastructure/PageView.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/parsing/CatalogPageScanner.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/parsing/CatalogRow.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/parsing/CatalogRowDecoder.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/parsing/CatalogSchema.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/parsing/ICatalogPageScanner.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/parsing/ICatalogRowDecoder.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/parsing/ILobChainRegistry.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/parsing/IRowDecoder.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/parsing/ITableCatalogBuilder.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/parsing/LobChainRegistry.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/parsing/RowDecoder.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/parsing/TableCatalogBuilder.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/include/sdf/parsing/TextDecoder.hpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/domain/ColumnDef.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/domain/ColumnType.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/domain/ColumnValue.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/domain/DateTimeValue.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/domain/Guid.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/domain/NumericValue.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/domain/Row.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/domain/TableDef.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/infrastructure/BinaryReader.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/infrastructure/PageView.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/parsing/CatalogPageScanner.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/parsing/CatalogRow.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/parsing/CatalogRowDecoder.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/parsing/CatalogSchema.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/parsing/LobChainRegistry.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/parsing/RowDecoder.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/parsing/TableCatalogBuilder.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/src/parsing/TextDecoder.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/tests/CMakeLists.txt +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/core/tests/count_test.cpp +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/docs/CONVENTIONS.md +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/research/catalog_parse/sdf_pages.py +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/research/catalog_parse/syscatalog.py +0 -0
- {sqlce-0.1.0 → sqlce-0.1.2}/research/sdf_reader.py +0 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
name: Build and release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
jobs:
|
|
8
|
+
build-linux:
|
|
9
|
+
name: Build wheel (Linux)
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Build wheels
|
|
15
|
+
uses: pypa/cibuildwheel@v2.21.3
|
|
16
|
+
env:
|
|
17
|
+
CIBW_ARCHS_LINUX: x86_64
|
|
18
|
+
CIBW_BUILD_VERBOSITY: 1
|
|
19
|
+
|
|
20
|
+
- name: Build sdist
|
|
21
|
+
run: |
|
|
22
|
+
python -m pip install --upgrade pip build
|
|
23
|
+
python -m build --sdist
|
|
24
|
+
|
|
25
|
+
- name: Collect wheels and sdist into one flat folder
|
|
26
|
+
run: |
|
|
27
|
+
mkdir -p collected
|
|
28
|
+
cp wheelhouse/*.whl collected/
|
|
29
|
+
cp dist/*.tar.gz collected/
|
|
30
|
+
|
|
31
|
+
- uses: actions/upload-artifact@v4
|
|
32
|
+
with:
|
|
33
|
+
name: dist-linux
|
|
34
|
+
path: collected/*
|
|
35
|
+
|
|
36
|
+
build-windows:
|
|
37
|
+
name: Build wheel (Windows)
|
|
38
|
+
runs-on: windows-latest
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
|
|
42
|
+
- name: Build wheels
|
|
43
|
+
uses: pypa/cibuildwheel@v2.21.3
|
|
44
|
+
env:
|
|
45
|
+
CIBW_ARCHS_WINDOWS: AMD64
|
|
46
|
+
CIBW_BUILD_VERBOSITY: 1
|
|
47
|
+
CIBW_CMAKE_ARGS: "-DSDF_BUILD_PYTHON_BINDINGS=ON"
|
|
48
|
+
|
|
49
|
+
- uses: actions/upload-artifact@v4
|
|
50
|
+
with:
|
|
51
|
+
name: dist-windows
|
|
52
|
+
path: wheelhouse/*
|
|
53
|
+
|
|
54
|
+
build-cpp-linux:
|
|
55
|
+
name: Build C++ library (Linux, ${{ matrix.link }})
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
strategy:
|
|
58
|
+
matrix:
|
|
59
|
+
link: [static, shared]
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v4
|
|
62
|
+
|
|
63
|
+
- name: Configure CMake
|
|
64
|
+
run: |
|
|
65
|
+
cmake -S . -B build \
|
|
66
|
+
-DCMAKE_BUILD_TYPE=Release \
|
|
67
|
+
-DBUILD_SHARED_LIBS=${{ matrix.link == 'shared' && 'ON' || 'OFF' }}
|
|
68
|
+
|
|
69
|
+
- name: Build libsdf
|
|
70
|
+
run: cmake --build build --target libsdf -j
|
|
71
|
+
|
|
72
|
+
- name: Package library + headers
|
|
73
|
+
run: |
|
|
74
|
+
version="${GITHUB_REF_NAME#v}"
|
|
75
|
+
pkg="libsdf-${version}-linux-x86_64-${{ matrix.link }}"
|
|
76
|
+
mkdir -p "$pkg/lib" "$pkg/include"
|
|
77
|
+
cp build/dist/lib/libsdf.* "$pkg/lib/"
|
|
78
|
+
cp -r core/include/. "$pkg/include/"
|
|
79
|
+
tar -czf "${pkg}.tar.gz" "$pkg"
|
|
80
|
+
|
|
81
|
+
- uses: actions/upload-artifact@v4
|
|
82
|
+
with:
|
|
83
|
+
name: cpp-linux-${{ matrix.link }}
|
|
84
|
+
path: libsdf-*.tar.gz
|
|
85
|
+
|
|
86
|
+
build-cpp-windows:
|
|
87
|
+
name: Build C++ library (Windows, ${{ matrix.link }})
|
|
88
|
+
runs-on: windows-latest
|
|
89
|
+
strategy:
|
|
90
|
+
matrix:
|
|
91
|
+
link: [static, shared]
|
|
92
|
+
steps:
|
|
93
|
+
- uses: actions/checkout@v4
|
|
94
|
+
|
|
95
|
+
- name: Configure CMake
|
|
96
|
+
shell: pwsh
|
|
97
|
+
run: |
|
|
98
|
+
$shared = if ("${{ matrix.link }}" -eq "shared") { "ON" } else { "OFF" }
|
|
99
|
+
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=$shared
|
|
100
|
+
|
|
101
|
+
- name: Build libsdf
|
|
102
|
+
run: cmake --build build --target libsdf --config Release -j
|
|
103
|
+
|
|
104
|
+
- name: Package library + headers
|
|
105
|
+
shell: pwsh
|
|
106
|
+
run: |
|
|
107
|
+
$version = "${env:GITHUB_REF_NAME}" -replace '^v', ''
|
|
108
|
+
$pkg = "libsdf-$version-windows-x86_64-${{ matrix.link }}"
|
|
109
|
+
New-Item -ItemType Directory -Force -Path "$pkg/lib" | Out-Null
|
|
110
|
+
New-Item -ItemType Directory -Force -Path "$pkg/include" | Out-Null
|
|
111
|
+
Copy-Item "build/dist/lib/libsdf.*" "$pkg/lib/"
|
|
112
|
+
Copy-Item -Recurse "core/include/*" "$pkg/include/"
|
|
113
|
+
Compress-Archive -Path $pkg -DestinationPath "$pkg.zip"
|
|
114
|
+
|
|
115
|
+
- uses: actions/upload-artifact@v4
|
|
116
|
+
with:
|
|
117
|
+
name: cpp-windows-${{ matrix.link }}
|
|
118
|
+
path: libsdf-*.zip
|
|
119
|
+
|
|
120
|
+
publish:
|
|
121
|
+
name: Publish to PyPI
|
|
122
|
+
needs: [build-linux, build-windows]
|
|
123
|
+
runs-on: ubuntu-latest
|
|
124
|
+
environment: pypi
|
|
125
|
+
permissions:
|
|
126
|
+
id-token: write
|
|
127
|
+
steps:
|
|
128
|
+
- uses: actions/download-artifact@v4
|
|
129
|
+
with:
|
|
130
|
+
pattern: dist-*
|
|
131
|
+
path: dist
|
|
132
|
+
merge-multiple: true
|
|
133
|
+
|
|
134
|
+
- name: Publish to PyPI
|
|
135
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
136
|
+
with:
|
|
137
|
+
packages-dir: dist
|
|
138
|
+
|
|
139
|
+
publish-release:
|
|
140
|
+
name: Publish GitHub Release
|
|
141
|
+
needs: [build-cpp-linux, build-cpp-windows, build-linux, build-windows]
|
|
142
|
+
runs-on: ubuntu-latest
|
|
143
|
+
permissions:
|
|
144
|
+
contents: write
|
|
145
|
+
steps:
|
|
146
|
+
- uses: actions/download-artifact@v4
|
|
147
|
+
with:
|
|
148
|
+
pattern: cpp-*
|
|
149
|
+
path: release-assets
|
|
150
|
+
merge-multiple: true
|
|
151
|
+
|
|
152
|
+
- uses: actions/download-artifact@v4
|
|
153
|
+
with:
|
|
154
|
+
pattern: dist-*
|
|
155
|
+
path: release-assets
|
|
156
|
+
merge-multiple: true
|
|
157
|
+
|
|
158
|
+
- name: Create GitHub Release
|
|
159
|
+
uses: softprops/action-gh-release@v2
|
|
160
|
+
with:
|
|
161
|
+
files: release-assets/*
|
|
162
|
+
generate_release_notes: true
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: sqlce
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Cross-platform lib for reading SQL Server CE (.sdf)
|
|
5
5
|
Author-Email: boykopovar <boykopovar@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -19,6 +19,12 @@ Classifier: Operating System :: POSIX :: Linux
|
|
|
19
19
|
Project-URL: Homepage, https://github.com/boykopovar/sqlce
|
|
20
20
|
Project-URL: Issues, https://github.com/boykopovar/sqlce/issues
|
|
21
21
|
Requires-Python: >=3.8
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: scikit-build-core>=0.10; extra == "dev"
|
|
24
|
+
Requires-Dist: pybind11>=2.12; extra == "dev"
|
|
25
|
+
Requires-Dist: build; extra == "dev"
|
|
26
|
+
Requires-Dist: twine; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest; extra == "dev"
|
|
22
28
|
Description-Content-Type: text/markdown
|
|
23
29
|
|
|
24
30
|
# sqlce
|
|
@@ -27,21 +33,34 @@ Description-Content-Type: text/markdown
|
|
|
27
33
|
|
|
28
34
|
Анализ внутреннего устройства формата по декомпилированным компонентам SQL CE. Цель - предоставить кроссплатформенную и независимую от рантайма реализацию чтения `.sdf` и документацию формата.
|
|
29
35
|
|
|
30
|
-
## Структура
|
|
31
36
|
|
|
32
37
|
- `core/` - реализация парсера на C++
|
|
33
38
|
- `bindings/` - Биндинги для C++ библиотеки к другим языкам
|
|
34
39
|
- `python/sqlce/` - Python API
|
|
35
40
|
- `research/` - Экспериментальные скрипты
|
|
36
41
|
|
|
37
|
-
## Установка
|
|
38
42
|
|
|
39
43
|
```bash
|
|
40
44
|
pip install sqlce
|
|
41
45
|
```
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
```python
|
|
48
|
+
from sqlce import SdfDatabase
|
|
44
49
|
|
|
45
|
-
|
|
50
|
+
db = SdfDatabase("example.sdf", password="secret123")
|
|
46
51
|
|
|
47
|
-
|
|
52
|
+
for table_name in db.list_tables():
|
|
53
|
+
print(table_name, db.table_schema(table_name))
|
|
54
|
+
for row in db.read_table(table_name):
|
|
55
|
+
print(row)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Текущая [реализация](main.py) поддерживает чтение (поддерживается передача пароля) списка таблиц, структуры таблиц, содержимого записей. Использует c++.
|
|
59
|
+
|
|
60
|
+
[Документация](docs/ru/python.md)
|
|
61
|
+
|
|
62
|
+
[Documentation](docs/en/python.md)
|
|
63
|
+
|
|
64
|
+
[Соглашения по написанию кода](docs/CONVENTIONS.md) (PR приветствуются)
|
|
65
|
+
|
|
66
|
+
> Исключительно для интероперабельности с существующими файлами `.sdf` формата. Не содержит оригинальный код продукта.
|
|
@@ -4,21 +4,34 @@
|
|
|
4
4
|
|
|
5
5
|
Анализ внутреннего устройства формата по декомпилированным компонентам SQL CE. Цель - предоставить кроссплатформенную и независимую от рантайма реализацию чтения `.sdf` и документацию формата.
|
|
6
6
|
|
|
7
|
-
## Структура
|
|
8
7
|
|
|
9
8
|
- `core/` - реализация парсера на C++
|
|
10
9
|
- `bindings/` - Биндинги для C++ библиотеки к другим языкам
|
|
11
10
|
- `python/sqlce/` - Python API
|
|
12
11
|
- `research/` - Экспериментальные скрипты
|
|
13
12
|
|
|
14
|
-
## Установка
|
|
15
13
|
|
|
16
14
|
```bash
|
|
17
15
|
pip install sqlce
|
|
18
16
|
```
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
```python
|
|
19
|
+
from sqlce import SdfDatabase
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
db = SdfDatabase("example.sdf", password="secret123")
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
for table_name in db.list_tables():
|
|
24
|
+
print(table_name, db.table_schema(table_name))
|
|
25
|
+
for row in db.read_table(table_name):
|
|
26
|
+
print(row)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Текущая [реализация](main.py) поддерживает чтение (поддерживается передача пароля) списка таблиц, структуры таблиц, содержимого записей. Использует c++.
|
|
30
|
+
|
|
31
|
+
[Документация](docs/ru/python.md)
|
|
32
|
+
|
|
33
|
+
[Documentation](docs/en/python.md)
|
|
34
|
+
|
|
35
|
+
[Соглашения по написанию кода](docs/CONVENTIONS.md) (PR приветствуются)
|
|
36
|
+
|
|
37
|
+
> Исключительно для интероперабельности с существующими файлами `.sdf` формата. Не содержит оригинальный код продукта.
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#include "sdf/application/ColumnSchema.hpp"
|
|
11
11
|
#include "sdf/application/SdfDatabase.hpp"
|
|
12
|
+
#include "sdf/domain/EncryptionMode.hpp"
|
|
12
13
|
#include "sdf/domain/Row.hpp"
|
|
13
14
|
|
|
14
15
|
#include "value_convert.hpp"
|
|
@@ -26,7 +27,9 @@ constexpr char ModuleDoc[] = "Native reader for SQL Server Compact (.sdf) databa
|
|
|
26
27
|
constexpr char DatabaseClassName[] = "SdfDatabase";
|
|
27
28
|
constexpr char DatabaseDoc[] = "Reads tables, schemas and rows from a .sdf database file.";
|
|
28
29
|
constexpr char InitDoc[] = "Open a .sdf file at the given path.";
|
|
30
|
+
constexpr char InitWithPasswordDoc[] = "Open a password-protected .sdf file, auto-detecting its encryption mode.";
|
|
29
31
|
constexpr char PathArgName[] = "path";
|
|
32
|
+
constexpr char PasswordArgName[] = "password";
|
|
30
33
|
constexpr char ListTablesName[] = "list_tables";
|
|
31
34
|
constexpr char ListTablesDoc[] = "Return the names of all tables in the database.";
|
|
32
35
|
constexpr char TableSchemaName[] = "table_schema";
|
|
@@ -44,6 +47,9 @@ constexpr char DeclaredSizeAttrName[] = "declared_size";
|
|
|
44
47
|
constexpr char PrecisionAttrName[] = "precision";
|
|
45
48
|
constexpr char ScaleAttrName[] = "scale";
|
|
46
49
|
|
|
50
|
+
constexpr char UnsupportedEncryptionModeErrorName[] = "UnsupportedEncryptionModeError";
|
|
51
|
+
constexpr char InvalidPasswordErrorName[] = "InvalidPasswordError";
|
|
52
|
+
|
|
47
53
|
py::dict RowToDict(const domain::Row& row)
|
|
48
54
|
{
|
|
49
55
|
py::dict result;
|
|
@@ -75,12 +81,40 @@ std::optional<int> OptionalByteToInt(std::optional<std::uint8_t> value)
|
|
|
75
81
|
return static_cast<int>(*value);
|
|
76
82
|
}
|
|
77
83
|
|
|
84
|
+
void RegisterExceptions(py::module_& module)
|
|
85
|
+
{
|
|
86
|
+
static const py::exception<domain::UnsupportedEncryptionModeException> unsupportedModeError(
|
|
87
|
+
module, UnsupportedEncryptionModeErrorName, PyExc_ValueError);
|
|
88
|
+
static const py::exception<domain::InvalidPasswordException> invalidPasswordError(
|
|
89
|
+
module, InvalidPasswordErrorName, PyExc_ValueError);
|
|
90
|
+
|
|
91
|
+
py::register_exception_translator([](std::exception_ptr lastException) {
|
|
92
|
+
try
|
|
93
|
+
{
|
|
94
|
+
if (lastException)
|
|
95
|
+
{
|
|
96
|
+
std::rethrow_exception(lastException);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (const domain::UnsupportedEncryptionModeException& error)
|
|
100
|
+
{
|
|
101
|
+
PyErr_SetString(unsupportedModeError.ptr(), error.what());
|
|
102
|
+
}
|
|
103
|
+
catch (const domain::InvalidPasswordException& error)
|
|
104
|
+
{
|
|
105
|
+
PyErr_SetString(invalidPasswordError.ptr(), error.what());
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
78
110
|
}
|
|
79
111
|
|
|
80
112
|
PYBIND11_MODULE(_sdf_native, module)
|
|
81
113
|
{
|
|
82
114
|
module.doc() = ModuleDoc;
|
|
83
115
|
|
|
116
|
+
RegisterExceptions(module);
|
|
117
|
+
|
|
84
118
|
py::class_<application::ColumnSchema>(module, SchemaClassName, SchemaDoc)
|
|
85
119
|
.def_property_readonly(
|
|
86
120
|
OrdinalAttrName,
|
|
@@ -103,6 +137,11 @@ PYBIND11_MODULE(_sdf_native, module)
|
|
|
103
137
|
|
|
104
138
|
py::class_<application::SdfDatabase>(module, DatabaseClassName, DatabaseDoc)
|
|
105
139
|
.def(py::init<const std::string&>(), py::arg(PathArgName), InitDoc)
|
|
140
|
+
.def(
|
|
141
|
+
py::init<const std::string&, const std::string&>(),
|
|
142
|
+
py::arg(PathArgName),
|
|
143
|
+
py::arg(PasswordArgName),
|
|
144
|
+
InitWithPasswordDoc)
|
|
106
145
|
.def(ListTablesName, &application::SdfDatabase::ListTables, ListTablesDoc)
|
|
107
146
|
.def(
|
|
108
147
|
TableSchemaName,
|
|
@@ -18,21 +18,32 @@ add_library(libsdf
|
|
|
18
18
|
src/domain/NumericValue.cpp
|
|
19
19
|
src/domain/Row.cpp
|
|
20
20
|
src/domain/TableDef.cpp
|
|
21
|
+
src/infrastructure/AesCbc.cpp
|
|
21
22
|
src/infrastructure/BinaryReader.cpp
|
|
22
23
|
src/infrastructure/FileStorage.cpp
|
|
23
24
|
src/infrastructure/PageView.cpp
|
|
25
|
+
src/infrastructure/Sha256.cpp
|
|
26
|
+
src/infrastructure/Sha512.cpp
|
|
24
27
|
src/parsing/CatalogPageScanner.cpp
|
|
25
28
|
src/parsing/CatalogRow.cpp
|
|
26
29
|
src/parsing/CatalogRowDecoder.cpp
|
|
27
30
|
src/parsing/CatalogSchema.cpp
|
|
28
31
|
src/parsing/LobChainRegistry.cpp
|
|
29
32
|
src/parsing/RowDecoder.cpp
|
|
33
|
+
src/parsing/SdfPageCipher.cpp
|
|
30
34
|
src/parsing/TableCatalogBuilder.cpp
|
|
31
35
|
src/parsing/TextDecoder.cpp
|
|
32
36
|
src/application/SdfDatabase.cpp
|
|
33
37
|
)
|
|
34
38
|
|
|
35
|
-
set_target_properties(libsdf PROPERTIES
|
|
39
|
+
set_target_properties(libsdf PROPERTIES
|
|
40
|
+
PREFIX ""
|
|
41
|
+
OUTPUT_NAME "libsdf"
|
|
42
|
+
POSITION_INDEPENDENT_CODE ON
|
|
43
|
+
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/dist/lib"
|
|
44
|
+
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/dist/lib"
|
|
45
|
+
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/dist/lib"
|
|
46
|
+
)
|
|
36
47
|
|
|
37
48
|
target_include_directories(libsdf PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
|
38
49
|
|
|
@@ -45,7 +56,7 @@ else()
|
|
|
45
56
|
target_compile_options(libsdf PRIVATE -Wall -Wextra)
|
|
46
57
|
endif()
|
|
47
58
|
|
|
48
|
-
add_executable(sdf_dump
|
|
59
|
+
add_executable(sdf_dump main.cpp)
|
|
49
60
|
target_link_libraries(sdf_dump PRIVATE libsdf)
|
|
50
61
|
|
|
51
62
|
if(MSVC)
|
|
@@ -53,6 +64,14 @@ if(MSVC)
|
|
|
53
64
|
target_compile_definitions(sdf_dump PRIVATE NOMINMAX _CRT_SECURE_NO_WARNINGS)
|
|
54
65
|
else()
|
|
55
66
|
target_compile_options(sdf_dump PRIVATE -Wall -Wextra)
|
|
67
|
+
if(MINGW)
|
|
68
|
+
target_link_options(sdf_dump PRIVATE
|
|
69
|
+
-static
|
|
70
|
+
-static-libgcc
|
|
71
|
+
-static-libstdc++
|
|
72
|
+
-Wl,-Bstatic,--whole-archive -lwinpthread -Wl,--no-whole-archive
|
|
73
|
+
)
|
|
74
|
+
endif()
|
|
56
75
|
endif()
|
|
57
76
|
|
|
58
77
|
enable_testing()
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include <vector>
|
|
8
8
|
|
|
9
9
|
#include "sdf/application/ColumnSchema.hpp"
|
|
10
|
+
#include "sdf/domain/IPageCipher.hpp"
|
|
10
11
|
#include "sdf/domain/IPageStorage.hpp"
|
|
11
12
|
#include "sdf/domain/Row.hpp"
|
|
12
13
|
#include "sdf/domain/TableDef.hpp"
|
|
@@ -22,6 +23,7 @@ class SdfDatabase
|
|
|
22
23
|
{
|
|
23
24
|
public:
|
|
24
25
|
explicit SdfDatabase(const std::string& path);
|
|
26
|
+
SdfDatabase(const std::string& path, const std::string& password);
|
|
25
27
|
|
|
26
28
|
std::vector<std::string> ListTables() const;
|
|
27
29
|
std::vector<ColumnSchema> TableSchema(const std::string& tableName) const;
|
|
@@ -36,6 +38,10 @@ private:
|
|
|
36
38
|
|
|
37
39
|
std::map<std::string, domain::TableDef> _tables;
|
|
38
40
|
|
|
41
|
+
explicit SdfDatabase(std::unique_ptr<domain::IPageStorage> storage);
|
|
42
|
+
|
|
43
|
+
static std::unique_ptr<domain::IPageStorage> OpenStorage(const std::string& path, const std::string& password);
|
|
44
|
+
|
|
39
45
|
void AssignDataPages();
|
|
40
46
|
const domain::TableDef& RequireTable(const std::string& tableName) const;
|
|
41
47
|
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#ifndef SDF_DOMAIN_ENCRYPTION_MODE_HPP
|
|
2
|
+
#define SDF_DOMAIN_ENCRYPTION_MODE_HPP
|
|
3
|
+
|
|
4
|
+
#include <cstdint>
|
|
5
|
+
#include <stdexcept>
|
|
6
|
+
#include <string>
|
|
7
|
+
|
|
8
|
+
namespace sdf::domain
|
|
9
|
+
{
|
|
10
|
+
|
|
11
|
+
enum class EncryptionMode : std::uint32_t
|
|
12
|
+
{
|
|
13
|
+
None = 0,
|
|
14
|
+
Rc4Sha1 = 1,
|
|
15
|
+
Aes128Sha1 = 2,
|
|
16
|
+
Aes128Sha256 = 3,
|
|
17
|
+
Aes256Sha512 = 4
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
class UnsupportedEncryptionModeException final : public std::runtime_error
|
|
21
|
+
{
|
|
22
|
+
public:
|
|
23
|
+
explicit UnsupportedEncryptionModeException(EncryptionMode mode)
|
|
24
|
+
: std::runtime_error("unsupported .sdf encryption mode: " + std::to_string(static_cast<std::uint32_t>(mode)))
|
|
25
|
+
, _mode(mode)
|
|
26
|
+
{
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
EncryptionMode Mode() const
|
|
30
|
+
{
|
|
31
|
+
return _mode;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private:
|
|
35
|
+
EncryptionMode _mode;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
class InvalidPasswordException final : public std::runtime_error
|
|
39
|
+
{
|
|
40
|
+
public:
|
|
41
|
+
InvalidPasswordException() : std::runtime_error("password verification failed")
|
|
42
|
+
{
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
#endif
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#ifndef SDF_DOMAIN_I_PAGE_CIPHER_HPP
|
|
2
|
+
#define SDF_DOMAIN_I_PAGE_CIPHER_HPP
|
|
3
|
+
|
|
4
|
+
#include <cstddef>
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <span>
|
|
7
|
+
#include <vector>
|
|
8
|
+
|
|
9
|
+
namespace sdf::domain
|
|
10
|
+
{
|
|
11
|
+
|
|
12
|
+
class IPageCipher
|
|
13
|
+
{
|
|
14
|
+
public:
|
|
15
|
+
virtual ~IPageCipher() = default;
|
|
16
|
+
|
|
17
|
+
virtual bool VerifyPassword() const = 0;
|
|
18
|
+
virtual std::vector<std::uint8_t> DecryptPage(std::size_t pageNumber, std::span<const std::uint8_t> page) const = 0;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#endif
|
|
@@ -14,6 +14,14 @@ constexpr std::size_t LobPageHeaderLength = 16;
|
|
|
14
14
|
constexpr std::size_t SlotArrayBaseIndex = 1017;
|
|
15
15
|
constexpr std::size_t RowHeaderLength = 28;
|
|
16
16
|
|
|
17
|
+
constexpr std::size_t PlaintextHeaderLength = 16;
|
|
18
|
+
constexpr std::size_t Page0ModeOffset = 184;
|
|
19
|
+
constexpr std::size_t Page0VerifierOffset = 76;
|
|
20
|
+
constexpr std::size_t Page0VerifierLength = 96;
|
|
21
|
+
constexpr std::size_t Page0KeyParamOffset = 188;
|
|
22
|
+
constexpr std::size_t Page0KeyParamLength = 4;
|
|
23
|
+
constexpr std::size_t MaxPasswordLength = 40;
|
|
24
|
+
|
|
17
25
|
}
|
|
18
26
|
|
|
19
27
|
#endif
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#ifndef SDF_INFRASTRUCTURE_AES_CBC_HPP
|
|
2
|
+
#define SDF_INFRASTRUCTURE_AES_CBC_HPP
|
|
3
|
+
|
|
4
|
+
#include <array>
|
|
5
|
+
#include <cstddef>
|
|
6
|
+
#include <cstdint>
|
|
7
|
+
#include <span>
|
|
8
|
+
#include <vector>
|
|
9
|
+
|
|
10
|
+
namespace sdf::infrastructure
|
|
11
|
+
{
|
|
12
|
+
|
|
13
|
+
class AesCbcDecryptor
|
|
14
|
+
{
|
|
15
|
+
public:
|
|
16
|
+
explicit AesCbcDecryptor(std::span<const std::uint8_t> key, std::span<const std::uint8_t, 16> iv);
|
|
17
|
+
|
|
18
|
+
std::vector<std::uint8_t> Decrypt(std::span<const std::uint8_t> ciphertext) const;
|
|
19
|
+
|
|
20
|
+
private:
|
|
21
|
+
std::vector<std::uint32_t> _roundKeys;
|
|
22
|
+
std::size_t _roundCount;
|
|
23
|
+
std::array<std::uint8_t, 16> _iv;
|
|
24
|
+
|
|
25
|
+
void DecryptBlock(const std::uint8_t* in, std::uint8_t* out) const;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#endif
|
|
@@ -2,18 +2,23 @@
|
|
|
2
2
|
#define SDF_INFRASTRUCTURE_FILE_STORAGE_HPP
|
|
3
3
|
|
|
4
4
|
#include <cstdint>
|
|
5
|
+
#include <optional>
|
|
5
6
|
#include <string>
|
|
6
7
|
#include <vector>
|
|
7
8
|
|
|
9
|
+
#include "sdf/domain/IPageCipher.hpp"
|
|
8
10
|
#include "sdf/domain/IPageStorage.hpp"
|
|
9
11
|
|
|
10
12
|
namespace sdf::infrastructure
|
|
11
13
|
{
|
|
12
14
|
|
|
15
|
+
std::vector<std::uint8_t> ReadFirstPageRaw(const std::string& path);
|
|
16
|
+
|
|
13
17
|
class FileStorage final : public domain::IPageStorage
|
|
14
18
|
{
|
|
15
19
|
public:
|
|
16
20
|
explicit FileStorage(const std::string& path);
|
|
21
|
+
FileStorage(const std::string& path, const domain::IPageCipher& cipher);
|
|
17
22
|
|
|
18
23
|
std::size_t PageCount() const override;
|
|
19
24
|
std::span<const std::uint8_t> PageBytes(std::size_t pageNumber) const override;
|
|
@@ -21,6 +26,8 @@ public:
|
|
|
21
26
|
private:
|
|
22
27
|
std::vector<std::uint8_t> _fileBytes;
|
|
23
28
|
std::size_t _pageCount;
|
|
29
|
+
|
|
30
|
+
void Load(const std::string& path);
|
|
24
31
|
};
|
|
25
32
|
|
|
26
33
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#ifndef SDF_INFRASTRUCTURE_SHA256_HPP
|
|
2
|
+
#define SDF_INFRASTRUCTURE_SHA256_HPP
|
|
3
|
+
|
|
4
|
+
#include <array>
|
|
5
|
+
#include <cstddef>
|
|
6
|
+
#include <cstdint>
|
|
7
|
+
#include <span>
|
|
8
|
+
|
|
9
|
+
namespace sdf::infrastructure
|
|
10
|
+
{
|
|
11
|
+
|
|
12
|
+
std::array<std::uint8_t, 32> Sha256(std::span<const std::uint8_t> message);
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
#endif
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#ifndef SDF_INFRASTRUCTURE_SHA512_HPP
|
|
2
|
+
#define SDF_INFRASTRUCTURE_SHA512_HPP
|
|
3
|
+
|
|
4
|
+
#include <array>
|
|
5
|
+
#include <cstddef>
|
|
6
|
+
#include <cstdint>
|
|
7
|
+
#include <span>
|
|
8
|
+
|
|
9
|
+
namespace sdf::infrastructure
|
|
10
|
+
{
|
|
11
|
+
|
|
12
|
+
std::array<std::uint8_t, 64> Sha512(std::span<const std::uint8_t> message);
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
#endif
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#ifndef SDF_PARSING_SDF_PAGE_CIPHER_HPP
|
|
2
|
+
#define SDF_PARSING_SDF_PAGE_CIPHER_HPP
|
|
3
|
+
|
|
4
|
+
#include <array>
|
|
5
|
+
#include <cstddef>
|
|
6
|
+
#include <cstdint>
|
|
7
|
+
#include <span>
|
|
8
|
+
#include <string>
|
|
9
|
+
#include <vector>
|
|
10
|
+
|
|
11
|
+
#include "sdf/domain/EncryptionMode.hpp"
|
|
12
|
+
#include "sdf/domain/IPageCipher.hpp"
|
|
13
|
+
|
|
14
|
+
namespace sdf::parsing
|
|
15
|
+
{
|
|
16
|
+
|
|
17
|
+
class SdfPageCipher final : public domain::IPageCipher
|
|
18
|
+
{
|
|
19
|
+
public:
|
|
20
|
+
SdfPageCipher(std::span<const std::uint8_t> page0, std::string password);
|
|
21
|
+
|
|
22
|
+
static domain::EncryptionMode ReadMode(std::span<const std::uint8_t> page0);
|
|
23
|
+
|
|
24
|
+
bool VerifyPassword() const override;
|
|
25
|
+
std::vector<std::uint8_t> DecryptPage(std::size_t pageNumber, std::span<const std::uint8_t> page) const override;
|
|
26
|
+
|
|
27
|
+
private:
|
|
28
|
+
domain::EncryptionMode _mode;
|
|
29
|
+
std::string _password;
|
|
30
|
+
std::vector<std::uint8_t> _passwordUtf16Le;
|
|
31
|
+
std::vector<std::uint8_t> _page0Ciphertext;
|
|
32
|
+
std::array<std::uint8_t, 4> _page0KeyParam;
|
|
33
|
+
|
|
34
|
+
std::vector<std::uint8_t> DecryptPage0(std::span<const std::uint8_t> page0) const;
|
|
35
|
+
std::vector<std::uint8_t> DeriveKey(std::span<const std::uint8_t> keyParam) const;
|
|
36
|
+
std::vector<std::uint8_t> HashPasswordWith(std::span<const std::uint8_t> keyParam) const;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#endif
|