├── .eslintrc.json ├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── native ├── .gitignore ├── Cargo.toml ├── res │ ├── darwin │ │ ├── Distribution │ │ ├── Resources │ │ │ ├── conclusion.html │ │ │ └── welcome.html │ │ └── scripts │ │ │ └── postinstall │ └── win.nsi ├── scripts │ ├── build.sh │ └── dev.sh └── src │ ├── indexer.rs │ ├── main.rs │ ├── native_messaging.rs │ └── secrets.rs ├── package-lock.json ├── package.json ├── src ├── background.ts ├── background │ ├── debug.ts │ ├── lib.ts │ └── native.ts ├── contentscript.ts ├── icons │ └── 32.png ├── manifest.chrome.json ├── manifest.firefox.json └── resources │ └── bundle.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "parser": "@typescript-eslint/parser", 4 | "ecmaVersion": 2018, 5 | "project": "./tsconfig.json" 6 | }, 7 | "env": { 8 | "es6": true 9 | }, 10 | "extends": [ 11 | "plugin:@typescript-eslint/recommended", 12 | "prettier" 13 | ] 14 | } -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: build 8 | runs-on: ${{ matrix.config.os }} 9 | strategy: 10 | matrix: 11 | config: 12 | - { os: ubuntu-latest, release_uname: "linux-x86_64" } 13 | - { os: macos-latest, release_uname: "mac" } 14 | - { os: windows-latest, release_uname: "windows" } 15 | 16 | steps: 17 | - uses: actions/checkout@v1 18 | - name: nodejs 19 | uses: actions/setup-node@v1 20 | with: 21 | node-version: ${{ matrix.node-version }} 22 | 23 | - name: ubuntu 24 | if: matrix.config.os == 'ubuntu-latest' 25 | run: | 26 | sudo apt-get install libsqlcipher0 libsqlcipher-dev libsecret-1-dev 27 | cargo install cargo-deb 28 | 29 | - name: build 30 | shell: bash 31 | run: | 32 | # enable symlinking for windows 33 | # (workaround for patched cxx crate) 34 | export MSYS=winsymlinks:nativestrict 35 | git config --global core.symlinks true 36 | 37 | npm install 38 | npm run lint 39 | npm run build:native 40 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | createrelease: 10 | name: create release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v1 14 | - id: create_release 15 | uses: actions/create-release@v1 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | with: 19 | tag_name: ${{ github.ref }} 20 | release_name: ${{ github.ref }} 21 | draft: true 22 | prerelease: true 23 | - run: echo "${{ steps.create_release.outputs.upload_url }}" > release_url.txt 24 | - uses: actions/upload-artifact@v1 25 | with: 26 | name: release_url 27 | path: release_url.txt 28 | 29 | macos: 30 | needs: [createrelease] 31 | name: build and upload 32 | runs-on: ${{ matrix.config.os }} 33 | strategy: 34 | matrix: 35 | config: 36 | - { 37 | os: ubuntu-latest, 38 | suffix: "x86-amd64.deb", 39 | asset_path: "./target/release/radical-native.deb", 40 | } 41 | - { 42 | os: ubuntu-latest, 43 | suffix: "x86-amd64", 44 | asset_path: "./target/release/radical-native", 45 | } 46 | - { 47 | os: macos-latest, 48 | suffix: "darwin.pkg", 49 | asset_path: "./target/release/radical-native.pkg", 50 | } 51 | - { 52 | os: windows-latest, 53 | suffix: "win64.exe", 54 | asset_path: "./target/release/radical-native-installer.exe", 55 | } 56 | steps: 57 | - uses: actions/checkout@v2 58 | - uses: actions/download-artifact@v1 59 | with: 60 | name: release_url 61 | 62 | - name: prereq 63 | if: matrix.config.os == 'ubuntu-latest' 64 | run: | 65 | sudo apt-get install libsqlcipher0 libsqlcipher-dev libsecret-1-dev 66 | cargo install cargo-deb 67 | 68 | - name: build 69 | id: build 70 | shell: bash 71 | run: | 72 | # enable symlinking for windows 73 | # (workaround for patched cxx crate) 74 | export MSYS=winsymlinks:nativestrict 75 | git config --global core.symlinks true 76 | 77 | npm install 78 | npm run lint 79 | npm run build:native 80 | 81 | value=`cat release_url/release_url.txt` 82 | echo "::set-output name=upload_url::$value" 83 | echo "::set-output name=version::${GITHUB_REF:10}" 84 | 85 | - id: upload-release-asset 86 | uses: actions/upload-release-asset@v1 87 | env: 88 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 89 | with: 90 | upload_url: ${{ steps.build.outputs.upload_url }} 91 | asset_path: ${{ matrix.config.asset_path }} 92 | asset_name: radical-native_${{ steps.build.outputs.version }}_${{ matrix.config.suffix }} 93 | asset_content_type: application/octet-stream 94 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | dist 4 | coverage 5 | .nyc_output 6 | .cache 7 | target 8 | .*DS_Store -------------------------------------------------------------------------------- /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 = "addr2line" 7 | version = "0.16.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aes-ctr" 22 | version = "0.6.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "7729c3cde54d67063be556aeac75a81330d802f0259500ca40cb52967f975763" 25 | dependencies = [ 26 | "aes-soft", 27 | "aesni", 28 | "cipher", 29 | "ctr", 30 | ] 31 | 32 | [[package]] 33 | name = "aes-soft" 34 | version = "0.6.4" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" 37 | dependencies = [ 38 | "cipher", 39 | "opaque-debug", 40 | ] 41 | 42 | [[package]] 43 | name = "aesni" 44 | version = "0.10.0" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" 47 | dependencies = [ 48 | "cipher", 49 | "opaque-debug", 50 | ] 51 | 52 | [[package]] 53 | name = "ahash" 54 | version = "0.4.7" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" 57 | 58 | [[package]] 59 | name = "anyhow" 60 | version = "1.0.42" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" 63 | 64 | [[package]] 65 | name = "atomicwrites" 66 | version = "0.2.5" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "6a2baf2feb820299c53c7ad1cc4f5914a220a1cb76d7ce321d2522a94b54651f" 69 | dependencies = [ 70 | "nix", 71 | "tempdir", 72 | "winapi 0.3.9", 73 | ] 74 | 75 | [[package]] 76 | name = "autocfg" 77 | version = "0.1.7" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" 80 | 81 | [[package]] 82 | name = "autocfg" 83 | version = "1.0.1" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 86 | 87 | [[package]] 88 | name = "backtrace" 89 | version = "0.3.61" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01" 92 | dependencies = [ 93 | "addr2line", 94 | "cc", 95 | "cfg-if 1.0.0", 96 | "libc", 97 | "miniz_oxide", 98 | "object", 99 | "rustc-demangle", 100 | ] 101 | 102 | [[package]] 103 | name = "base64" 104 | version = "0.11.0" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" 107 | 108 | [[package]] 109 | name = "base64" 110 | version = "0.13.0" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 113 | 114 | [[package]] 115 | name = "bitflags" 116 | version = "1.2.1" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 119 | 120 | [[package]] 121 | name = "bitpacking" 122 | version = "0.8.4" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "a8c7d2ac73c167c06af4a5f37e6e59d84148d57ccbe4480b76f0273eefea82d7" 125 | dependencies = [ 126 | "crunchy", 127 | ] 128 | 129 | [[package]] 130 | name = "block-buffer" 131 | version = "0.9.0" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 134 | dependencies = [ 135 | "generic-array", 136 | ] 137 | 138 | [[package]] 139 | name = "byteorder" 140 | version = "1.4.3" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 143 | 144 | [[package]] 145 | name = "bytes" 146 | version = "1.0.1" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" 149 | 150 | [[package]] 151 | name = "cc" 152 | version = "1.0.69" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" 155 | 156 | [[package]] 157 | name = "census" 158 | version = "0.4.0" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "5927edd8345aef08578bcbb4aea7314f340d80c7f4931f99fbeb40b99d8f5060" 161 | 162 | [[package]] 163 | name = "cfg-if" 164 | version = "0.1.10" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 167 | 168 | [[package]] 169 | name = "cfg-if" 170 | version = "1.0.0" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 173 | 174 | [[package]] 175 | name = "chrono" 176 | version = "0.4.19" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 179 | dependencies = [ 180 | "libc", 181 | "num-integer", 182 | "num-traits", 183 | "time", 184 | "winapi 0.3.9", 185 | ] 186 | 187 | [[package]] 188 | name = "cipher" 189 | version = "0.2.5" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" 192 | dependencies = [ 193 | "generic-array", 194 | ] 195 | 196 | [[package]] 197 | name = "cloudabi" 198 | version = "0.0.3" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 201 | dependencies = [ 202 | "bitflags", 203 | ] 204 | 205 | [[package]] 206 | name = "codespan-reporting" 207 | version = "0.11.1" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 210 | dependencies = [ 211 | "termcolor", 212 | "unicode-width", 213 | ] 214 | 215 | [[package]] 216 | name = "combine" 217 | version = "4.6.0" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "a2d47c1b11006b87e492b53b313bb699ce60e16613c4dddaa91f8f7c220ab2fa" 220 | dependencies = [ 221 | "bytes", 222 | "memchr", 223 | ] 224 | 225 | [[package]] 226 | name = "cpufeatures" 227 | version = "0.1.5" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef" 230 | dependencies = [ 231 | "libc", 232 | ] 233 | 234 | [[package]] 235 | name = "crc32fast" 236 | version = "1.2.1" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" 239 | dependencies = [ 240 | "cfg-if 1.0.0", 241 | ] 242 | 243 | [[package]] 244 | name = "crossbeam" 245 | version = "0.7.3" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e" 248 | dependencies = [ 249 | "cfg-if 0.1.10", 250 | "crossbeam-channel 0.4.4", 251 | "crossbeam-deque 0.7.4", 252 | "crossbeam-epoch 0.8.2", 253 | "crossbeam-queue", 254 | "crossbeam-utils 0.7.2", 255 | ] 256 | 257 | [[package]] 258 | name = "crossbeam-channel" 259 | version = "0.4.4" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" 262 | dependencies = [ 263 | "crossbeam-utils 0.7.2", 264 | "maybe-uninit", 265 | ] 266 | 267 | [[package]] 268 | name = "crossbeam-channel" 269 | version = "0.5.1" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" 272 | dependencies = [ 273 | "cfg-if 1.0.0", 274 | "crossbeam-utils 0.8.5", 275 | ] 276 | 277 | [[package]] 278 | name = "crossbeam-deque" 279 | version = "0.7.4" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" 282 | dependencies = [ 283 | "crossbeam-epoch 0.8.2", 284 | "crossbeam-utils 0.7.2", 285 | "maybe-uninit", 286 | ] 287 | 288 | [[package]] 289 | name = "crossbeam-deque" 290 | version = "0.8.1" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" 293 | dependencies = [ 294 | "cfg-if 1.0.0", 295 | "crossbeam-epoch 0.9.5", 296 | "crossbeam-utils 0.8.5", 297 | ] 298 | 299 | [[package]] 300 | name = "crossbeam-epoch" 301 | version = "0.8.2" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" 304 | dependencies = [ 305 | "autocfg 1.0.1", 306 | "cfg-if 0.1.10", 307 | "crossbeam-utils 0.7.2", 308 | "lazy_static", 309 | "maybe-uninit", 310 | "memoffset 0.5.6", 311 | "scopeguard", 312 | ] 313 | 314 | [[package]] 315 | name = "crossbeam-epoch" 316 | version = "0.9.5" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" 319 | dependencies = [ 320 | "cfg-if 1.0.0", 321 | "crossbeam-utils 0.8.5", 322 | "lazy_static", 323 | "memoffset 0.6.4", 324 | "scopeguard", 325 | ] 326 | 327 | [[package]] 328 | name = "crossbeam-queue" 329 | version = "0.2.3" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" 332 | dependencies = [ 333 | "cfg-if 0.1.10", 334 | "crossbeam-utils 0.7.2", 335 | "maybe-uninit", 336 | ] 337 | 338 | [[package]] 339 | name = "crossbeam-utils" 340 | version = "0.7.2" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 343 | dependencies = [ 344 | "autocfg 1.0.1", 345 | "cfg-if 0.1.10", 346 | "lazy_static", 347 | ] 348 | 349 | [[package]] 350 | name = "crossbeam-utils" 351 | version = "0.8.5" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" 354 | dependencies = [ 355 | "cfg-if 1.0.0", 356 | "lazy_static", 357 | ] 358 | 359 | [[package]] 360 | name = "crunchy" 361 | version = "0.2.2" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 364 | 365 | [[package]] 366 | name = "crypto-mac" 367 | version = "0.10.1" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" 370 | dependencies = [ 371 | "generic-array", 372 | "subtle", 373 | ] 374 | 375 | [[package]] 376 | name = "ctr" 377 | version = "0.6.0" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" 380 | dependencies = [ 381 | "cipher", 382 | ] 383 | 384 | [[package]] 385 | name = "cxx" 386 | version = "1.0.51" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "5f787514a7ca5cc081700331c94f177ac7a7f0e9b97b0f2df1e176f414d782a4" 389 | dependencies = [ 390 | "cc", 391 | "cxxbridge-flags", 392 | "cxxbridge-macro", 393 | "link-cplusplus", 394 | ] 395 | 396 | [[package]] 397 | name = "cxx-build" 398 | version = "1.0.51" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "8e350548eb43a8914080d58cac995e50a9dc96af03979851c29645b610abe098" 401 | dependencies = [ 402 | "cc", 403 | "codespan-reporting", 404 | "lazy_static", 405 | "proc-macro2", 406 | "quote", 407 | "scratch", 408 | "syn", 409 | ] 410 | 411 | [[package]] 412 | name = "cxxbridge-flags" 413 | version = "1.0.51" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "13c4c8a0e13a48a978d6c62ab8e9227806434cfbaf2f7e9e336f167792ee0b20" 416 | 417 | [[package]] 418 | name = "cxxbridge-macro" 419 | version = "1.0.51" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "43a5246573c244d4467906250ec705e90e7ce9db30dc259c7c2a7ba70a35a1a7" 422 | dependencies = [ 423 | "proc-macro2", 424 | "quote", 425 | "syn", 426 | ] 427 | 428 | [[package]] 429 | name = "digest" 430 | version = "0.9.0" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 433 | dependencies = [ 434 | "generic-array", 435 | ] 436 | 437 | [[package]] 438 | name = "dirs" 439 | version = "3.0.2" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" 442 | dependencies = [ 443 | "dirs-sys", 444 | ] 445 | 446 | [[package]] 447 | name = "dirs-sys" 448 | version = "0.3.6" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" 451 | dependencies = [ 452 | "libc", 453 | "redox_users", 454 | "winapi 0.3.9", 455 | ] 456 | 457 | [[package]] 458 | name = "downcast-rs" 459 | version = "1.2.0" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 462 | 463 | [[package]] 464 | name = "either" 465 | version = "1.6.1" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 468 | 469 | [[package]] 470 | name = "fail" 471 | version = "0.3.0" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "f63eec71a3013ee912a0ecb339ff0c5fa5ed9660df04bfefa10c250b885d018c" 474 | dependencies = [ 475 | "lazy_static", 476 | "log", 477 | "rand 0.6.5", 478 | ] 479 | 480 | [[package]] 481 | name = "failure" 482 | version = "0.1.8" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" 485 | dependencies = [ 486 | "backtrace", 487 | "failure_derive", 488 | ] 489 | 490 | [[package]] 491 | name = "failure_derive" 492 | version = "0.1.8" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" 495 | dependencies = [ 496 | "proc-macro2", 497 | "quote", 498 | "syn", 499 | "synstructure", 500 | ] 501 | 502 | [[package]] 503 | name = "fallible-iterator" 504 | version = "0.2.0" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 507 | 508 | [[package]] 509 | name = "fallible-streaming-iterator" 510 | version = "0.1.9" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" 513 | 514 | [[package]] 515 | name = "filetime" 516 | version = "0.2.14" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" 519 | dependencies = [ 520 | "cfg-if 1.0.0", 521 | "libc", 522 | "redox_syscall", 523 | "winapi 0.3.9", 524 | ] 525 | 526 | [[package]] 527 | name = "fnv" 528 | version = "1.0.7" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 531 | 532 | [[package]] 533 | name = "fs2" 534 | version = "0.4.3" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" 537 | dependencies = [ 538 | "libc", 539 | "winapi 0.3.9", 540 | ] 541 | 542 | [[package]] 543 | name = "fs_extra" 544 | version = "1.2.0" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" 547 | 548 | [[package]] 549 | name = "fsevent" 550 | version = "0.4.0" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" 553 | dependencies = [ 554 | "bitflags", 555 | "fsevent-sys", 556 | ] 557 | 558 | [[package]] 559 | name = "fsevent-sys" 560 | version = "2.0.1" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" 563 | dependencies = [ 564 | "libc", 565 | ] 566 | 567 | [[package]] 568 | name = "fst" 569 | version = "0.3.5" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "927fb434ff9f0115b215dc0efd2e4fbdd7448522a92a1aa37c77d6a2f8f1ebd6" 572 | dependencies = [ 573 | "byteorder", 574 | ] 575 | 576 | [[package]] 577 | name = "fuchsia-cprng" 578 | version = "0.1.1" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 581 | 582 | [[package]] 583 | name = "fuchsia-zircon" 584 | version = "0.3.3" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 587 | dependencies = [ 588 | "bitflags", 589 | "fuchsia-zircon-sys", 590 | ] 591 | 592 | [[package]] 593 | name = "fuchsia-zircon-sys" 594 | version = "0.3.3" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 597 | 598 | [[package]] 599 | name = "futures" 600 | version = "0.3.16" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "1adc00f486adfc9ce99f77d717836f0c5aa84965eb0b4f051f4e83f7cab53f8b" 603 | dependencies = [ 604 | "futures-channel", 605 | "futures-core", 606 | "futures-executor", 607 | "futures-io", 608 | "futures-sink", 609 | "futures-task", 610 | "futures-util", 611 | ] 612 | 613 | [[package]] 614 | name = "futures-channel" 615 | version = "0.3.16" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "74ed2411805f6e4e3d9bc904c95d5d423b89b3b25dc0250aa74729de20629ff9" 618 | dependencies = [ 619 | "futures-core", 620 | "futures-sink", 621 | ] 622 | 623 | [[package]] 624 | name = "futures-core" 625 | version = "0.3.16" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99" 628 | 629 | [[package]] 630 | name = "futures-executor" 631 | version = "0.3.16" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "4d0d535a57b87e1ae31437b892713aee90cd2d7b0ee48727cd11fc72ef54761c" 634 | dependencies = [ 635 | "futures-core", 636 | "futures-task", 637 | "futures-util", 638 | "num_cpus", 639 | ] 640 | 641 | [[package]] 642 | name = "futures-io" 643 | version = "0.3.16" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "0b0e06c393068f3a6ef246c75cdca793d6a46347e75286933e5e75fd2fd11582" 646 | 647 | [[package]] 648 | name = "futures-macro" 649 | version = "0.3.16" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "c54913bae956fb8df7f4dc6fc90362aa72e69148e3f39041fbe8742d21e0ac57" 652 | dependencies = [ 653 | "autocfg 1.0.1", 654 | "proc-macro-hack", 655 | "proc-macro2", 656 | "quote", 657 | "syn", 658 | ] 659 | 660 | [[package]] 661 | name = "futures-sink" 662 | version = "0.3.16" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "c0f30aaa67363d119812743aa5f33c201a7a66329f97d1a887022971feea4b53" 665 | 666 | [[package]] 667 | name = "futures-task" 668 | version = "0.3.16" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "bbe54a98670017f3be909561f6ad13e810d9a51f3f061b902062ca3da80799f2" 671 | 672 | [[package]] 673 | name = "futures-util" 674 | version = "0.3.16" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78" 677 | dependencies = [ 678 | "autocfg 1.0.1", 679 | "futures-channel", 680 | "futures-core", 681 | "futures-io", 682 | "futures-macro", 683 | "futures-sink", 684 | "futures-task", 685 | "memchr", 686 | "pin-project-lite", 687 | "pin-utils", 688 | "proc-macro-hack", 689 | "proc-macro-nested", 690 | "slab", 691 | ] 692 | 693 | [[package]] 694 | name = "generic-array" 695 | version = "0.14.4" 696 | source = "registry+https://github.com/rust-lang/crates.io-index" 697 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 698 | dependencies = [ 699 | "typenum", 700 | "version_check", 701 | ] 702 | 703 | [[package]] 704 | name = "getrandom" 705 | version = "0.1.16" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 708 | dependencies = [ 709 | "cfg-if 1.0.0", 710 | "libc", 711 | "wasi 0.9.0+wasi-snapshot-preview1", 712 | ] 713 | 714 | [[package]] 715 | name = "getrandom" 716 | version = "0.2.3" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 719 | dependencies = [ 720 | "cfg-if 1.0.0", 721 | "libc", 722 | "wasi 0.10.2+wasi-snapshot-preview1", 723 | ] 724 | 725 | [[package]] 726 | name = "gimli" 727 | version = "0.25.0" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" 730 | 731 | [[package]] 732 | name = "hashbrown" 733 | version = "0.9.1" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 736 | dependencies = [ 737 | "ahash", 738 | ] 739 | 740 | [[package]] 741 | name = "hashlink" 742 | version = "0.6.0" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "d99cf782f0dc4372d26846bec3de7804ceb5df083c2d4462c0b8d2330e894fa8" 745 | dependencies = [ 746 | "hashbrown", 747 | ] 748 | 749 | [[package]] 750 | name = "hermit-abi" 751 | version = "0.1.19" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 754 | dependencies = [ 755 | "libc", 756 | ] 757 | 758 | [[package]] 759 | name = "hkdf" 760 | version = "0.10.0" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" 763 | dependencies = [ 764 | "digest", 765 | "hmac", 766 | ] 767 | 768 | [[package]] 769 | name = "hmac" 770 | version = "0.10.1" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" 773 | dependencies = [ 774 | "crypto-mac", 775 | "digest", 776 | ] 777 | 778 | [[package]] 779 | name = "htmlescape" 780 | version = "0.3.1" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" 783 | 784 | [[package]] 785 | name = "inotify" 786 | version = "0.7.1" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" 789 | dependencies = [ 790 | "bitflags", 791 | "inotify-sys", 792 | "libc", 793 | ] 794 | 795 | [[package]] 796 | name = "inotify-sys" 797 | version = "0.1.5" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" 800 | dependencies = [ 801 | "libc", 802 | ] 803 | 804 | [[package]] 805 | name = "instant" 806 | version = "0.1.10" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" 809 | dependencies = [ 810 | "cfg-if 1.0.0", 811 | ] 812 | 813 | [[package]] 814 | name = "iovec" 815 | version = "0.1.4" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 818 | dependencies = [ 819 | "libc", 820 | ] 821 | 822 | [[package]] 823 | name = "itertools" 824 | version = "0.8.2" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" 827 | dependencies = [ 828 | "either", 829 | ] 830 | 831 | [[package]] 832 | name = "itoa" 833 | version = "0.4.7" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 836 | 837 | [[package]] 838 | name = "kernel32-sys" 839 | version = "0.2.2" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 842 | dependencies = [ 843 | "winapi 0.2.8", 844 | "winapi-build", 845 | ] 846 | 847 | [[package]] 848 | name = "keytar" 849 | version = "0.1.4" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "df77696a9dbf25012c644b1ebe639c0ee5a996bae10c6b2d9d28874a84beefb0" 852 | dependencies = [ 853 | "keytar-sys", 854 | ] 855 | 856 | [[package]] 857 | name = "keytar-sys" 858 | version = "0.1.4" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "1ea10bbe9c8c05c6a66f587227c3147deba324d2287b6dc59f9d17a2229c1ffe" 861 | dependencies = [ 862 | "cc", 863 | "cxx", 864 | "cxx-build", 865 | "pkg-config", 866 | ] 867 | 868 | [[package]] 869 | name = "lazy_static" 870 | version = "1.4.0" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 873 | 874 | [[package]] 875 | name = "lazycell" 876 | version = "1.3.0" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 879 | 880 | [[package]] 881 | name = "levenshtein_automata" 882 | version = "0.1.1" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "73a004f877f468548d8d0ac4977456a249d8fabbdb8416c36db163dfc8f2e8ca" 885 | dependencies = [ 886 | "fst", 887 | ] 888 | 889 | [[package]] 890 | name = "libc" 891 | version = "0.2.98" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" 894 | 895 | [[package]] 896 | name = "libsqlite3-sys" 897 | version = "0.20.1" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "64d31059f22935e6c31830db5249ba2b7ecd54fd73a9909286f0a67aa55c2fbd" 900 | dependencies = [ 901 | "pkg-config", 902 | "vcpkg", 903 | ] 904 | 905 | [[package]] 906 | name = "link-cplusplus" 907 | version = "1.0.5" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "8f1becd27d473556dc610b8afa1636ef90747b574a84553bc11e82371d5ef2d1" 910 | dependencies = [ 911 | "cc", 912 | ] 913 | 914 | [[package]] 915 | name = "linked-hash-map" 916 | version = "0.5.4" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" 919 | 920 | [[package]] 921 | name = "lock_api" 922 | version = "0.4.4" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" 925 | dependencies = [ 926 | "scopeguard", 927 | ] 928 | 929 | [[package]] 930 | name = "log" 931 | version = "0.4.14" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 934 | dependencies = [ 935 | "cfg-if 1.0.0", 936 | ] 937 | 938 | [[package]] 939 | name = "lru-cache" 940 | version = "0.1.2" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" 943 | dependencies = [ 944 | "linked-hash-map", 945 | ] 946 | 947 | [[package]] 948 | name = "maplit" 949 | version = "1.0.2" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" 952 | 953 | [[package]] 954 | name = "maybe-uninit" 955 | version = "2.0.0" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 958 | 959 | [[package]] 960 | name = "memchr" 961 | version = "2.4.0" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" 964 | 965 | [[package]] 966 | name = "memmap" 967 | version = "0.7.0" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" 970 | dependencies = [ 971 | "libc", 972 | "winapi 0.3.9", 973 | ] 974 | 975 | [[package]] 976 | name = "memoffset" 977 | version = "0.5.6" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" 980 | dependencies = [ 981 | "autocfg 1.0.1", 982 | ] 983 | 984 | [[package]] 985 | name = "memoffset" 986 | version = "0.6.4" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" 989 | dependencies = [ 990 | "autocfg 1.0.1", 991 | ] 992 | 993 | [[package]] 994 | name = "miniz_oxide" 995 | version = "0.4.4" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 998 | dependencies = [ 999 | "adler", 1000 | "autocfg 1.0.1", 1001 | ] 1002 | 1003 | [[package]] 1004 | name = "mio" 1005 | version = "0.6.23" 1006 | source = "registry+https://github.com/rust-lang/crates.io-index" 1007 | checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" 1008 | dependencies = [ 1009 | "cfg-if 0.1.10", 1010 | "fuchsia-zircon", 1011 | "fuchsia-zircon-sys", 1012 | "iovec", 1013 | "kernel32-sys", 1014 | "libc", 1015 | "log", 1016 | "miow", 1017 | "net2", 1018 | "slab", 1019 | "winapi 0.2.8", 1020 | ] 1021 | 1022 | [[package]] 1023 | name = "mio-extras" 1024 | version = "2.0.6" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" 1027 | dependencies = [ 1028 | "lazycell", 1029 | "log", 1030 | "mio", 1031 | "slab", 1032 | ] 1033 | 1034 | [[package]] 1035 | name = "miow" 1036 | version = "0.2.2" 1037 | source = "registry+https://github.com/rust-lang/crates.io-index" 1038 | checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" 1039 | dependencies = [ 1040 | "kernel32-sys", 1041 | "net2", 1042 | "winapi 0.2.8", 1043 | "ws2_32-sys", 1044 | ] 1045 | 1046 | [[package]] 1047 | name = "murmurhash32" 1048 | version = "0.2.0" 1049 | source = "registry+https://github.com/rust-lang/crates.io-index" 1050 | checksum = "d736ff882f0e85fe9689fb23db229616c4c00aee2b3ac282f666d8f20eb25d4a" 1051 | dependencies = [ 1052 | "byteorder", 1053 | ] 1054 | 1055 | [[package]] 1056 | name = "net2" 1057 | version = "0.2.37" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" 1060 | dependencies = [ 1061 | "cfg-if 0.1.10", 1062 | "libc", 1063 | "winapi 0.3.9", 1064 | ] 1065 | 1066 | [[package]] 1067 | name = "nix" 1068 | version = "0.14.1" 1069 | source = "registry+https://github.com/rust-lang/crates.io-index" 1070 | checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" 1071 | dependencies = [ 1072 | "bitflags", 1073 | "cc", 1074 | "cfg-if 0.1.10", 1075 | "libc", 1076 | "void", 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "notify" 1081 | version = "4.0.17" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257" 1084 | dependencies = [ 1085 | "bitflags", 1086 | "filetime", 1087 | "fsevent", 1088 | "fsevent-sys", 1089 | "inotify", 1090 | "libc", 1091 | "mio", 1092 | "mio-extras", 1093 | "walkdir", 1094 | "winapi 0.3.9", 1095 | ] 1096 | 1097 | [[package]] 1098 | name = "num-integer" 1099 | version = "0.1.44" 1100 | source = "registry+https://github.com/rust-lang/crates.io-index" 1101 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 1102 | dependencies = [ 1103 | "autocfg 1.0.1", 1104 | "num-traits", 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "num-traits" 1109 | version = "0.2.14" 1110 | source = "registry+https://github.com/rust-lang/crates.io-index" 1111 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 1112 | dependencies = [ 1113 | "autocfg 1.0.1", 1114 | ] 1115 | 1116 | [[package]] 1117 | name = "num_cpus" 1118 | version = "1.13.0" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 1121 | dependencies = [ 1122 | "hermit-abi", 1123 | "libc", 1124 | ] 1125 | 1126 | [[package]] 1127 | name = "object" 1128 | version = "0.26.0" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "c55827317fb4c08822499848a14237d2874d6f139828893017237e7ab93eb386" 1131 | dependencies = [ 1132 | "memchr", 1133 | ] 1134 | 1135 | [[package]] 1136 | name = "once_cell" 1137 | version = "1.8.0" 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" 1139 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" 1140 | 1141 | [[package]] 1142 | name = "opaque-debug" 1143 | version = "0.3.0" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 1146 | 1147 | [[package]] 1148 | name = "owned-read" 1149 | version = "0.4.1" 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" 1151 | checksum = "b66d1e235abcebc845cf93550b89b74f468c051496fafb433ede4104b9f71ba1" 1152 | dependencies = [ 1153 | "stable_deref_trait", 1154 | ] 1155 | 1156 | [[package]] 1157 | name = "owning_ref" 1158 | version = "0.4.1" 1159 | source = "registry+https://github.com/rust-lang/crates.io-index" 1160 | checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" 1161 | dependencies = [ 1162 | "stable_deref_trait", 1163 | ] 1164 | 1165 | [[package]] 1166 | name = "parking_lot" 1167 | version = "0.11.1" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" 1170 | dependencies = [ 1171 | "instant", 1172 | "lock_api", 1173 | "parking_lot_core", 1174 | ] 1175 | 1176 | [[package]] 1177 | name = "parking_lot_core" 1178 | version = "0.8.3" 1179 | source = "registry+https://github.com/rust-lang/crates.io-index" 1180 | checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" 1181 | dependencies = [ 1182 | "cfg-if 1.0.0", 1183 | "instant", 1184 | "libc", 1185 | "redox_syscall", 1186 | "smallvec", 1187 | "winapi 0.3.9", 1188 | ] 1189 | 1190 | [[package]] 1191 | name = "pbkdf2" 1192 | version = "0.6.0" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "b3b8c0d71734018084da0c0354193a5edfb81b20d2d57a92c5b154aefc554a4a" 1195 | dependencies = [ 1196 | "base64 0.13.0", 1197 | "crypto-mac", 1198 | "hmac", 1199 | "rand 0.7.3", 1200 | "rand_core 0.5.1", 1201 | "sha2", 1202 | "subtle", 1203 | ] 1204 | 1205 | [[package]] 1206 | name = "pin-project-lite" 1207 | version = "0.2.7" 1208 | source = "registry+https://github.com/rust-lang/crates.io-index" 1209 | checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" 1210 | 1211 | [[package]] 1212 | name = "pin-utils" 1213 | version = "0.1.0" 1214 | source = "registry+https://github.com/rust-lang/crates.io-index" 1215 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1216 | 1217 | [[package]] 1218 | name = "pkg-config" 1219 | version = "0.3.19" 1220 | source = "registry+https://github.com/rust-lang/crates.io-index" 1221 | checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 1222 | 1223 | [[package]] 1224 | name = "ppv-lite86" 1225 | version = "0.2.10" 1226 | source = "registry+https://github.com/rust-lang/crates.io-index" 1227 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 1228 | 1229 | [[package]] 1230 | name = "proc-macro-hack" 1231 | version = "0.5.19" 1232 | source = "registry+https://github.com/rust-lang/crates.io-index" 1233 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 1234 | 1235 | [[package]] 1236 | name = "proc-macro-nested" 1237 | version = "0.1.7" 1238 | source = "registry+https://github.com/rust-lang/crates.io-index" 1239 | checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" 1240 | 1241 | [[package]] 1242 | name = "proc-macro2" 1243 | version = "1.0.28" 1244 | source = "registry+https://github.com/rust-lang/crates.io-index" 1245 | checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" 1246 | dependencies = [ 1247 | "unicode-xid", 1248 | ] 1249 | 1250 | [[package]] 1251 | name = "quote" 1252 | version = "1.0.9" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 1255 | dependencies = [ 1256 | "proc-macro2", 1257 | ] 1258 | 1259 | [[package]] 1260 | name = "r2d2" 1261 | version = "0.8.9" 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" 1263 | checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" 1264 | dependencies = [ 1265 | "log", 1266 | "parking_lot", 1267 | "scheduled-thread-pool", 1268 | ] 1269 | 1270 | [[package]] 1271 | name = "r2d2_sqlite" 1272 | version = "0.17.0" 1273 | source = "registry+https://github.com/rust-lang/crates.io-index" 1274 | checksum = "227ab35ff4cbb01fa76da8f062590fe677b93c8d9e8415eb5fa981f2c1dba9d8" 1275 | dependencies = [ 1276 | "r2d2", 1277 | "rusqlite", 1278 | ] 1279 | 1280 | [[package]] 1281 | name = "radical-native" 1282 | version = "0.1.0-beta.15" 1283 | dependencies = [ 1284 | "anyhow", 1285 | "base64 0.13.0", 1286 | "byteorder", 1287 | "dirs", 1288 | "keytar", 1289 | "rand 0.8.4", 1290 | "serde", 1291 | "serde_json", 1292 | "seshat", 1293 | "tempfile", 1294 | ] 1295 | 1296 | [[package]] 1297 | name = "rand" 1298 | version = "0.4.6" 1299 | source = "registry+https://github.com/rust-lang/crates.io-index" 1300 | checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" 1301 | dependencies = [ 1302 | "fuchsia-cprng", 1303 | "libc", 1304 | "rand_core 0.3.1", 1305 | "rdrand", 1306 | "winapi 0.3.9", 1307 | ] 1308 | 1309 | [[package]] 1310 | name = "rand" 1311 | version = "0.6.5" 1312 | source = "registry+https://github.com/rust-lang/crates.io-index" 1313 | checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" 1314 | dependencies = [ 1315 | "autocfg 0.1.7", 1316 | "libc", 1317 | "rand_chacha 0.1.1", 1318 | "rand_core 0.4.2", 1319 | "rand_hc 0.1.0", 1320 | "rand_isaac", 1321 | "rand_jitter", 1322 | "rand_os", 1323 | "rand_pcg", 1324 | "rand_xorshift", 1325 | "winapi 0.3.9", 1326 | ] 1327 | 1328 | [[package]] 1329 | name = "rand" 1330 | version = "0.7.3" 1331 | source = "registry+https://github.com/rust-lang/crates.io-index" 1332 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1333 | dependencies = [ 1334 | "rand_chacha 0.2.2", 1335 | "rand_core 0.5.1", 1336 | "rand_hc 0.2.0", 1337 | ] 1338 | 1339 | [[package]] 1340 | name = "rand" 1341 | version = "0.8.4" 1342 | source = "registry+https://github.com/rust-lang/crates.io-index" 1343 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 1344 | dependencies = [ 1345 | "libc", 1346 | "rand_chacha 0.3.1", 1347 | "rand_core 0.6.3", 1348 | "rand_hc 0.3.1", 1349 | ] 1350 | 1351 | [[package]] 1352 | name = "rand_chacha" 1353 | version = "0.1.1" 1354 | source = "registry+https://github.com/rust-lang/crates.io-index" 1355 | checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" 1356 | dependencies = [ 1357 | "autocfg 0.1.7", 1358 | "rand_core 0.3.1", 1359 | ] 1360 | 1361 | [[package]] 1362 | name = "rand_chacha" 1363 | version = "0.2.2" 1364 | source = "registry+https://github.com/rust-lang/crates.io-index" 1365 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1366 | dependencies = [ 1367 | "ppv-lite86", 1368 | "rand_core 0.5.1", 1369 | ] 1370 | 1371 | [[package]] 1372 | name = "rand_chacha" 1373 | version = "0.3.1" 1374 | source = "registry+https://github.com/rust-lang/crates.io-index" 1375 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1376 | dependencies = [ 1377 | "ppv-lite86", 1378 | "rand_core 0.6.3", 1379 | ] 1380 | 1381 | [[package]] 1382 | name = "rand_core" 1383 | version = "0.3.1" 1384 | source = "registry+https://github.com/rust-lang/crates.io-index" 1385 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 1386 | dependencies = [ 1387 | "rand_core 0.4.2", 1388 | ] 1389 | 1390 | [[package]] 1391 | name = "rand_core" 1392 | version = "0.4.2" 1393 | source = "registry+https://github.com/rust-lang/crates.io-index" 1394 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 1395 | 1396 | [[package]] 1397 | name = "rand_core" 1398 | version = "0.5.1" 1399 | source = "registry+https://github.com/rust-lang/crates.io-index" 1400 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1401 | dependencies = [ 1402 | "getrandom 0.1.16", 1403 | ] 1404 | 1405 | [[package]] 1406 | name = "rand_core" 1407 | version = "0.6.3" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 1410 | dependencies = [ 1411 | "getrandom 0.2.3", 1412 | ] 1413 | 1414 | [[package]] 1415 | name = "rand_hc" 1416 | version = "0.1.0" 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" 1418 | checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 1419 | dependencies = [ 1420 | "rand_core 0.3.1", 1421 | ] 1422 | 1423 | [[package]] 1424 | name = "rand_hc" 1425 | version = "0.2.0" 1426 | source = "registry+https://github.com/rust-lang/crates.io-index" 1427 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1428 | dependencies = [ 1429 | "rand_core 0.5.1", 1430 | ] 1431 | 1432 | [[package]] 1433 | name = "rand_hc" 1434 | version = "0.3.1" 1435 | source = "registry+https://github.com/rust-lang/crates.io-index" 1436 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 1437 | dependencies = [ 1438 | "rand_core 0.6.3", 1439 | ] 1440 | 1441 | [[package]] 1442 | name = "rand_isaac" 1443 | version = "0.1.1" 1444 | source = "registry+https://github.com/rust-lang/crates.io-index" 1445 | checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 1446 | dependencies = [ 1447 | "rand_core 0.3.1", 1448 | ] 1449 | 1450 | [[package]] 1451 | name = "rand_jitter" 1452 | version = "0.1.4" 1453 | source = "registry+https://github.com/rust-lang/crates.io-index" 1454 | checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" 1455 | dependencies = [ 1456 | "libc", 1457 | "rand_core 0.4.2", 1458 | "winapi 0.3.9", 1459 | ] 1460 | 1461 | [[package]] 1462 | name = "rand_os" 1463 | version = "0.1.3" 1464 | source = "registry+https://github.com/rust-lang/crates.io-index" 1465 | checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 1466 | dependencies = [ 1467 | "cloudabi", 1468 | "fuchsia-cprng", 1469 | "libc", 1470 | "rand_core 0.4.2", 1471 | "rdrand", 1472 | "winapi 0.3.9", 1473 | ] 1474 | 1475 | [[package]] 1476 | name = "rand_pcg" 1477 | version = "0.1.2" 1478 | source = "registry+https://github.com/rust-lang/crates.io-index" 1479 | checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" 1480 | dependencies = [ 1481 | "autocfg 0.1.7", 1482 | "rand_core 0.4.2", 1483 | ] 1484 | 1485 | [[package]] 1486 | name = "rand_xorshift" 1487 | version = "0.1.1" 1488 | source = "registry+https://github.com/rust-lang/crates.io-index" 1489 | checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" 1490 | dependencies = [ 1491 | "rand_core 0.3.1", 1492 | ] 1493 | 1494 | [[package]] 1495 | name = "rayon" 1496 | version = "1.5.1" 1497 | source = "registry+https://github.com/rust-lang/crates.io-index" 1498 | checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" 1499 | dependencies = [ 1500 | "autocfg 1.0.1", 1501 | "crossbeam-deque 0.8.1", 1502 | "either", 1503 | "rayon-core", 1504 | ] 1505 | 1506 | [[package]] 1507 | name = "rayon-core" 1508 | version = "1.9.1" 1509 | source = "registry+https://github.com/rust-lang/crates.io-index" 1510 | checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" 1511 | dependencies = [ 1512 | "crossbeam-channel 0.5.1", 1513 | "crossbeam-deque 0.8.1", 1514 | "crossbeam-utils 0.8.5", 1515 | "lazy_static", 1516 | "num_cpus", 1517 | ] 1518 | 1519 | [[package]] 1520 | name = "rdrand" 1521 | version = "0.4.0" 1522 | source = "registry+https://github.com/rust-lang/crates.io-index" 1523 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 1524 | dependencies = [ 1525 | "rand_core 0.3.1", 1526 | ] 1527 | 1528 | [[package]] 1529 | name = "redox_syscall" 1530 | version = "0.2.9" 1531 | source = "registry+https://github.com/rust-lang/crates.io-index" 1532 | checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" 1533 | dependencies = [ 1534 | "bitflags", 1535 | ] 1536 | 1537 | [[package]] 1538 | name = "redox_users" 1539 | version = "0.4.0" 1540 | source = "registry+https://github.com/rust-lang/crates.io-index" 1541 | checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" 1542 | dependencies = [ 1543 | "getrandom 0.2.3", 1544 | "redox_syscall", 1545 | ] 1546 | 1547 | [[package]] 1548 | name = "regex" 1549 | version = "1.5.4" 1550 | source = "registry+https://github.com/rust-lang/crates.io-index" 1551 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 1552 | dependencies = [ 1553 | "regex-syntax 0.6.25", 1554 | ] 1555 | 1556 | [[package]] 1557 | name = "regex-syntax" 1558 | version = "0.4.2" 1559 | source = "registry+https://github.com/rust-lang/crates.io-index" 1560 | checksum = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" 1561 | 1562 | [[package]] 1563 | name = "regex-syntax" 1564 | version = "0.6.25" 1565 | source = "registry+https://github.com/rust-lang/crates.io-index" 1566 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 1567 | 1568 | [[package]] 1569 | name = "remove_dir_all" 1570 | version = "0.5.3" 1571 | source = "registry+https://github.com/rust-lang/crates.io-index" 1572 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1573 | dependencies = [ 1574 | "winapi 0.3.9", 1575 | ] 1576 | 1577 | [[package]] 1578 | name = "rusqlite" 1579 | version = "0.24.2" 1580 | source = "registry+https://github.com/rust-lang/crates.io-index" 1581 | checksum = "d5f38ee71cbab2c827ec0ac24e76f82eca723cee92c509a65f67dee393c25112" 1582 | dependencies = [ 1583 | "bitflags", 1584 | "fallible-iterator", 1585 | "fallible-streaming-iterator", 1586 | "hashlink", 1587 | "libsqlite3-sys", 1588 | "memchr", 1589 | "smallvec", 1590 | ] 1591 | 1592 | [[package]] 1593 | name = "rust-stemmers" 1594 | version = "1.2.0" 1595 | source = "registry+https://github.com/rust-lang/crates.io-index" 1596 | checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" 1597 | dependencies = [ 1598 | "serde", 1599 | "serde_derive", 1600 | ] 1601 | 1602 | [[package]] 1603 | name = "rustc-demangle" 1604 | version = "0.1.20" 1605 | source = "registry+https://github.com/rust-lang/crates.io-index" 1606 | checksum = "dead70b0b5e03e9c814bcb6b01e03e68f7c57a80aa48c72ec92152ab3e818d49" 1607 | 1608 | [[package]] 1609 | name = "ryu" 1610 | version = "1.0.5" 1611 | source = "registry+https://github.com/rust-lang/crates.io-index" 1612 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1613 | 1614 | [[package]] 1615 | name = "same-file" 1616 | version = "1.0.6" 1617 | source = "registry+https://github.com/rust-lang/crates.io-index" 1618 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1619 | dependencies = [ 1620 | "winapi-util", 1621 | ] 1622 | 1623 | [[package]] 1624 | name = "scheduled-thread-pool" 1625 | version = "0.2.5" 1626 | source = "registry+https://github.com/rust-lang/crates.io-index" 1627 | checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" 1628 | dependencies = [ 1629 | "parking_lot", 1630 | ] 1631 | 1632 | [[package]] 1633 | name = "scopeguard" 1634 | version = "1.1.0" 1635 | source = "registry+https://github.com/rust-lang/crates.io-index" 1636 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1637 | 1638 | [[package]] 1639 | name = "scratch" 1640 | version = "1.0.0" 1641 | source = "registry+https://github.com/rust-lang/crates.io-index" 1642 | checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69" 1643 | 1644 | [[package]] 1645 | name = "serde" 1646 | version = "1.0.118" 1647 | source = "registry+https://github.com/rust-lang/crates.io-index" 1648 | checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" 1649 | dependencies = [ 1650 | "serde_derive", 1651 | ] 1652 | 1653 | [[package]] 1654 | name = "serde_derive" 1655 | version = "1.0.118" 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" 1657 | checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" 1658 | dependencies = [ 1659 | "proc-macro2", 1660 | "quote", 1661 | "syn", 1662 | ] 1663 | 1664 | [[package]] 1665 | name = "serde_json" 1666 | version = "1.0.61" 1667 | source = "registry+https://github.com/rust-lang/crates.io-index" 1668 | checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" 1669 | dependencies = [ 1670 | "itoa", 1671 | "ryu", 1672 | "serde", 1673 | ] 1674 | 1675 | [[package]] 1676 | name = "seshat" 1677 | version = "2.3.0" 1678 | source = "registry+https://github.com/rust-lang/crates.io-index" 1679 | checksum = "14482a038f94226556cc68e967d1046d70af80cc61de30401d58b2538e1cc850" 1680 | dependencies = [ 1681 | "aes-ctr", 1682 | "byteorder", 1683 | "crypto-mac", 1684 | "fs_extra", 1685 | "hkdf", 1686 | "hmac", 1687 | "lru-cache", 1688 | "pbkdf2", 1689 | "r2d2", 1690 | "r2d2_sqlite", 1691 | "rand 0.8.4", 1692 | "rusqlite", 1693 | "serde", 1694 | "serde_json", 1695 | "sha2", 1696 | "tantivy", 1697 | "thiserror", 1698 | "tinysegmenter", 1699 | "uuid", 1700 | "zeroize", 1701 | ] 1702 | 1703 | [[package]] 1704 | name = "sha2" 1705 | version = "0.9.5" 1706 | source = "registry+https://github.com/rust-lang/crates.io-index" 1707 | checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" 1708 | dependencies = [ 1709 | "block-buffer", 1710 | "cfg-if 1.0.0", 1711 | "cpufeatures", 1712 | "digest", 1713 | "opaque-debug", 1714 | ] 1715 | 1716 | [[package]] 1717 | name = "slab" 1718 | version = "0.4.3" 1719 | source = "registry+https://github.com/rust-lang/crates.io-index" 1720 | checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" 1721 | 1722 | [[package]] 1723 | name = "smallvec" 1724 | version = "1.6.1" 1725 | source = "registry+https://github.com/rust-lang/crates.io-index" 1726 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 1727 | 1728 | [[package]] 1729 | name = "snap" 1730 | version = "1.0.5" 1731 | source = "registry+https://github.com/rust-lang/crates.io-index" 1732 | checksum = "45456094d1983e2ee2a18fdfebce3189fa451699d0502cb8e3b49dba5ba41451" 1733 | 1734 | [[package]] 1735 | name = "stable_deref_trait" 1736 | version = "1.2.0" 1737 | source = "registry+https://github.com/rust-lang/crates.io-index" 1738 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1739 | 1740 | [[package]] 1741 | name = "subtle" 1742 | version = "2.4.1" 1743 | source = "registry+https://github.com/rust-lang/crates.io-index" 1744 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 1745 | 1746 | [[package]] 1747 | name = "syn" 1748 | version = "1.0.74" 1749 | source = "registry+https://github.com/rust-lang/crates.io-index" 1750 | checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" 1751 | dependencies = [ 1752 | "proc-macro2", 1753 | "quote", 1754 | "unicode-xid", 1755 | ] 1756 | 1757 | [[package]] 1758 | name = "synstructure" 1759 | version = "0.12.5" 1760 | source = "registry+https://github.com/rust-lang/crates.io-index" 1761 | checksum = "474aaa926faa1603c40b7885a9eaea29b444d1cb2850cb7c0e37bb1a4182f4fa" 1762 | dependencies = [ 1763 | "proc-macro2", 1764 | "quote", 1765 | "syn", 1766 | "unicode-xid", 1767 | ] 1768 | 1769 | [[package]] 1770 | name = "tantivy" 1771 | version = "0.12.0" 1772 | source = "registry+https://github.com/rust-lang/crates.io-index" 1773 | checksum = "02e1d2fbfa82ab829208e5f03f4d2c177b8a126252ab4f80ed232e1064770efb" 1774 | dependencies = [ 1775 | "atomicwrites", 1776 | "base64 0.11.0", 1777 | "bitpacking", 1778 | "byteorder", 1779 | "census", 1780 | "chrono", 1781 | "crc32fast", 1782 | "crossbeam", 1783 | "downcast-rs", 1784 | "fail", 1785 | "failure", 1786 | "fnv", 1787 | "fs2", 1788 | "futures", 1789 | "htmlescape", 1790 | "itertools", 1791 | "levenshtein_automata", 1792 | "log", 1793 | "memmap", 1794 | "murmurhash32", 1795 | "notify", 1796 | "num_cpus", 1797 | "once_cell", 1798 | "owned-read", 1799 | "owning_ref", 1800 | "rayon", 1801 | "regex", 1802 | "rust-stemmers", 1803 | "serde", 1804 | "serde_derive", 1805 | "serde_json", 1806 | "smallvec", 1807 | "snap", 1808 | "stable_deref_trait", 1809 | "tantivy-fst", 1810 | "tantivy-query-grammar", 1811 | "tempfile", 1812 | "uuid", 1813 | "winapi 0.3.9", 1814 | ] 1815 | 1816 | [[package]] 1817 | name = "tantivy-fst" 1818 | version = "0.2.1" 1819 | source = "registry+https://github.com/rust-lang/crates.io-index" 1820 | checksum = "38878efb477cf2efb7d9112b12b230c27d32abdfec4bea5e66095733f2928610" 1821 | dependencies = [ 1822 | "byteorder", 1823 | "levenshtein_automata", 1824 | "regex-syntax 0.4.2", 1825 | "utf8-ranges", 1826 | ] 1827 | 1828 | [[package]] 1829 | name = "tantivy-query-grammar" 1830 | version = "0.12.0" 1831 | source = "registry+https://github.com/rust-lang/crates.io-index" 1832 | checksum = "900f098da37d350b0e8f116791b9ee43e600704cb6b5cc83b7f826d1b119f21c" 1833 | dependencies = [ 1834 | "combine", 1835 | ] 1836 | 1837 | [[package]] 1838 | name = "tempdir" 1839 | version = "0.3.7" 1840 | source = "registry+https://github.com/rust-lang/crates.io-index" 1841 | checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" 1842 | dependencies = [ 1843 | "rand 0.4.6", 1844 | "remove_dir_all", 1845 | ] 1846 | 1847 | [[package]] 1848 | name = "tempfile" 1849 | version = "3.2.0" 1850 | source = "registry+https://github.com/rust-lang/crates.io-index" 1851 | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" 1852 | dependencies = [ 1853 | "cfg-if 1.0.0", 1854 | "libc", 1855 | "rand 0.8.4", 1856 | "redox_syscall", 1857 | "remove_dir_all", 1858 | "winapi 0.3.9", 1859 | ] 1860 | 1861 | [[package]] 1862 | name = "termcolor" 1863 | version = "1.1.2" 1864 | source = "registry+https://github.com/rust-lang/crates.io-index" 1865 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 1866 | dependencies = [ 1867 | "winapi-util", 1868 | ] 1869 | 1870 | [[package]] 1871 | name = "thiserror" 1872 | version = "1.0.26" 1873 | source = "registry+https://github.com/rust-lang/crates.io-index" 1874 | checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" 1875 | dependencies = [ 1876 | "thiserror-impl", 1877 | ] 1878 | 1879 | [[package]] 1880 | name = "thiserror-impl" 1881 | version = "1.0.26" 1882 | source = "registry+https://github.com/rust-lang/crates.io-index" 1883 | checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" 1884 | dependencies = [ 1885 | "proc-macro2", 1886 | "quote", 1887 | "syn", 1888 | ] 1889 | 1890 | [[package]] 1891 | name = "time" 1892 | version = "0.1.43" 1893 | source = "registry+https://github.com/rust-lang/crates.io-index" 1894 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 1895 | dependencies = [ 1896 | "libc", 1897 | "winapi 0.3.9", 1898 | ] 1899 | 1900 | [[package]] 1901 | name = "tinysegmenter" 1902 | version = "0.1.1" 1903 | source = "registry+https://github.com/rust-lang/crates.io-index" 1904 | checksum = "1755695d17d470baf2d937a59ab4e86de3034b056fc8700e21411b0efca36497" 1905 | dependencies = [ 1906 | "lazy_static", 1907 | "maplit", 1908 | ] 1909 | 1910 | [[package]] 1911 | name = "typenum" 1912 | version = "1.13.0" 1913 | source = "registry+https://github.com/rust-lang/crates.io-index" 1914 | checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" 1915 | 1916 | [[package]] 1917 | name = "unicode-width" 1918 | version = "0.1.8" 1919 | source = "registry+https://github.com/rust-lang/crates.io-index" 1920 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 1921 | 1922 | [[package]] 1923 | name = "unicode-xid" 1924 | version = "0.2.2" 1925 | source = "registry+https://github.com/rust-lang/crates.io-index" 1926 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1927 | 1928 | [[package]] 1929 | name = "utf8-ranges" 1930 | version = "1.0.4" 1931 | source = "registry+https://github.com/rust-lang/crates.io-index" 1932 | checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" 1933 | 1934 | [[package]] 1935 | name = "uuid" 1936 | version = "0.8.2" 1937 | source = "registry+https://github.com/rust-lang/crates.io-index" 1938 | checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" 1939 | dependencies = [ 1940 | "getrandom 0.2.3", 1941 | "serde", 1942 | ] 1943 | 1944 | [[package]] 1945 | name = "vcpkg" 1946 | version = "0.2.15" 1947 | source = "registry+https://github.com/rust-lang/crates.io-index" 1948 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1949 | 1950 | [[package]] 1951 | name = "version_check" 1952 | version = "0.9.3" 1953 | source = "registry+https://github.com/rust-lang/crates.io-index" 1954 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 1955 | 1956 | [[package]] 1957 | name = "void" 1958 | version = "1.0.2" 1959 | source = "registry+https://github.com/rust-lang/crates.io-index" 1960 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 1961 | 1962 | [[package]] 1963 | name = "walkdir" 1964 | version = "2.3.2" 1965 | source = "registry+https://github.com/rust-lang/crates.io-index" 1966 | checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" 1967 | dependencies = [ 1968 | "same-file", 1969 | "winapi 0.3.9", 1970 | "winapi-util", 1971 | ] 1972 | 1973 | [[package]] 1974 | name = "wasi" 1975 | version = "0.9.0+wasi-snapshot-preview1" 1976 | source = "registry+https://github.com/rust-lang/crates.io-index" 1977 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 1978 | 1979 | [[package]] 1980 | name = "wasi" 1981 | version = "0.10.2+wasi-snapshot-preview1" 1982 | source = "registry+https://github.com/rust-lang/crates.io-index" 1983 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1984 | 1985 | [[package]] 1986 | name = "winapi" 1987 | version = "0.2.8" 1988 | source = "registry+https://github.com/rust-lang/crates.io-index" 1989 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1990 | 1991 | [[package]] 1992 | name = "winapi" 1993 | version = "0.3.9" 1994 | source = "registry+https://github.com/rust-lang/crates.io-index" 1995 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1996 | dependencies = [ 1997 | "winapi-i686-pc-windows-gnu", 1998 | "winapi-x86_64-pc-windows-gnu", 1999 | ] 2000 | 2001 | [[package]] 2002 | name = "winapi-build" 2003 | version = "0.1.1" 2004 | source = "registry+https://github.com/rust-lang/crates.io-index" 2005 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 2006 | 2007 | [[package]] 2008 | name = "winapi-i686-pc-windows-gnu" 2009 | version = "0.4.0" 2010 | source = "registry+https://github.com/rust-lang/crates.io-index" 2011 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2012 | 2013 | [[package]] 2014 | name = "winapi-util" 2015 | version = "0.1.5" 2016 | source = "registry+https://github.com/rust-lang/crates.io-index" 2017 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 2018 | dependencies = [ 2019 | "winapi 0.3.9", 2020 | ] 2021 | 2022 | [[package]] 2023 | name = "winapi-x86_64-pc-windows-gnu" 2024 | version = "0.4.0" 2025 | source = "registry+https://github.com/rust-lang/crates.io-index" 2026 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2027 | 2028 | [[package]] 2029 | name = "ws2_32-sys" 2030 | version = "0.2.1" 2031 | source = "registry+https://github.com/rust-lang/crates.io-index" 2032 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 2033 | dependencies = [ 2034 | "winapi 0.2.8", 2035 | "winapi-build", 2036 | ] 2037 | 2038 | [[package]] 2039 | name = "zeroize" 2040 | version = "1.4.1" 2041 | source = "registry+https://github.com/rust-lang/crates.io-index" 2042 | checksum = "377db0846015f7ae377174787dd452e1c5f5a9050bc6f954911d01f116daa0cd" 2043 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "native" 4 | ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 stoically@protonmail.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Radical Native 2 | 3 | [![Radical Native Matrix room #radical-webext:matrix.org](https://img.shields.io/badge/matrix-%23radical--webext%3Amatrix.org-blue)](https://matrix.to/#/#radical-webext:matrix.org) 4 | 5 | Extending [Riot Web](https://github.com/vector-im/riot-web) with native capabilities 6 | 7 | #### Features 8 | 9 | - [x] Search functionality in encrypted rooms using [seshat](https://github.com/matrix-org/seshat) 10 | - [x] Secure OS key storage for pickle keys using [keytar](https://github.com/atom/node-keytar) 11 | - [ ] OS global keyboard shortcuts (e.g. push to talk) 12 | - [ ] Tray icon 13 | 14 | #### Supported Browsers 15 | 16 | - [x] Firefox 17 | - [ ] Chrome 18 | 19 | ## Install 20 | 21 | ### 1. Radical Native 22 | 23 | - Ubuntu/Debian: [`.deb`](https://github.com/stoically/radical-native/releases) 24 | - Arch: [`AUR`](https://aur.archlinux.org/packages/radical-native-bin/) 25 | - MacOS: [`.pkg`](https://github.com/stoically/radical-native/releases) 26 | - Note: Requires Ctrl+Click on the `.pkg` since the installer isn't signed yet 27 | - Windows: [`.exe`](https://github.com/stoically/radical-native/releases) 28 | 29 | Hint: The event store is saved into the `radical-native` directory inside your [local user data directory](https://github.com/soc/dirs-rs#features). 30 | 31 | ### 2. Radical Native Add-on 32 | 33 | - Firefox: [`.xpi`](https://github.com/stoically/radical-native/releases) 34 | 35 | The Radical Native Firefox Add-on facilitates the communication between Riot Web and the Radical Native Binary. 36 | 37 | ## Usage 38 | 39 | - Open any Riot website in your browser 40 | - Click the Radical Native icon in the toolbar (RAM icon) 41 | - Riot website should reload and the toolbar icon should have an "on" badge 42 | - Check Riot's "Settings > Security & Privacy > Message search > Manage", it should show ongoing work 43 | 44 | ## Troubleshooting 45 | 46 | - Try to execute the `radical-native` binary directly - it should respond with "ready: true" 47 | - Check the Radical Native Add-on console for error logs: `about:debugging#/runtime/this-firefox` > Radical Native Inspect 48 | - If indexing gets stuck you can safely disable and enable it in the "Manage" dialog 49 | 50 | ## Development 51 | 52 | - `cargo install cargo-watch` 53 | - Ubuntu/Debian: `apt install libsqlcipher0 libsqlcipher-dev libsecret-1-dev` 54 | - MacOS: `brew install libsqlcipher` 55 | 56 | ``` 57 | npm install 58 | npm run dev 59 | ``` 60 | 61 | ### Firefox 62 | 63 | - Load the build located in `build/firefox` as Temporary Add-on via 64 | `about:debugging#/runtime/this-firefox` 65 | 66 | ### Chrome 67 | 68 | - Load the build located in `build/chrome` as Unpacked extension via `chrome://extensions/` 69 | 70 | ## Tests 71 | 72 | ```shell 73 | # watcher 74 | npm run test:watch 75 | 76 | # once 77 | npm run test 78 | 79 | # once & coverage 80 | npm run test:coverage 81 | ``` 82 | 83 | ## Attribution 84 | 85 | Icon made by [Freepik](https://www.flaticon.com/authors/freepik) from [www.flaticon.com](https://www.flaticon.com/) 86 | -------------------------------------------------------------------------------- /native/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /native/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "radical-native" 3 | description = "Extending Riot Web with native capabilities" 4 | authors = ["stoically "] 5 | version = "0.1.0-beta.15" 6 | edition = "2018" 7 | license = "MIT" 8 | 9 | [dependencies] 10 | anyhow = "1.0.42" 11 | base64 = "0.13.0" 12 | byteorder = "1.4.3" 13 | dirs = "3.0.2" 14 | keytar = "0.1.4" 15 | rand = "0.8.4" 16 | # Pinned because of https://github.com/matrix-org/seshat/blob/632075eeb03e11875d72382b701a9bd3f53e1f35/Cargo.toml#L36-L43 17 | serde = "=1.0.118" 18 | serde_json = "=1.0.61" 19 | seshat = "2.3.0" 20 | 21 | [dev-dependencies] 22 | tempfile = "3.2.0" 23 | 24 | [package.metadata.deb] 25 | depends = "libsecret-1-0, libsqlcipher0" 26 | section = "utils" 27 | extended-description = """\ 28 | This is a host application for Radical Native browser extension that enhances Riot Web with native capabilities. 29 | The communication is handled through Native Messaging API. 30 | . 31 | Features 32 | Search functionality in encrypted rooms using seshat 33 | Secure OS key storage for pickle keys using keytar\ 34 | """ 35 | 36 | assets = [ 37 | ["target/release/radical-native", "/usr/bin/", "755"], 38 | ["target/release/radical.native.json", "/usr/lib/mozilla/native-messaging-hosts/", "644"] 39 | ] 40 | -------------------------------------------------------------------------------- /native/res/darwin/Distribution: -------------------------------------------------------------------------------- 1 | 2 | 3 | Radical Native 4 | 5 | 6 | 7 | 8 | 9 | 10 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | radical-native.pkg 34 | 35 | -------------------------------------------------------------------------------- /native/res/darwin/Resources/conclusion.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Radical Native

10 |

Thank you for installing Radical Native.

11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /native/res/darwin/Resources/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 |

This will install Radical Native on your computer.

10 |
11 |

Click “Continue" to continue the setup

12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /native/res/darwin/scripts/postinstall: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p /Library/Application\ Support/Mozilla/NativeMessagingHosts 4 | ln -nfs /Library/RadicalNative/radical.native.json /Library/Application\ Support/Mozilla/NativeMessagingHosts -------------------------------------------------------------------------------- /native/res/win.nsi: -------------------------------------------------------------------------------- 1 | !include "WordFunc.nsh" 2 | 3 | Name "Radical Native" 4 | Outfile ..\..\target\release\radical-native-installer.exe 5 | Unicode true 6 | InstallDir $PROGRAMFILES64\radical-native 7 | 8 | Section 9 | ExecWait "TaskKill /IM radical-native.exe /F" 10 | 11 | SetOutPath $INSTDIR 12 | CreateDirectory $INSTDIR 13 | 14 | File ..\..\target\release\radical-native.exe 15 | 16 | ${WordReplace} $INSTDIR "\" "\\" "+" $R0 17 | FileOpen $0 "$INSTDIR\radical.native.json" w 18 | FileWrite $0 '{$\r$\n\ 19 | "name": "radical.native",$\r$\n\ 20 | "description": "Radical Native",$\r$\n\ 21 | "path": "$R0\\radical-native.exe",$\r$\n\ 22 | "type": "stdio",$\r$\n\ 23 | "allowed_extensions": [ "@radical-native", "@riot-webext" ]$\r$\n\ 24 | }' 25 | FileClose $0 26 | 27 | SetRegView 64 28 | WriteRegStr HKLM "Software\Mozilla\NativeMessagingHosts\radical.native" "" "$INSTDIR\radical.native.json" 29 | 30 | WriteUninstaller $INSTDIR\uninstaller.exe 31 | SectionEnd 32 | 33 | Section "Uninstall" 34 | ExecWait "TaskKill /IM radical-native.exe /F" 35 | Delete $INSTDIR\uninstaller.exe 36 | Delete $INSTDIR\radical-native.exe 37 | Delete $INSTDIR\radical.native.json 38 | DeleteRegKey HKLM "Software\Mozilla\NativeMessagingHosts\radical.native" 39 | RMDir $INSTDIR 40 | SectionEnd -------------------------------------------------------------------------------- /native/scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # based on https://github.com/vector-im/riot-desktop/tree/master/hak/matrix-seshat 3 | 4 | set -e 5 | 6 | NASM_VERSION="2.14.02" 7 | OPENSSL_VERSION="1.1.1f" 8 | SQLCIPHER_VERSION="4.3.0" 9 | 10 | mkdir -p target/release 11 | mkdir -p build/native/opt 12 | 13 | OPT_DIR="$PWD/build/native/opt" 14 | export OPT_DIR 15 | 16 | fetch_openssl() { 17 | [ -d "openssl-${OPENSSL_VERSION}" ] && return 18 | 19 | curl -L -o openssl.tgz "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz" 20 | tar xzf openssl.tgz 21 | } 22 | 23 | fetch_sqlcipher() { 24 | [ -d "sqlcipher-${SQLCIPHER_VERSION}" ] && return 25 | 26 | curl -L -o sqlcipher.tgz "https://github.com/sqlcipher/sqlcipher/archive/v${SQLCIPHER_VERSION}.tar.gz" 27 | tar xzf sqlcipher.tgz 28 | } 29 | 30 | native_manifest() { 31 | cat <<-END 32 | { 33 | "name": "radical.native", 34 | "description": "Radical Native", 35 | "path": "$1", 36 | "type": "stdio", 37 | "allowed_extensions": [ "@radical-native", "@riot-webext" ] 38 | } 39 | END 40 | } 41 | 42 | linux() { 43 | native_manifest "/usr/bin/radical-native" > target/release/radical.native.json 44 | cargo test 45 | cargo deb -p radical-native --output target/release/radical-native.deb 46 | } 47 | 48 | macos_build_sqlcipher() { 49 | cd sqlcipher-${SQLCIPHER_VERSION} || exit 1 50 | 51 | CFLAGS=-DSQLITE_HAS_CODEC \ 52 | LDFLAGS="-framework Security -framework Foundation" \ 53 | ./configure \ 54 | --prefix="${OPT_DIR}" \ 55 | --enable-tempstore=yes \ 56 | --enable-shared=no \ 57 | --with-crypto-lib=commoncrypto 58 | 59 | make 60 | make install 61 | 62 | cd .. 63 | } 64 | 65 | macos() { 66 | pushd build/native || exit 1 67 | fetch_sqlcipher 68 | macos_build_sqlcipher 69 | popd 70 | 71 | RUSTFLAGS="-L${OPT_DIR}/lib" 72 | export RUSTFLAGS 73 | 74 | cargo test 75 | cargo build --release 76 | 77 | NATIVE_RES_PATH="./native/res/darwin" 78 | PKG_PATH="./build/native/pkg" 79 | PKG_ROOT_PATH="${PKG_PATH}/root" 80 | PKG_INST_PATH="${PKG_ROOT_PATH}/Library/RadicalNative" 81 | PKG_PACKAGE_PATH="${PKG_PATH}/package" 82 | rm -rf "${PKG_PATH}" 83 | mkdir -p "${PKG_PATH}" "${PKG_INST_PATH}" "${PKG_PACKAGE_PATH}" 84 | 85 | native_manifest "/Library/RadicalNative/radical-native" > "${PKG_INST_PATH}/radical.native.json" 86 | cp ./target/release/radical-native "${PKG_INST_PATH}" 87 | 88 | pkgbuild --identifier io.github.stoically.radical-native \ 89 | --version 0.1.0 \ 90 | --scripts "${NATIVE_RES_PATH}/scripts" \ 91 | --root "${PKG_ROOT_PATH}" \ 92 | "${PKG_PACKAGE_PATH}/radical-native.pkg" 93 | 94 | productbuild --distribution "${NATIVE_RES_PATH}/Distribution" \ 95 | --resources "${NATIVE_RES_PATH}/Resources" \ 96 | --package-path "${PKG_PACKAGE_PATH}" \ 97 | ./target/release/radical-native.pkg 98 | } 99 | 100 | win_install_nasm() { 101 | if [ ! -d "nasm-${NASM_VERSION}" ]; then 102 | curl -L -o nasm.zip "https://www.nasm.us/pub/nasm/releasebuilds/${NASM_VERSION}/win64/nasm-${NASM_VERSION}-win64.zip" 103 | unzip nasm.zip 104 | fi 105 | 106 | PATH="$PATH:$(realpath nasm-${NASM_VERSION})" 107 | export PATH 108 | } 109 | 110 | win_vcvarsall() { 111 | vcvarsallbat=$(find /c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/ -name vcvarsall.bat) 112 | while read -r line; do 113 | export "${line?}" || continue 114 | done < <(cmd.exe //q //c "$vcvarsallbat" x86_amd64 \> nul \&\& env | dos2unix) 115 | } 116 | 117 | win_build_openssl() { 118 | cd openssl-${OPENSSL_VERSION} || exit 1 119 | 120 | /c/Strawberry/perl/bin/perl.exe Configure --prefix="${OPT_DIR}" \ 121 | no-afalgeng no-capieng no-cms no-ct no-deprecated no-dgram \ 122 | no-dso no-ec no-ec2m no-gost no-nextprotoneg no-ocsp no-sock \ 123 | no-srp no-srtp no-tests no-ssl no-tls no-dtls no-shared no-aria \ 124 | no-camellia no-cast no-chacha no-cmac no-des no-dh no-dsa no-ecdh \ 125 | no-ecdsa no-idea no-md4 no-mdc2 no-ocb no-poly1305 no-rc2 no-rc4 \ 126 | no-rmd160 no-scrypt no-seed no-siphash no-sm2 no-sm3 no-sm4 no-whirlpool \ 127 | VC-WIN64A 128 | 129 | nmake build_libs 130 | nmake install_dev 131 | 132 | cd .. 133 | } 134 | 135 | win_build_sqlcipher() { 136 | cd sqlcipher-${SQLCIPHER_VERSION} || exit 1 137 | 138 | CCOPTS="-DSQLITE_HAS_CODEC -I ${OPT_WIN_DIR}\include" \ 139 | LTLIBPATHS="/LIBPATH:${OPT_WIN_DIR}\lib" \ 140 | LTLIBS="libcrypto.lib" \ 141 | nmake //f Makefile.msc libsqlite3.lib 142 | 143 | cp libsqlite3.lib "${OPT_DIR}/lib/sqlcipher.lib" 144 | cp sqlite3.h "${OPT_DIR}/include/sqlcipher.h" 145 | 146 | cd .. 147 | } 148 | 149 | # locally tested with 150 | # - Windows 10 Pro VM on x86_amd64 151 | # - Git for Windows bash (MINGW64) 152 | # - Visual Studio Build Tools 2019 153 | # + C++ build tools 154 | # + MSVC VS 2019 155 | # + Windows 10 SDK 156 | # + C++ CMake tools 157 | # - NSIS 3 158 | win() { 159 | # https://stackoverflow.com/a/13701495 160 | OPT_WIN_DIR=$(echo "${OPT_DIR}" | sed -e 's/^\///' -e 's/\//\\/g' -e 's/^./\0:/') 161 | export OPT_WIN_DIR 162 | 163 | pushd build/native || exit 1 164 | fetch_openssl 165 | fetch_sqlcipher 166 | win_vcvarsall 167 | win_install_nasm 168 | win_build_openssl 169 | win_build_sqlcipher 170 | popd 171 | 172 | SQLCIPHER_STATIC="1" 173 | SQLCIPHER_LIB_DIR="${OPT_WIN_DIR}\lib" 174 | SQLCIPHER_INCLUDE_DIR="${OPT_WIN_DIR}\include" 175 | export SQLCIPHER_STATIC SQLCIPHER_LIB_DIR SQLCIPHER_INCLUDE_DIR 176 | 177 | RUSTFLAGS="-Ctarget-feature=+crt-static -Clink-args=libcrypto.lib -L${OPT_WIN_DIR}\lib" 178 | RUSTUP_TOOLCHAIN="stable-x86_64-pc-windows-msvc" 179 | export RUSTFLAGS RUSTUP_TOOLCHAIN 180 | 181 | cargo test 182 | cargo build --release 183 | 184 | /c/Program\ Files\ \(x86\)/NSIS/Bin/makensis.exe native/res/win.nsi 185 | } 186 | 187 | case "$OSTYPE" in 188 | linux*) linux ;; 189 | darwin*) macos ;; 190 | msys) win ;; 191 | *) echo "Unsupported OS: $OSTYPE" 192 | exit 1 193 | esac 194 | -------------------------------------------------------------------------------- /native/scripts/dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | case "$OSTYPE" in 4 | linux*) echo "OS: Linux" 5 | if [ -z "$XDG_DATA_HOME" ]; then 6 | DATA_HOME="$HOME/.local/share" 7 | else 8 | DATA_HOME="$XDG_DATA_HOME" 9 | fi 10 | ;; 11 | darwin*) echo "OS: OSX" 12 | DATA_HOME="$HOME/Library/Application Support" 13 | ;; 14 | *) echo "Unsupported OS: $OSTYPE" 15 | exit 1 16 | ;; 17 | esac 18 | 19 | HOST_BIN_HOME="$DATA_HOME/radical-native" 20 | mkdir -p "$HOST_BIN_HOME" 21 | NATIVE_MANIFEST_NAME="radical.native.dev" 22 | NATIVE_MANIFEST_FILENAME="$NATIVE_MANIFEST_NAME.json" 23 | NATIVE_HOST_APP_BIN="$PWD/target/debug/radical-native" 24 | 25 | 26 | install() { 27 | if [ "$1" = "firefox" ]; then 28 | ALLOWED='"allowed_extensions": [ "@radical-native", "@riot-webext" ]' 29 | case "$OSTYPE" in 30 | linux*) NATIVE_HOSTS_PATH="$HOME/.mozilla/native-messaging-hosts" ;; 31 | darwin*) NATIVE_HOSTS_PATH="$HOME/Library/Application Support/Mozilla/NativeMessagingHosts" ;; 32 | esac 33 | fi 34 | if [ "$1" = "chromium" ]; then 35 | ALLOWED='"allowed_origins": [ "chrome-extension://hdikcfhaiboiiihkfmgaldafdbplnjok/" ]' 36 | case "$OSTYPE" in 37 | linux*) NATIVE_HOSTS_PATH="$HOME/.config/chromium/NativeMessagingHosts" ;; 38 | darwin*) NATIVE_HOSTS_PATH="$HOME/Library/Application Support/Chromium/NativeMessagingHosts" ;; 39 | esac 40 | fi 41 | if [ "$1" = "chrome" ]; then 42 | ALLOWED='"allowed_origins": [ "chrome-extension://hdikcfhaiboiiihkfmgaldafdbplnjok/" ]' 43 | case "$OSTYPE" in 44 | linux*) NATIVE_HOSTS_PATH="$HOME/.config/google-chrome/NativeMessagingHosts" ;; 45 | darwin*) NATIVE_HOSTS_PATH="$HOME/Library/Application Support/Google/Chrome/NativeMessagingHosts" ;; 46 | esac 47 | fi 48 | 49 | NATIVE_MANIFEST=$(cat <<-END 50 | { 51 | "name": "$NATIVE_MANIFEST_NAME", 52 | "description": "Radical Native", 53 | "path": "$NATIVE_HOST_APP_BIN", 54 | "type": "stdio", 55 | $ALLOWED 56 | } 57 | END 58 | ) 59 | 60 | NATIVE_MANIFEST_PATH="$NATIVE_HOSTS_PATH/$NATIVE_MANIFEST_FILENAME" 61 | mkdir -p "$NATIVE_HOSTS_PATH" 62 | echo "$NATIVE_MANIFEST" > "$NATIVE_MANIFEST_PATH" 63 | echo "$1: Installed native messaging manifest to: $NATIVE_MANIFEST_PATH" 64 | } 65 | 66 | install "firefox" 67 | #install "chrome" 68 | #install "chromium" -------------------------------------------------------------------------------- /native/src/indexer.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{bail, Context, Result}; 2 | use serde::{Deserialize, Serialize}; 3 | use serde_json::{json, Value}; 4 | use seshat::{ 5 | Config, Connection, CrawlerCheckpoint, Database, Error as SeshatError, EventType, Language, 6 | LoadConfig, Profile, RecoveryDatabase, SearchConfig, 7 | }; 8 | use std::{collections::HashMap, path::PathBuf}; 9 | 10 | use crate::Radical; 11 | 12 | #[derive(Debug, Deserialize)] 13 | #[serde(rename_all = "camelCase", tag = "method")] 14 | pub enum Message { 15 | InitEventIndex(InitEventIndex), 16 | LoadCheckpoints, 17 | IsEventIndexEmpty, 18 | IsRoomIndexed { content: IsRoomIndexed }, 19 | CommitLiveEvents, 20 | AddEventToIndex { content: AddEventToIndex }, 21 | AddCrawlerCheckpoint { content: AddHistoricEvents }, 22 | AddHistoricEvents { content: AddHistoricEvents }, 23 | RemoveCrawlerCheckpoint { content: AddHistoricEvents }, 24 | SearchEventIndex { content: SearchEventIndex }, 25 | LoadFileEvents { content: LoadConfig }, 26 | GetUserVersion, 27 | SetUserVersion { content: SetUserVersion }, 28 | DeleteEvent { content: DeleteEvent }, 29 | GetStats, 30 | CloseEventIndex, 31 | DeleteEventIndex, 32 | } 33 | 34 | #[derive(Debug, Deserialize)] 35 | pub struct InitEventIndex { 36 | pub passphrase: Option, 37 | pub language: Option, 38 | } 39 | 40 | #[derive(Debug, Deserialize)] 41 | #[serde(rename_all = "camelCase")] 42 | pub struct IsRoomIndexed { 43 | pub room_id: String, 44 | } 45 | 46 | #[derive(Debug, Deserialize)] 47 | pub struct AddEventToIndex { 48 | pub ev: Value, 49 | pub profile: Profile, 50 | } 51 | 52 | #[derive(Debug, Deserialize)] 53 | #[serde(rename_all = "camelCase")] 54 | pub struct AddHistoricEvents { 55 | pub checkpoint: Option, 56 | pub old_checkpoint: Option, 57 | pub events: Option>, 58 | } 59 | 60 | #[derive(Debug, Deserialize)] 61 | pub struct SearchEventIndex { 62 | pub term: String, 63 | pub config: SearchConfig, 64 | } 65 | 66 | #[derive(Debug, Deserialize)] 67 | #[serde(rename_all = "camelCase")] 68 | pub struct DeleteEvent { 69 | pub event_id: String, 70 | } 71 | 72 | #[derive(Debug, Deserialize)] 73 | #[serde(rename_all = "camelCase")] 74 | pub struct SetUserVersion { 75 | pub version: i64, 76 | } 77 | 78 | #[derive(Debug, Serialize, Deserialize)] 79 | pub struct SearchResults { 80 | pub count: usize, 81 | pub results: Vec, 82 | pub highlights: Vec, 83 | pub next_batch: Option, 84 | } 85 | 86 | #[derive(Debug, Serialize, Deserialize)] 87 | pub struct SearchResult { 88 | pub rank: f32, 89 | pub result: Value, 90 | pub context: SearchResultContext, 91 | } 92 | 93 | #[derive(Debug, Serialize, Deserialize)] 94 | pub struct SearchResultContext { 95 | pub events_before: Vec, 96 | pub events_after: Vec, 97 | pub profile_info: HashMap, 98 | } 99 | 100 | #[derive(Debug, Serialize, Deserialize)] 101 | pub struct SearchHighlight {} 102 | 103 | #[derive(Debug, Serialize, Deserialize)] 104 | pub struct FileEvent { 105 | pub event: Value, 106 | pub profile: Profile, 107 | } 108 | 109 | #[derive(Debug, Serialize, Deserialize)] 110 | pub struct Events { 111 | pub event: Value, 112 | pub profile: Profile, 113 | } 114 | 115 | // remove duplication once specialization lands 116 | // https://github.com/rust-lang/rust/issues/31844 117 | #[derive(Debug, Serialize, Deserialize)] 118 | #[serde(tag = "type")] 119 | pub enum Event { 120 | #[serde(rename = "m.room.message")] 121 | Message(EventMessage), 122 | #[serde(rename = "m.room.name")] 123 | Name(EventName), 124 | #[serde(rename = "m.room.topic")] 125 | Topic(EventTopic), 126 | } 127 | 128 | #[derive(Debug, Serialize, Deserialize)] 129 | pub struct EventMessage { 130 | pub event_id: String, 131 | pub sender: String, 132 | #[serde(rename = "origin_server_ts")] 133 | pub server_ts: i64, 134 | pub room_id: String, 135 | pub content: EventMessageContent, 136 | } 137 | 138 | #[derive(Debug, Serialize, Deserialize)] 139 | pub struct EventMessageContent { 140 | pub body: String, 141 | pub msgtype: Option, 142 | } 143 | 144 | #[derive(Debug, Serialize, Deserialize)] 145 | pub struct EventName { 146 | pub event_id: String, 147 | pub sender: String, 148 | #[serde(rename = "origin_server_ts")] 149 | pub server_ts: i64, 150 | pub room_id: String, 151 | pub content: EventNameContent, 152 | } 153 | 154 | #[derive(Debug, Serialize, Deserialize)] 155 | pub struct EventNameContent { 156 | pub name: String, 157 | } 158 | 159 | #[derive(Debug, Serialize, Deserialize)] 160 | pub struct EventTopic { 161 | pub event_id: String, 162 | pub sender: String, 163 | #[serde(rename = "origin_server_ts")] 164 | pub server_ts: i64, 165 | pub room_id: String, 166 | pub content: EventTopicContent, 167 | } 168 | 169 | #[derive(Debug, Serialize, Deserialize)] 170 | pub struct EventTopicContent { 171 | pub topic: String, 172 | } 173 | 174 | pub fn handle_message(radical: &mut Radical, message_in: Value) -> Result { 175 | let event_store = match message_in.get("eventStore") { 176 | Some(res) => res.as_str().context("eventStore.as_str() failed")?, 177 | None => "default", 178 | } 179 | .to_owned(); 180 | let message: Message = serde_json::from_value(message_in)?; 181 | 182 | let res = match radical.indexer.get_mut(&event_store) { 183 | None => match message { 184 | Message::InitEventIndex(message) => { 185 | let config = Indexer::config(message); 186 | let indexer = Indexer::new(&event_store, config)?; 187 | radical.indexer.insert(event_store.to_owned(), indexer); 188 | json!(null) 189 | } 190 | Message::DeleteEventIndex => Indexer::delete(&event_store, &mut radical.indexer)?, 191 | Message::CloseEventIndex => json!(null), // no-op 192 | _ => bail!("index not initialized"), 193 | }, 194 | Some(indexer) => match message { 195 | Message::LoadCheckpoints => indexer.load_checkpoints()?, 196 | Message::IsEventIndexEmpty => indexer.is_event_index_empty()?, 197 | Message::IsRoomIndexed { content } => indexer.is_room_indexed(content)?, 198 | Message::CommitLiveEvents => indexer.commit_live_events()?, 199 | Message::AddEventToIndex { content } => indexer.add_event_to_index(content)?, 200 | Message::AddCrawlerCheckpoint { content } => indexer.add_history_events(content)?, 201 | Message::AddHistoricEvents { content } => indexer.add_history_events(content)?, 202 | Message::RemoveCrawlerCheckpoint { content } => indexer.add_history_events(content)?, 203 | Message::SearchEventIndex { content } => indexer.search_event_index(content)?, 204 | Message::LoadFileEvents { content } => indexer.load_file_events(content)?, 205 | Message::GetUserVersion => indexer.get_user_version()?, 206 | Message::SetUserVersion { content } => indexer.set_user_version(content)?, 207 | Message::DeleteEvent { content } => indexer.delete_event(content)?, 208 | Message::GetStats => indexer.get_stats()?, 209 | Message::CloseEventIndex => Indexer::shutdown(&event_store, &mut radical.indexer)?, 210 | Message::DeleteEventIndex => Indexer::delete(&event_store, &mut radical.indexer)?, 211 | Message::InitEventIndex(_) => json!(null), // no-op 212 | }, 213 | }; 214 | 215 | Ok(res) 216 | } 217 | 218 | pub type IndexerMap = HashMap; 219 | 220 | pub struct Indexer { 221 | database: Database, 222 | connection: Connection, 223 | } 224 | 225 | impl Indexer { 226 | pub fn new(event_store: &str, config: Config) -> Result { 227 | let path = Indexer::event_store_path(event_store)?; 228 | std::fs::create_dir_all(&path)?; 229 | 230 | Ok(Indexer::new_in_path(path, config)?) 231 | } 232 | 233 | pub fn new_in_path(path: PathBuf, config: Config) -> Result { 234 | let database = match Database::new_with_config(&path, &config) { 235 | Ok(database) => database, 236 | Err(error) => match error { 237 | SeshatError::ReindexError => { 238 | Indexer::reindex(&path, &config)?; 239 | Database::new_with_config(path, &config)? 240 | } 241 | _ => bail!("Error opening the database: {:?}", error), 242 | }, 243 | }; 244 | 245 | let connection = database.get_connection()?; 246 | Ok(Indexer { 247 | database, 248 | connection, 249 | }) 250 | } 251 | 252 | pub fn reindex(path: &PathBuf, config: &Config) -> Result<()> { 253 | let mut db = RecoveryDatabase::new_with_config(&path, config)?; 254 | // https://github.com/stoically/radical-native/issues/19#issuecomment-648382654 255 | if db.get_connection()?.get_user_version()? == 0 { 256 | db.shutdown()?; 257 | if path.exists() { 258 | std::fs::remove_dir_all(path)?; 259 | } 260 | } else { 261 | // copy&paste'd from 262 | // https://github.com/matrix-org/seshat/blob/96d02b6e5d3a53db0174361aee36a02936c40bfa/seshat-node/native/src/tasks.rs#L387 263 | // this will probably move upstream into dedicated methods at some point, 264 | // when design decisions about reindex progress in the riot UI are made 265 | db.delete_the_index()?; 266 | db.open_index()?; 267 | 268 | let mut events = db.load_events_deserialized(500, None)?; 269 | db.index_events(&events)?; 270 | 271 | loop { 272 | events = db.load_events_deserialized(500, events.last())?; 273 | 274 | if events.is_empty() { 275 | break; 276 | } 277 | 278 | db.index_events(&events)?; 279 | db.commit()?; 280 | } 281 | 282 | db.commit_and_close()?; 283 | } 284 | 285 | Ok(()) 286 | } 287 | 288 | fn event_store_path(event_store: &str) -> Result { 289 | let mut path = match dirs::data_local_dir() { 290 | Some(path) => path, 291 | None => bail!("userdata dir not found"), 292 | }; 293 | path.push("radical-native"); 294 | path.push("EventStore"); 295 | path.push(event_store); 296 | Ok(path) 297 | } 298 | 299 | fn shutdown(event_store: &str, indexer: &mut IndexerMap) -> Result { 300 | if let Some(indexer) = indexer.remove(event_store) { 301 | indexer.database.shutdown().recv()??; 302 | } 303 | Ok(json!(null)) 304 | } 305 | 306 | fn delete(event_store: &str, indexer: &mut IndexerMap) -> Result { 307 | Indexer::shutdown(event_store, indexer)?; 308 | 309 | let path = Indexer::event_store_path(event_store)?; 310 | if path.exists() { 311 | std::fs::remove_dir_all(path)?; 312 | } 313 | 314 | Ok(json!(null)) 315 | } 316 | 317 | fn config(message: InitEventIndex) -> Config { 318 | let mut config = Config::new(); 319 | 320 | let passphrase = match message.passphrase { 321 | Some(passphrase) => passphrase, 322 | None => "DEFAULT_PASSPHRASE".to_owned(), 323 | }; 324 | config = config.set_passphrase(passphrase); 325 | 326 | if let Some(language) = message.language { 327 | let language = Language::from(language.as_str()); 328 | config = config.set_language(&language); 329 | } 330 | 331 | config 332 | } 333 | 334 | fn convert_event(event: Value) -> Result { 335 | let source = serde_json::to_string(&event)?; 336 | let event: Event = serde_json::from_value(event)?; 337 | let res = match event { 338 | Event::Message(ev) => seshat::Event { 339 | event_type: EventType::Message, 340 | content_value: ev.content.body, 341 | msgtype: ev.content.msgtype, 342 | event_id: ev.event_id, 343 | sender: ev.sender, 344 | server_ts: ev.server_ts, 345 | room_id: ev.room_id, 346 | source, 347 | }, 348 | Event::Name(ev) => seshat::Event { 349 | event_type: EventType::Name, 350 | content_value: ev.content.name, 351 | msgtype: None, 352 | event_id: ev.event_id, 353 | sender: ev.sender, 354 | server_ts: ev.server_ts, 355 | room_id: ev.room_id, 356 | source, 357 | }, 358 | Event::Topic(ev) => seshat::Event { 359 | event_type: EventType::Message, 360 | content_value: ev.content.topic, 361 | msgtype: None, 362 | event_id: ev.event_id, 363 | sender: ev.sender, 364 | server_ts: ev.server_ts, 365 | room_id: ev.room_id, 366 | source, 367 | }, 368 | }; 369 | 370 | Ok(res) 371 | } 372 | 373 | fn load_checkpoints(&self) -> Result { 374 | let checkpoints = self.connection.load_checkpoints()?; 375 | Ok(json!(checkpoints)) 376 | } 377 | 378 | fn is_event_index_empty(&self) -> Result { 379 | let res = self.connection.is_empty()?; 380 | Ok(json!(res)) 381 | } 382 | 383 | fn is_room_indexed(&self, message: IsRoomIndexed) -> Result { 384 | let res = self.connection.is_room_indexed(&message.room_id)?; 385 | Ok(json!(res)) 386 | } 387 | 388 | fn commit_live_events(&mut self) -> Result { 389 | self.database.commit_no_wait().recv()??; 390 | Ok(json!(true)) 391 | } 392 | 393 | fn add_event_to_index(&self, message: AddEventToIndex) -> Result { 394 | let event = Indexer::convert_event(message.ev)?; 395 | self.database.add_event(event, message.profile); 396 | Ok(json!(null)) 397 | } 398 | 399 | fn add_history_events(&self, message: AddHistoricEvents) -> Result { 400 | let mut events: Vec<(seshat::Event, Profile)> = Vec::new(); 401 | if let Some(events_in) = message.events { 402 | for event_in in events_in { 403 | let profile = event_in.profile; 404 | let event = Indexer::convert_event(event_in.event)?; 405 | events.push((event, profile)); 406 | } 407 | } 408 | 409 | let res = self 410 | .database 411 | .add_historic_events(events, message.checkpoint, message.old_checkpoint) 412 | .recv()??; 413 | 414 | Ok(json!(res)) 415 | } 416 | 417 | fn search_event_index(&self, message: SearchEventIndex) -> Result { 418 | let searcher = self.database.get_searcher(); 419 | let search_batch = searcher.search(&message.term, &message.config)?; 420 | 421 | let mut results = Vec::new(); 422 | for result in search_batch.results { 423 | let event: Value = serde_json::from_str(&result.event_source)?; 424 | let mut events_before = Vec::new(); 425 | for event in result.events_before.iter() { 426 | let event: Value = serde_json::from_str(event)?; 427 | events_before.push(event); 428 | } 429 | let mut events_after = Vec::new(); 430 | for event in result.events_after.iter() { 431 | let event: Value = serde_json::from_str(event)?; 432 | events_after.push(event); 433 | } 434 | 435 | results.push(SearchResult { 436 | rank: result.score, 437 | result: event, 438 | context: SearchResultContext { 439 | events_before, 440 | events_after, 441 | profile_info: result.profile_info, 442 | }, 443 | }); 444 | } 445 | 446 | let next_batch = match search_batch.next_batch { 447 | Some(next_batch) => Some(next_batch.to_hyphenated().to_string()), 448 | None => None, 449 | }; 450 | 451 | let res = SearchResults { 452 | count: search_batch.count, 453 | results, 454 | highlights: vec![], 455 | next_batch, 456 | }; 457 | 458 | Ok(json!(res)) 459 | } 460 | 461 | fn load_file_events(&self, message: LoadConfig) -> Result { 462 | let ret = self.connection.load_file_events(&message)?; 463 | let mut results = Vec::new(); 464 | for (source, profile) in ret { 465 | let event: Value = serde_json::from_str(&source)?; 466 | results.push(FileEvent { event, profile }); 467 | } 468 | Ok(json!(results)) 469 | } 470 | 471 | fn get_stats(&self) -> Result { 472 | let res = self.connection.get_stats()?; 473 | Ok(json!(res)) 474 | } 475 | 476 | fn get_user_version(&self) -> Result { 477 | let res = self.connection.get_user_version()?; 478 | Ok(json!(res)) 479 | } 480 | 481 | fn set_user_version(&self, content: SetUserVersion) -> Result { 482 | self.connection.set_user_version(content.version)?; 483 | Ok(json!(null)) 484 | } 485 | 486 | fn delete_event(&self, message: DeleteEvent) -> Result { 487 | let res = self.database.delete_event(&message.event_id).recv()??; 488 | Ok(json!(res)) 489 | } 490 | } 491 | 492 | #[cfg(test)] 493 | mod tests { 494 | use super::*; 495 | use tempfile::tempdir; 496 | 497 | fn event_room_message_text() -> Value { 498 | json!({ 499 | "type": "m.room.message", 500 | "room_id": "!FDVbSkWZSIcwvBFMdt:localhost", 501 | "sender": "@example2:localhost", 502 | "content": { 503 | "body": "Test message", 504 | "msgtype": "m.text" 505 | }, 506 | "origin_server_ts": 1_580_728_702_628 as usize, 507 | "unsigned": { 508 | "age": 949_499_816 as usize 509 | }, 510 | "event_id": "$lp49H7iDTNWQxD-fiZ6sDE6vT70DlYdKdoujEB5QtLM", 511 | "user_id": "@example2:localhost", 512 | "age": 949_499_816 as usize 513 | }) 514 | } 515 | 516 | fn checkpoint() -> Value { 517 | json!({ 518 | "roomId": "!FDVbSkWZSIcwvBFMdt:localhost", 519 | "token": "123", 520 | "direction": "b" 521 | }) 522 | } 523 | 524 | fn checkpoint2() -> Value { 525 | json!({ 526 | "roomId": "!FDVbSkWZSIcwvBFMdt:localhost", 527 | "token": "456", 528 | "direction": "b" 529 | }) 530 | } 531 | 532 | fn indexer(tmpdir: &std::path::Path) -> Indexer { 533 | let mut config = Config::new(); 534 | config = config.set_passphrase("TEST_PASS"); 535 | Indexer::new_in_path(tmpdir.to_path_buf(), config).expect("indexer") 536 | } 537 | 538 | #[test] 539 | fn crawler_checkpoints() { 540 | let tmpdir = tempdir().expect("tempdir"); 541 | let indexer = indexer(tmpdir.path()); 542 | let checkpoint = checkpoint(); 543 | 544 | let message: AddHistoricEvents = serde_json::from_value(json!({ 545 | "checkpoint": checkpoint.clone() 546 | })) 547 | .unwrap(); 548 | indexer 549 | .add_history_events(message) 550 | .expect("add_crawler_checkpoint"); 551 | 552 | let message: AddHistoricEvents = 553 | serde_json::from_value(json!({ "oldCheckpoint": checkpoint })).unwrap(); 554 | indexer 555 | .add_history_events(message) 556 | .expect("remove_crawler_checkpoint"); 557 | 558 | let checkpoints = indexer.load_checkpoints().expect("load_checkpoints"); 559 | let count = checkpoints.as_array().expect("checkpoints.as_array").len(); 560 | assert_eq!(count, 0); 561 | } 562 | 563 | #[test] 564 | fn initial_crawl() { 565 | let tmpdir = tempdir().expect("tempdir"); 566 | let indexer = indexer(tmpdir.path()); 567 | let checkpoint = checkpoint(); 568 | let profile = Profile::new("Alice", ""); 569 | 570 | let message: AddHistoricEvents = serde_json::from_value(json!({ 571 | "checkpoint": checkpoint.clone() 572 | })) 573 | .unwrap(); 574 | indexer 575 | .add_history_events(message) 576 | .expect("add_crawler_checkpoint"); 577 | 578 | let message: AddHistoricEvents = serde_json::from_value(json!({ 579 | "checkpoint": checkpoint2(), 580 | "events": [ 581 | { 582 | "event": event_room_message_text(), 583 | "profile": profile 584 | } 585 | ], 586 | "oldCheckpoint": checkpoint 587 | })) 588 | .unwrap(); 589 | indexer 590 | .add_history_events(message) 591 | .expect("add_history_events"); 592 | 593 | assert_eq!( 594 | indexer 595 | .load_checkpoints() 596 | .expect("load_checkpoints") 597 | .as_array() 598 | .expect("load_checkpoints.as_array") 599 | .len(), 600 | 1 601 | ); 602 | } 603 | 604 | #[test] 605 | fn add_event() { 606 | let tmpdir = tempdir().expect("tempdir"); 607 | let mut indexer = indexer(tmpdir.path()); 608 | 609 | let profile = Profile::new("Alice", ""); 610 | let payload: AddEventToIndex = serde_json::from_value(json!({ 611 | "ev": event_room_message_text(), 612 | "profile": profile 613 | })) 614 | .unwrap(); 615 | indexer 616 | .add_event_to_index(payload) 617 | .expect("add_event_to_index"); 618 | 619 | indexer.commit_live_events().expect("commit_live_events"); 620 | 621 | let reply = indexer.get_stats().expect("get_stats"); 622 | assert_eq!(reply["eventCount"].as_i64().expect("eventCount"), 1); 623 | } 624 | 625 | #[test] 626 | fn json_messages() { 627 | let tmpdir = tempdir().expect("tempdir"); 628 | // make sure that we have only one test that modifies the environment 629 | // since tests run in parallel 630 | std::env::set_var("HOME", tmpdir.path().to_str().expect("tmpdir path")); 631 | use std::collections::HashMap; 632 | let mut radical = Radical { 633 | indexer: HashMap::new(), 634 | }; 635 | handle_message( 636 | &mut radical, 637 | json!({ 638 | "method": "initEventIndex" 639 | }), 640 | ) 641 | .expect("initEventIndex"); 642 | 643 | let profile = Profile::new("Alice", ""); 644 | handle_message( 645 | &mut radical, 646 | json!({ 647 | "method": "addEventToIndex", 648 | "content": { 649 | "ev": event_room_message_text(), 650 | "profile": profile 651 | } 652 | }), 653 | ) 654 | .expect("addEventToIndex"); 655 | 656 | handle_message( 657 | &mut radical, 658 | json!({ 659 | "method": "commitLiveEvents" 660 | }), 661 | ) 662 | .expect("commitLiveEvents"); 663 | 664 | let checkpoint = checkpoint(); 665 | handle_message( 666 | &mut radical, 667 | json!({ 668 | "method": "addCrawlerCheckpoint", 669 | "content": { 670 | "checkpoint": checkpoint 671 | } 672 | }), 673 | ) 674 | .expect("addCrawlerCheckpoint"); 675 | 676 | handle_message( 677 | &mut radical, 678 | json!({ 679 | "method": "removeCrawlerCheckpoint", 680 | "content": { 681 | "oldCheckpoint": checkpoint 682 | } 683 | }), 684 | ) 685 | .expect("removeCrawlerCheckpoint"); 686 | 687 | let checkpoints = handle_message( 688 | &mut radical, 689 | json!({ 690 | "method": "loadCheckpoints" 691 | }), 692 | ) 693 | .expect("loadCheckpoints"); 694 | 695 | let reply = handle_message( 696 | &mut radical, 697 | json!({ 698 | "method": "getStats" 699 | }), 700 | ) 701 | .expect("getStats"); 702 | 703 | assert_eq!(checkpoints.as_array().expect("checkpoints").len(), 0); 704 | assert_eq!(reply["eventCount"].as_i64().expect("eventCount"), 1); 705 | } 706 | } 707 | -------------------------------------------------------------------------------- /native/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::anyhow; 2 | use serde::Deserialize; 3 | use serde_json::Value; 4 | use std::collections::HashMap; 5 | 6 | mod indexer; 7 | mod native_messaging; 8 | mod secrets; 9 | 10 | use indexer::IndexerMap; 11 | use native_messaging::{stdin, stdout_error, stdout_ready, stdout_reply}; 12 | 13 | pub struct Radical { 14 | indexer: IndexerMap, 15 | } 16 | 17 | #[derive(Debug, Deserialize)] 18 | #[serde(rename_all = "camelCase", tag = "type")] 19 | pub enum Message { 20 | Seshat(Value), 21 | Keytar(Value), 22 | } 23 | 24 | fn main() { 25 | let mut radical = Radical { 26 | indexer: HashMap::new(), 27 | }; 28 | 29 | stdout_ready(); 30 | loop { 31 | let (rpc_id, message) = match stdin() { 32 | Ok(stdin) => stdin, 33 | Err(error) => { 34 | stdout_error(-1, error); 35 | continue; 36 | } 37 | }; 38 | 39 | let message = serde_json::from_value(message); 40 | let reply = match message { 41 | Ok(Message::Seshat(message)) => indexer::handle_message(&mut radical, message), 42 | Ok(Message::Keytar(message)) => secrets::handle_message(message), 43 | _ => Err(anyhow!("handling message failed: {:?}", message)), 44 | }; 45 | 46 | match reply { 47 | Ok(reply) => stdout_reply(rpc_id, reply), 48 | Err(error) => stdout_error(rpc_id, error), 49 | }; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /native/src/native_messaging.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{bail, Context, Error, Result}; 2 | use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; 3 | use serde_json::{json, Value}; 4 | use std::io::{self, prelude::*, Cursor}; 5 | 6 | pub fn stdin() -> Result<(i64, Value)> { 7 | let mut buffer = [0; 4]; 8 | io::stdin().read_exact(&mut buffer)?; 9 | let mut buf = Cursor::new(&buffer); 10 | let size = buf.read_u32::()?; 11 | 12 | let mut data_buffer = vec![0u8; size as usize]; 13 | io::stdin().read_exact(&mut data_buffer)?; 14 | let message: Value = serde_json::from_slice(&data_buffer)?; 15 | let rpc_id = match message.get("rpcId") { 16 | Some(res) => res.as_i64().context("invalid rpcId")?, 17 | None => bail!("no rpcId given"), 18 | }; 19 | 20 | Ok((rpc_id, message)) 21 | } 22 | 23 | pub fn stdout_ready() { 24 | stdout(json!({ 25 | "ready": true, 26 | "version": env!("CARGO_PKG_VERSION").to_owned(), 27 | })) 28 | .unwrap_or_else(|error| eprintln!("{:?}", error)); 29 | } 30 | 31 | pub fn stdout_reply(rpc_id: i64, reply: Value) { 32 | stdout(json!({ 33 | "rpcId": rpc_id, 34 | "reply": reply, 35 | })) 36 | .unwrap_or_else(|error| eprintln!("{:?}", error)); 37 | } 38 | 39 | pub fn stdout_error(rpc_id: i64, error: Error) { 40 | stdout(json!({ 41 | "rpcId": rpc_id, 42 | "error": format!("{:?}", error), 43 | })) 44 | .unwrap_or_else(|error| eprintln!("{:?}", error)); 45 | } 46 | 47 | fn stdout(message: Value) -> Result<()> { 48 | let message = serde_json::to_string(&message)?; 49 | let mut size = Vec::default(); 50 | size.write_u32::(message.len() as u32)?; 51 | io::stdout().write_all(&size)?; 52 | io::stdout().write_all(&message.into_bytes())?; 53 | Ok(io::stdout().flush()?) 54 | } 55 | -------------------------------------------------------------------------------- /native/src/secrets.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use base64::{encode_config, STANDARD_NO_PAD}; 3 | use keytar::{delete_password, get_password, set_password, Password}; 4 | use rand::random; 5 | use serde::Deserialize; 6 | use serde_json::{json, Value}; 7 | 8 | const SERVICE: &str = "riot.im"; 9 | 10 | #[derive(Deserialize)] 11 | #[serde(rename_all = "camelCase", tag = "method")] 12 | enum Message { 13 | GetPickleKey { content: MessageContent }, 14 | CreatePickleKey { content: MessageContent }, 15 | DestroyPickleKey { content: MessageContent }, 16 | } 17 | 18 | #[derive(Deserialize)] 19 | #[serde(rename_all = "camelCase")] 20 | struct MessageContent { 21 | user_id: String, 22 | device_id: String, 23 | } 24 | 25 | pub fn handle_message(message_in: Value) -> Result { 26 | let message: Message = serde_json::from_value(message_in)?; 27 | let res = match message { 28 | Message::GetPickleKey { content } => { 29 | let Password { success, password } = 30 | get_password(SERVICE, &account_string(content.user_id, content.device_id))?; 31 | 32 | if success { 33 | json!(password) 34 | } else { 35 | json!(null) 36 | } 37 | } 38 | Message::CreatePickleKey { content } => { 39 | let random_vec: Vec = (0..32).into_iter().map(|_| random::()).collect(); 40 | let pickle_key = encode_config(&random_vec, STANDARD_NO_PAD); 41 | let res = json!(pickle_key); 42 | 43 | set_password( 44 | SERVICE, 45 | &account_string(content.user_id, content.device_id), 46 | &pickle_key, 47 | )?; 48 | 49 | res 50 | } 51 | Message::DestroyPickleKey { content } => { 52 | let success = 53 | delete_password(SERVICE, &account_string(content.user_id, content.device_id))?; 54 | 55 | json!(success) 56 | } 57 | }; 58 | 59 | Ok(res) 60 | } 61 | 62 | fn account_string(user_id: String, device_id: String) -> String { 63 | format!("{}|{}", user_id, device_id) 64 | } 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "radical-native", 3 | "private": true, 4 | "scripts": { 5 | "clean": "rimraf build target", 6 | "build": "cross-env run-s build:*", 7 | "build:firefox": "cross-env mkdir -p build/firefox && run-p build:firefox:*", 8 | "build:firefox:parcel": "parcel build src/background.ts src/contentscript.ts src/resources/bundle.ts -d build/firefox --no-source-maps --no-minify", 9 | "build:firefox:static": "cp src/manifest.firefox.json build/firefox/manifest.json && cp -r src/icons LICENSE build/firefox", 10 | "build:native": "cross-env ./native/scripts/build.sh", 11 | "dev": "run-p dev:*", 12 | "dev:firefox": "mkdir -p build/firefox && run-p dev:firefox:*", 13 | "dev:firefox:parcel": "parcel watch src/background.ts src/contentscript.ts src/resources/bundle.ts src/riot-patch.ts -d build/firefox --no-hmr", 14 | "dev:firefox:static": "nodemon -w src --ext '*' --exec 'cp src/manifest.firefox.json build/firefox/manifest.json && cp -r src/icons build/firefox'", 15 | "dev:chrome": "mkdir -p build/chrome && run-p dev:chrome:*", 16 | "dev:chrome:parcel": "parcel watch src/background.ts src/contentscript.ts src/resources/bundle.ts src/riot-patch.ts -d build/chrome --no-hmr", 17 | "dev:chrome:polyfill": "cp node_modules/webextension-polyfill/dist/browser-polyfill.min.js build/chrome", 18 | "dev:chrome:static": "nodemon -w src --ext '*' --exec 'cp src/manifest.chrome.json build/chrome/manifest.json'", 19 | "dev:install-native": "./native/scripts/dev.sh", 20 | "dev:native": "cargo watch -x build", 21 | "lint": "cross-env run-p lint:*", 22 | "lint:eslint": "eslint src/**/*.ts", 23 | "lint:tsc": "tsc", 24 | "test": "cross-env run-p test:cargo", 25 | "test:cargo": "cargo test", 26 | "test:webext": "ts-mocha --paths", 27 | "test:watch": "run-p test:watch:*", 28 | "test:watch:cargo": "cargo watch -x test", 29 | "test:watch:webext": "npm run test:webext -- --watch", 30 | "test:coverage": "run-p test:coverage:*", 31 | "test:coverage:webext": "nyc npm run test" 32 | }, 33 | "devDependencies": { 34 | "@types/firefox-webext-browser": "^82.0.1", 35 | "@types/mocha": "^9.0.0", 36 | "@types/node": "^16.4.12", 37 | "@types/uuid": "^8.3.1", 38 | "@typescript-eslint/eslint-plugin": "^4.29.0", 39 | "@typescript-eslint/parser": "^4.29.0", 40 | "cross-env": "^7.0.3", 41 | "eslint": "^7.32.0", 42 | "eslint-config-prettier": "^8.3.0", 43 | "eslint-plugin-prettier": "^3.4.0", 44 | "mocha": "^9.0.3", 45 | "nodemon": "^2.0.12", 46 | "npm-run-all": "^4.1.5", 47 | "nyc": "^15.1.0", 48 | "parcel": "^1.12.4", 49 | "prettier": "^2.3.2", 50 | "rimraf": "^3.0.2", 51 | "ts-mocha": "^8.0.0", 52 | "tsconfig-paths": "^3.10.1", 53 | "typescript": "^4.3.5", 54 | "webextension-polyfill": "^0.8.0" 55 | }, 56 | "browserslist": [ 57 | "Firefox >= 67", 58 | "last 2 Chrome versions" 59 | ], 60 | "prettier": { 61 | "trailingComma": "es5", 62 | "endOfLine": "auto" 63 | }, 64 | "mocha": { 65 | "bail": true, 66 | "reporter": "progress", 67 | "extension": "ts", 68 | "project": "tsconfig.json" 69 | }, 70 | "nyc": { 71 | "reporter": [ 72 | "html", 73 | "text", 74 | "lcov" 75 | ] 76 | }, 77 | "license": "MIT", 78 | "dependencies": { 79 | "uuid": "^8.3.2" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/background.ts: -------------------------------------------------------------------------------- 1 | import { Background } from "./background/lib"; 2 | 3 | declare global { 4 | interface Window { 5 | bg: Background; 6 | } 7 | } 8 | 9 | window.bg = new Background(); 10 | -------------------------------------------------------------------------------- /src/background/debug.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface Window { 3 | DEBUG: boolean; 4 | } 5 | } 6 | 7 | window.DEBUG = process.env.NODE_ENV === "development" ? true : false; 8 | 9 | export const debug = (...args: any[]): void => { 10 | if (!window.DEBUG) { 11 | return; 12 | } 13 | console.log("[RadicalNative::background]", ...args); 14 | }; 15 | -------------------------------------------------------------------------------- /src/background/lib.ts: -------------------------------------------------------------------------------- 1 | import { v4 as uuidv4 } from "uuid"; 2 | import { debug } from "./debug"; 3 | import { NativePort } from "./native"; 4 | 5 | export class Background { 6 | public manifest = browser.runtime.getManifest(); 7 | public version = this.manifest.version; 8 | public browserType = this.manifest.applications?.gecko ? "firefox" : "chrome"; 9 | public native = new NativePort(this); 10 | public uuid!: string; 11 | 12 | private initialized = false; 13 | private initializedPromise: Promise; 14 | private bundleResourceURL = browser.runtime.getURL("resources/bundle.js"); 15 | private riots: any[] = []; 16 | private riotTabs: Set = new Set(); 17 | 18 | constructor() { 19 | browser.browserAction.disable(); 20 | browser.browserAction.setBadgeText({ text: "init" }); 21 | browser.browserAction.setTitle({ title: "Initializing.." }); 22 | 23 | this.initializedPromise = new Promise(this.initialize.bind(this)); 24 | this.setupListeners(); 25 | } 26 | 27 | private async initialize(resolve: any, reject: any): Promise { 28 | try { 29 | const storage = await browser.storage.local.get(); 30 | if (storage.uuid) { 31 | this.uuid = storage.uuid; 32 | } else { 33 | this.uuid = uuidv4(); 34 | await browser.storage.local.set({ uuid: this.uuid }); 35 | } 36 | 37 | if (!storage.version || storage.version !== this.version) { 38 | await browser.storage.local.set({ version: this.version }); 39 | } 40 | 41 | if (storage.riots) { 42 | this.riots = storage.riots; 43 | } 44 | } catch (error) { 45 | reject(`unrecoverable storage error: ${error.toString()}`); 46 | throw error; 47 | } 48 | this.initialized = true; 49 | resolve(); 50 | } 51 | 52 | private setupListeners(): void { 53 | browser.webRequest.onBeforeRequest.addListener( 54 | async (details: any): Promise => { 55 | if (!this.initialized) { 56 | debug("incoming request, waiting for initialization", details); 57 | await this.initializedPromise; 58 | } 59 | 60 | if (details.url.endsWith("/bundle.js")) { 61 | debug("incoming bundle request", details); 62 | return this.riotBundleListener(details); 63 | } 64 | 65 | return {}; 66 | }, 67 | { 68 | urls: [""], 69 | types: ["script", "xmlhttprequest"], 70 | }, 71 | ["blocking"] 72 | ); 73 | 74 | browser.browserAction.onClicked.addListener( 75 | this.onBrowserActionClick.bind(this) 76 | ); 77 | 78 | browser.browserAction.setBadgeBackgroundColor({ 79 | color: "gray", 80 | }); 81 | 82 | browser.runtime.onMessage.addListener( 83 | (message: any, sender: browser.runtime.MessageSender) => { 84 | debug("internal message received", message, sender); 85 | return this.native.handleRuntimeMessage(message, sender); 86 | } 87 | ); 88 | } 89 | 90 | private async riotBundleListener(details: { 91 | url: string; 92 | tabId: number; 93 | }): Promise { 94 | await browser.tabs 95 | .executeScript(details.tabId, { 96 | file: "contentscript.js", 97 | runAt: "document_start", 98 | }) 99 | .catch(() => { 100 | // expected because of how parcel packages the contentscript 101 | }) 102 | .finally(async () => { 103 | return browser.tabs.sendMessage(details.tabId, { 104 | method: "ready", 105 | bundle: `${details.url}?load`, 106 | }); 107 | }); 108 | 109 | browser.browserAction.setTitle({ 110 | title: "Radical Native active for this Riot. Click to disable.", 111 | tabId: details.tabId, 112 | }); 113 | browser.browserAction.setBadgeText({ 114 | tabId: details.tabId, 115 | text: "on", 116 | }); 117 | this.riotTabs.add(details.tabId); 118 | 119 | // TODO: just let the original bundle load, since we injected the necessary 120 | // stuff already anyway 121 | return { 122 | redirectUrl: this.bundleResourceURL, 123 | }; 124 | } 125 | 126 | private async onBrowserActionClick(tab: browser.tabs.Tab): Promise { 127 | debug("browser action clicked", tab); 128 | if (!tab.url?.startsWith("http")) { 129 | browser.browserAction.disable(tab.id); 130 | browser.browserAction.setTitle({ 131 | title: `Can't activate Radical Native for ${tab.url}`, 132 | tabId: tab.id, 133 | }); 134 | browser.browserAction.setBadgeText({ text: "err", tabId: tab.id }); 135 | return; 136 | } 137 | const url = new URL(tab.url!); 138 | const riot = { 139 | protocol: url.protocol, 140 | hostname: url.hostname, 141 | pathname: url.pathname, 142 | cookieStoreId: tab.cookieStoreId || false, 143 | }; 144 | const pattern = `${riot.protocol}//${riot.hostname}${riot.pathname}*`; 145 | const origins = [pattern]; 146 | 147 | if (!this.riotTabs.has(tab.id!)) { 148 | const granted = await browser.permissions.request({ 149 | origins, 150 | }); 151 | if (!granted) { 152 | return; 153 | } 154 | 155 | this.riots.push(riot); 156 | } else { 157 | this.riots = this.riots.filter((enabledRiot: any) => { 158 | return !( 159 | riot.protocol === enabledRiot.protocol && 160 | riot.hostname === enabledRiot.hostname && 161 | riot.pathname === enabledRiot.pathname 162 | ); 163 | }); 164 | browser.permissions.remove({ 165 | origins, 166 | }); 167 | browser.browserAction.setBadgeText({ 168 | tabId: tab.id!, 169 | text: null, 170 | }); 171 | 172 | this.riotTabs.delete(tab.id!); 173 | } 174 | 175 | await browser.storage.local.set({ riots: this.riots }); 176 | browser.tabs.reload(tab.id!); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/background/native.ts: -------------------------------------------------------------------------------- 1 | import { debug } from "./debug"; 2 | import { Background } from "./lib"; 3 | 4 | export class NativePort { 5 | private name = 6 | process.env.NODE_ENV === "development" 7 | ? "radical.native.dev" 8 | : "radical.native"; 9 | private port?: browser.runtime.Port; 10 | private rpcPromises: Map = new Map(); 11 | private ready = false; 12 | private bg: Background; 13 | 14 | constructor(bg: Background) { 15 | this.bg = bg; 16 | this.init(); 17 | } 18 | 19 | async handleRuntimeMessage( 20 | runtimeMessage: any, 21 | sender: browser.runtime.MessageSender 22 | ): Promise { 23 | debug("runtime message received", runtimeMessage); 24 | if (!this.ready) { 25 | debug("port not ready, waiting 5s"); 26 | // port not ready yet, give it 5s to change its mind 27 | await new Promise((resolve) => setTimeout(resolve, 5 * 1000)); 28 | 29 | if (!this.ready) { 30 | debug("port not reachable, probably not installed"); 31 | return null; 32 | } 33 | } 34 | 35 | // construct native message from runtime message 36 | const message = runtimeMessage.content; 37 | message.type = runtimeMessage.type; 38 | message.rpcId = runtimeMessage.rpcId; 39 | 40 | switch (message.type) { 41 | case "seshat": 42 | if (message.method === "supportsEventIndexing") { 43 | return true; 44 | } 45 | 46 | const url = new URL(sender.url!); 47 | message.eventStore = `web-${this.bg.uuid}-${encodeURIComponent( 48 | `${url.origin}${url.pathname}` 49 | )}-${ 50 | this.bg.browserType === "firefox" 51 | ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 52 | sender.tab!.cookieStoreId! 53 | : "default" 54 | }`; 55 | break; 56 | } 57 | 58 | return this.postMessage(message); 59 | } 60 | 61 | private init(): void { 62 | this.port = browser.runtime.connectNative(this.name); 63 | this.port.onDisconnect.addListener(this.handleDisconnect.bind(this)); 64 | this.port.onMessage.addListener(this.handleMessage.bind(this)); 65 | } 66 | 67 | private close(): void { 68 | this.ready = false; 69 | this.port?.onDisconnect.removeListener(this.handleDisconnect.bind(this)); 70 | this.port?.onMessage.removeListener(this.handleMessage.bind(this)); 71 | delete this.port; 72 | } 73 | 74 | private postMessage(message: any): Promise { 75 | return new Promise((resolve, reject) => { 76 | this.rpcPromises.set(message.rpcId, { 77 | message, 78 | resolve, 79 | reject, 80 | }); 81 | debug(`posting to ${this.name}`, message); 82 | this.port?.postMessage(message); 83 | }); 84 | } 85 | 86 | private handleMessage(message: any): void { 87 | if (message.ready) { 88 | debug("port ready"); 89 | browser.browserAction.enable(); 90 | browser.browserAction.setTitle({ title: "Radical Native" }); 91 | browser.browserAction.setBadgeText({ text: null }); 92 | this.ready = true; 93 | return; 94 | } 95 | 96 | const rpcPromise = this.rpcPromises.get(message.rpcId); 97 | if (!rpcPromise) { 98 | debug("port message received without matching rpcPromise", message); 99 | return; 100 | } 101 | 102 | if (!message.error) { 103 | debug("port message received", { 104 | message, 105 | origExternalMessage: rpcPromise.message, 106 | }); 107 | rpcPromise.resolve(message.reply); 108 | } else { 109 | console.error("port error received", { 110 | error: message.error, 111 | origExternalMessage: rpcPromise.message, 112 | }); 113 | rpcPromise.reject(new Error(message.error)); 114 | } 115 | this.rpcPromises.delete(message.rpcId); 116 | } 117 | 118 | private handleDisconnect(port: browser.runtime.Port): void { 119 | debug("port disconnected", port); 120 | this.close(); 121 | 122 | if (port.error) { 123 | browser.browserAction.setBadgeText({ text: "err" }); 124 | browser.browserAction.setTitle({ 125 | title: "Cannot connect to the native application, trying again in 60s", 126 | }); 127 | } 128 | 129 | debug("retrying port connection in 60s"); 130 | setTimeout(() => { 131 | if (!this.ready) { 132 | this.init(); 133 | } 134 | }, 60 * 1000); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/contentscript.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | 3 | let bundleURL: string; 4 | 5 | const handleFromBundleMessage = (message: any): void => { 6 | switch (message.method) { 7 | case "ready": 8 | window.postMessage( 9 | { 10 | type: "bundle", 11 | target: "page", 12 | method: "init", 13 | bundle: bundleURL, 14 | }, 15 | "*" 16 | ); 17 | break; 18 | } 19 | }; 20 | 21 | const handleMessage = async (message: any): Promise => { 22 | const rpcReply: { [key: string]: any } = { 23 | type: message.type, 24 | target: "page", 25 | method: "rpc", 26 | rpcId: message.rpcId, 27 | }; 28 | 29 | try { 30 | rpcReply.reply = await browser.runtime.sendMessage(message); 31 | } catch (error) { 32 | console.error(error.toString(), message); 33 | rpcReply.error = error.toString(); 34 | } 35 | 36 | window.postMessage(rpcReply, "*"); 37 | }; 38 | 39 | window.addEventListener("message", function (event) { 40 | if (event.source !== window || event?.data?.target !== "contentscript") { 41 | return; 42 | } 43 | 44 | switch (event.data.type) { 45 | case "bundle": 46 | handleFromBundleMessage(event.data); 47 | break; 48 | 49 | default: 50 | handleMessage(event.data); 51 | break; 52 | } 53 | }); 54 | 55 | browser.runtime.onMessage.addListener( 56 | async (message: any): Promise => { 57 | if (message.method === "ready") { 58 | bundleURL = message.bundle; 59 | return "ready"; 60 | } 61 | } 62 | ); 63 | -------------------------------------------------------------------------------- /src/icons/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stoically/radical-native/bb61bc8535fc2c9d7b7ccdafbc0d7466d0fdba4b/src/icons/32.png -------------------------------------------------------------------------------- /src/manifest.chrome.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Extending Riot Web with native messaging capabilities", 3 | "manifest_version": 2, 4 | "name": "Radical Native", 5 | "version": "0.1", 6 | "browser_action": { 7 | "default_title": "Radical Native", 8 | "default_icon": { 9 | "32": "icons/32.png" 10 | } 11 | }, 12 | "background": { 13 | "scripts": ["browser-polyfill.min.js", "background.js"] 14 | }, 15 | "permissions": [ 16 | "activeTab", 17 | "nativeMessaging", 18 | "storage", 19 | "webRequest", 20 | "webRequestBlocking" 21 | ], 22 | "optional_permissions": [""], 23 | "web_accessible_resources": ["resources/*"] 24 | } 25 | -------------------------------------------------------------------------------- /src/manifest.firefox.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Extending Riot Web with native messaging capabilities", 3 | "manifest_version": 2, 4 | "name": "Radical Native", 5 | "version": "0.1beta15", 6 | "icons": { 7 | "32": "icons/32.png" 8 | }, 9 | "browser_action": { 10 | "default_title": "Radical Native", 11 | "default_icon": { 12 | "32": "icons/32.png" 13 | } 14 | }, 15 | "background": { 16 | "scripts": ["background.js"] 17 | }, 18 | "permissions": [ 19 | "activeTab", 20 | "contextualIdentities", 21 | "cookies", 22 | "nativeMessaging", 23 | "storage", 24 | "webRequest", 25 | "webRequestBlocking" 26 | ], 27 | "optional_permissions": [""], 28 | "web_accessible_resources": ["resources/*"], 29 | "applications": { 30 | "gecko": { 31 | "id": "@radical-native", 32 | "strict_min_version": "68.0" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/resources/bundle.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /* eslint-disable @typescript-eslint/explicit-function-return-type */ 3 | 4 | let rpcId = 0; 5 | const rpcPromises: Map = new Map(); 6 | 7 | function rpcHandleMessage(message: any) { 8 | switch (message.method) { 9 | case "rpc": 10 | const rpcPromise = rpcPromises.get(message.rpcId); 11 | if (!rpcPromise) { 12 | console.log( 13 | "[RadicalNative::page] message received without matching rpcPromise", 14 | message 15 | ); 16 | return; 17 | } 18 | 19 | if (!message.error) { 20 | rpcPromise.resolve(message.reply); 21 | } else { 22 | rpcPromise.reject(message.error); 23 | } 24 | rpcPromises.delete(message.rpcId); 25 | break; 26 | } 27 | } 28 | 29 | function rpcPostMessage(type: string, message: any): Promise { 30 | return new Promise((resolve, reject) => { 31 | rpcId++; 32 | const rpcMessage = { 33 | type, 34 | target: "contentscript", 35 | rpcId, 36 | content: message, 37 | }; 38 | rpcPromises.set(rpcId, { 39 | message: rpcMessage, 40 | resolve, 41 | reject, 42 | }); 43 | window.postMessage(rpcMessage, "*"); 44 | }); 45 | } 46 | 47 | class SeshatIndexManager { 48 | supportsEventIndexing() { 49 | return rpcPostMessage("seshat", { method: "supportsEventIndexing" }); 50 | } 51 | 52 | initEventIndex() { 53 | return rpcPostMessage("seshat", { method: "initEventIndex" }); 54 | } 55 | 56 | addEventToIndex(ev: any, profile: any) { 57 | return rpcPostMessage("seshat", { 58 | method: "addEventToIndex", 59 | content: { ev, profile }, 60 | }); 61 | } 62 | 63 | isEventIndexEmpty() { 64 | return rpcPostMessage("seshat", { method: "isEventIndexEmpty" }); 65 | } 66 | 67 | isRoomIndexed(roomId: string): Promise { 68 | return rpcPostMessage("seshat", { 69 | method: "isRoomIndexed", 70 | content: { roomId }, 71 | }); 72 | } 73 | 74 | commitLiveEvents() { 75 | return rpcPostMessage("seshat", { method: "commitLiveEvents" }); 76 | } 77 | 78 | searchEventIndex(config: any) { 79 | const term = config.search_term; 80 | 81 | return rpcPostMessage("seshat", { 82 | method: "searchEventIndex", 83 | content: { term, config }, 84 | }); 85 | } 86 | 87 | addHistoricEvents(events: any, checkpoint: any, oldCheckpoint: any) { 88 | return rpcPostMessage("seshat", { 89 | method: "addHistoricEvents", 90 | content: { 91 | events, 92 | checkpoint, 93 | oldCheckpoint, 94 | }, 95 | }); 96 | } 97 | 98 | addCrawlerCheckpoint(checkpoint: any) { 99 | return rpcPostMessage("seshat", { 100 | method: "addCrawlerCheckpoint", 101 | content: { checkpoint }, 102 | }); 103 | } 104 | 105 | removeCrawlerCheckpoint(oldCheckpoint: any) { 106 | return rpcPostMessage("seshat", { 107 | method: "removeCrawlerCheckpoint", 108 | content: { oldCheckpoint }, 109 | }); 110 | } 111 | 112 | loadFileEvents(args: any) { 113 | return rpcPostMessage("seshat", { 114 | method: "loadFileEvents", 115 | content: { ...args }, 116 | }); 117 | } 118 | 119 | loadCheckpoints() { 120 | return rpcPostMessage("seshat", { method: "loadCheckpoints" }); 121 | } 122 | 123 | closeEventIndex() { 124 | return rpcPostMessage("seshat", { method: "closeEventIndex" }); 125 | } 126 | 127 | getStats() { 128 | return rpcPostMessage("seshat", { method: "getStats" }); 129 | } 130 | 131 | getUserVersion() { 132 | return rpcPostMessage("seshat", { method: "getUserVersion" }); 133 | } 134 | 135 | setUserVersion(version: number): Promise { 136 | return rpcPostMessage("seshat", { 137 | method: "setUserVersion", 138 | content: { version }, 139 | }); 140 | } 141 | 142 | deleteEventIndex() { 143 | return rpcPostMessage("seshat", { method: "deleteEventIndex" }); 144 | } 145 | 146 | deleteEvent(eventId: any) { 147 | return rpcPostMessage("seshat", { 148 | method: "deleteEvent", 149 | content: { eventId }, 150 | }); 151 | } 152 | } 153 | 154 | const indexManager = new SeshatIndexManager(); 155 | 156 | class PlatformPeg { 157 | private platform: any = null; 158 | 159 | get() { 160 | return this.platform; 161 | } 162 | 163 | set(plaf: any) { 164 | this.platform = plaf; 165 | this.platform.getEventIndexingManager = () => indexManager; 166 | 167 | this.platform.getPickleKey = async ( 168 | userId: string, 169 | deviceId: string 170 | ): Promise => { 171 | try { 172 | return await rpcPostMessage("keytar", { 173 | method: "getPickleKey", 174 | content: { userId, deviceId }, 175 | }); 176 | } catch (e) { 177 | return null; 178 | } 179 | }; 180 | 181 | this.platform.createPickleKey = async ( 182 | userId: string, 183 | deviceId: string 184 | ): Promise => { 185 | try { 186 | return await rpcPostMessage("keytar", { 187 | method: "createPickleKey", 188 | content: { userId, deviceId }, 189 | }); 190 | } catch (e) { 191 | return null; 192 | } 193 | }; 194 | 195 | this.platform.destroyPickleKey = async ( 196 | userId: string, 197 | deviceId: string 198 | ): Promise => { 199 | try { 200 | await rpcPostMessage("keytar", { 201 | method: "destroyPickleKey", 202 | content: { userId, deviceId }, 203 | }); 204 | } catch (e) {} 205 | }; 206 | } 207 | } 208 | 209 | interface Window { 210 | mxPlatformPeg: PlatformPeg; 211 | } 212 | 213 | window.mxPlatformPeg = new PlatformPeg(); 214 | 215 | const handleToBundleMessage = (message: any) => { 216 | switch (message.method) { 217 | case "init": 218 | const bundle = document.createElement("script"); 219 | bundle.src = message.bundle; 220 | bundle.async = true; 221 | document.body.append(bundle); 222 | break; 223 | } 224 | }; 225 | 226 | window.addEventListener("message", function (event) { 227 | if (event.source !== window || event?.data?.target !== "page") { 228 | return; 229 | } 230 | 231 | switch (event.data.type) { 232 | case "bundle": 233 | handleToBundleMessage(event.data); 234 | break; 235 | 236 | default: 237 | rpcHandleMessage(event.data); 238 | break; 239 | } 240 | }); 241 | 242 | window.postMessage( 243 | { 244 | type: "bundle", 245 | method: "ready", 246 | target: "contentscript", 247 | }, 248 | "*" 249 | ); 250 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "commonjs", 5 | "noEmit": true, 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "baseUrl": ".", 9 | "paths": { 10 | "~/*": ["/*"] 11 | } 12 | }, 13 | "include": ["src/**/*.ts", "test/**/*.ts"] 14 | } 15 | --------------------------------------------------------------------------------