├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── archive.rs ├── conf.rs ├── data.rs ├── encryption.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /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 = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "android_system_properties" 18 | version = "0.1.5" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 21 | dependencies = [ 22 | "libc", 23 | ] 24 | 25 | [[package]] 26 | name = "anyhow" 27 | version = "1.0.66" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" 30 | 31 | [[package]] 32 | name = "async-trait" 33 | version = "0.1.58" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" 36 | dependencies = [ 37 | "proc-macro2", 38 | "quote", 39 | "syn", 40 | ] 41 | 42 | [[package]] 43 | name = "atty" 44 | version = "0.2.14" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 47 | dependencies = [ 48 | "hermit-abi", 49 | "libc", 50 | "winapi", 51 | ] 52 | 53 | [[package]] 54 | name = "autocfg" 55 | version = "1.1.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 58 | 59 | [[package]] 60 | name = "base64" 61 | version = "0.13.1" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" 64 | 65 | [[package]] 66 | name = "bincode" 67 | version = "2.0.0-rc.2" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "7bb50c5a2ef4b9b1e7ae73e3a73b52ea24b20312d629f9c4df28260b7ad2c3c4" 70 | dependencies = [ 71 | "bincode_derive", 72 | "serde", 73 | ] 74 | 75 | [[package]] 76 | name = "bincode_derive" 77 | version = "2.0.0-rc.2" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "0a45a23389446d2dd25dc8e73a7a3b3c43522b630cac068927f0649d43d719d2" 80 | dependencies = [ 81 | "virtue", 82 | ] 83 | 84 | [[package]] 85 | name = "bitflags" 86 | version = "1.3.2" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 89 | 90 | [[package]] 91 | name = "block-buffer" 92 | version = "0.10.3" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" 95 | dependencies = [ 96 | "generic-array", 97 | ] 98 | 99 | [[package]] 100 | name = "bumpalo" 101 | version = "3.11.1" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" 104 | 105 | [[package]] 106 | name = "byteorder" 107 | version = "1.4.3" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 110 | 111 | [[package]] 112 | name = "bzip2" 113 | version = "0.4.3" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" 116 | dependencies = [ 117 | "bzip2-sys", 118 | "libc", 119 | ] 120 | 121 | [[package]] 122 | name = "bzip2-sys" 123 | version = "0.1.11+1.0.8" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" 126 | dependencies = [ 127 | "cc", 128 | "libc", 129 | "pkg-config", 130 | ] 131 | 132 | [[package]] 133 | name = "cc" 134 | version = "1.0.74" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" 137 | dependencies = [ 138 | "jobserver", 139 | ] 140 | 141 | [[package]] 142 | name = "cfg-if" 143 | version = "1.0.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 146 | 147 | [[package]] 148 | name = "chrono" 149 | version = "0.4.22" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" 152 | dependencies = [ 153 | "iana-time-zone", 154 | "js-sys", 155 | "num-integer", 156 | "num-traits", 157 | "time", 158 | "wasm-bindgen", 159 | "winapi", 160 | ] 161 | 162 | [[package]] 163 | name = "clap" 164 | version = "3.2.23" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" 167 | dependencies = [ 168 | "atty", 169 | "bitflags", 170 | "clap_derive", 171 | "clap_lex", 172 | "indexmap", 173 | "once_cell", 174 | "strsim", 175 | "termcolor", 176 | "textwrap", 177 | ] 178 | 179 | [[package]] 180 | name = "clap_derive" 181 | version = "3.2.18" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" 184 | dependencies = [ 185 | "heck", 186 | "proc-macro-error", 187 | "proc-macro2", 188 | "quote", 189 | "syn", 190 | ] 191 | 192 | [[package]] 193 | name = "clap_lex" 194 | version = "0.2.4" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 197 | dependencies = [ 198 | "os_str_bytes", 199 | ] 200 | 201 | [[package]] 202 | name = "codespan-reporting" 203 | version = "0.11.1" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 206 | dependencies = [ 207 | "termcolor", 208 | "unicode-width", 209 | ] 210 | 211 | [[package]] 212 | name = "config" 213 | version = "0.13.2" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "11f1667b8320afa80d69d8bbe40830df2c8a06003d86f73d8e003b2c48df416d" 216 | dependencies = [ 217 | "async-trait", 218 | "json5", 219 | "lazy_static", 220 | "nom", 221 | "pathdiff", 222 | "ron", 223 | "rust-ini", 224 | "serde", 225 | "serde_json", 226 | "toml", 227 | "yaml-rust", 228 | ] 229 | 230 | [[package]] 231 | name = "core-foundation-sys" 232 | version = "0.8.3" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 235 | 236 | [[package]] 237 | name = "cpufeatures" 238 | version = "0.2.5" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" 241 | dependencies = [ 242 | "libc", 243 | ] 244 | 245 | [[package]] 246 | name = "crc32fast" 247 | version = "1.3.2" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 250 | dependencies = [ 251 | "cfg-if", 252 | ] 253 | 254 | [[package]] 255 | name = "crossbeam-epoch" 256 | version = "0.9.11" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" 259 | dependencies = [ 260 | "autocfg", 261 | "cfg-if", 262 | "crossbeam-utils", 263 | "memoffset", 264 | "scopeguard", 265 | ] 266 | 267 | [[package]] 268 | name = "crossbeam-utils" 269 | version = "0.8.12" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" 272 | dependencies = [ 273 | "cfg-if", 274 | ] 275 | 276 | [[package]] 277 | name = "crypto-common" 278 | version = "0.1.6" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 281 | dependencies = [ 282 | "generic-array", 283 | "typenum", 284 | ] 285 | 286 | [[package]] 287 | name = "cxx" 288 | version = "1.0.80" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" 291 | dependencies = [ 292 | "cc", 293 | "cxxbridge-flags", 294 | "cxxbridge-macro", 295 | "link-cplusplus", 296 | ] 297 | 298 | [[package]] 299 | name = "cxx-build" 300 | version = "1.0.80" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" 303 | dependencies = [ 304 | "cc", 305 | "codespan-reporting", 306 | "once_cell", 307 | "proc-macro2", 308 | "quote", 309 | "scratch", 310 | "syn", 311 | ] 312 | 313 | [[package]] 314 | name = "cxxbridge-flags" 315 | version = "1.0.80" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" 318 | 319 | [[package]] 320 | name = "cxxbridge-macro" 321 | version = "1.0.80" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" 324 | dependencies = [ 325 | "proc-macro2", 326 | "quote", 327 | "syn", 328 | ] 329 | 330 | [[package]] 331 | name = "digest" 332 | version = "0.10.5" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" 335 | dependencies = [ 336 | "block-buffer", 337 | "crypto-common", 338 | ] 339 | 340 | [[package]] 341 | name = "directories" 342 | version = "4.0.1" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" 345 | dependencies = [ 346 | "dirs-sys", 347 | ] 348 | 349 | [[package]] 350 | name = "dirs-sys" 351 | version = "0.3.7" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" 354 | dependencies = [ 355 | "libc", 356 | "redox_users", 357 | "winapi", 358 | ] 359 | 360 | [[package]] 361 | name = "dlv-list" 362 | version = "0.3.0" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" 365 | 366 | [[package]] 367 | name = "filetime" 368 | version = "0.2.18" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" 371 | dependencies = [ 372 | "cfg-if", 373 | "libc", 374 | "redox_syscall", 375 | "windows-sys", 376 | ] 377 | 378 | [[package]] 379 | name = "foreign-types" 380 | version = "0.3.2" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 383 | dependencies = [ 384 | "foreign-types-shared", 385 | ] 386 | 387 | [[package]] 388 | name = "foreign-types-shared" 389 | version = "0.1.1" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 392 | 393 | [[package]] 394 | name = "form_urlencoded" 395 | version = "1.1.0" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 398 | dependencies = [ 399 | "percent-encoding", 400 | ] 401 | 402 | [[package]] 403 | name = "fs2" 404 | version = "0.4.3" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" 407 | dependencies = [ 408 | "libc", 409 | "winapi", 410 | ] 411 | 412 | [[package]] 413 | name = "fxhash" 414 | version = "0.2.1" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 417 | dependencies = [ 418 | "byteorder", 419 | ] 420 | 421 | [[package]] 422 | name = "generic-array" 423 | version = "0.14.6" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" 426 | dependencies = [ 427 | "typenum", 428 | "version_check", 429 | ] 430 | 431 | [[package]] 432 | name = "getrandom" 433 | version = "0.2.8" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 436 | dependencies = [ 437 | "cfg-if", 438 | "libc", 439 | "wasi 0.11.0+wasi-snapshot-preview1", 440 | ] 441 | 442 | [[package]] 443 | name = "git2" 444 | version = "0.14.4" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c" 447 | dependencies = [ 448 | "bitflags", 449 | "libc", 450 | "libgit2-sys", 451 | "log", 452 | "openssl-probe", 453 | "openssl-sys", 454 | "url", 455 | ] 456 | 457 | [[package]] 458 | name = "hashbrown" 459 | version = "0.12.3" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 462 | dependencies = [ 463 | "ahash", 464 | ] 465 | 466 | [[package]] 467 | name = "heck" 468 | version = "0.4.0" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 471 | 472 | [[package]] 473 | name = "hermit-abi" 474 | version = "0.1.19" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 477 | dependencies = [ 478 | "libc", 479 | ] 480 | 481 | [[package]] 482 | name = "iana-time-zone" 483 | version = "0.1.53" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" 486 | dependencies = [ 487 | "android_system_properties", 488 | "core-foundation-sys", 489 | "iana-time-zone-haiku", 490 | "js-sys", 491 | "wasm-bindgen", 492 | "winapi", 493 | ] 494 | 495 | [[package]] 496 | name = "iana-time-zone-haiku" 497 | version = "0.1.1" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" 500 | dependencies = [ 501 | "cxx", 502 | "cxx-build", 503 | ] 504 | 505 | [[package]] 506 | name = "idna" 507 | version = "0.3.0" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" 510 | dependencies = [ 511 | "unicode-bidi", 512 | "unicode-normalization", 513 | ] 514 | 515 | [[package]] 516 | name = "indexmap" 517 | version = "1.9.1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 520 | dependencies = [ 521 | "autocfg", 522 | "hashbrown", 523 | ] 524 | 525 | [[package]] 526 | name = "instant" 527 | version = "0.1.12" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 530 | dependencies = [ 531 | "cfg-if", 532 | ] 533 | 534 | [[package]] 535 | name = "itoa" 536 | version = "1.0.4" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" 539 | 540 | [[package]] 541 | name = "jobserver" 542 | version = "0.1.25" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" 545 | dependencies = [ 546 | "libc", 547 | ] 548 | 549 | [[package]] 550 | name = "js-sys" 551 | version = "0.3.60" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" 554 | dependencies = [ 555 | "wasm-bindgen", 556 | ] 557 | 558 | [[package]] 559 | name = "json5" 560 | version = "0.4.1" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" 563 | dependencies = [ 564 | "pest", 565 | "pest_derive", 566 | "serde", 567 | ] 568 | 569 | [[package]] 570 | name = "lazy_static" 571 | version = "1.4.0" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 574 | 575 | [[package]] 576 | name = "libc" 577 | version = "0.2.137" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" 580 | 581 | [[package]] 582 | name = "libgit2-sys" 583 | version = "0.13.4+1.4.2" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "d0fa6563431ede25f5cc7f6d803c6afbc1c5d3ad3d4925d12c882bf2b526f5d1" 586 | dependencies = [ 587 | "cc", 588 | "libc", 589 | "libssh2-sys", 590 | "libz-sys", 591 | "openssl-sys", 592 | "pkg-config", 593 | ] 594 | 595 | [[package]] 596 | name = "libssh2-sys" 597 | version = "0.2.23" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" 600 | dependencies = [ 601 | "cc", 602 | "libc", 603 | "libz-sys", 604 | "openssl-sys", 605 | "pkg-config", 606 | "vcpkg", 607 | ] 608 | 609 | [[package]] 610 | name = "libz-sys" 611 | version = "1.1.8" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" 614 | dependencies = [ 615 | "cc", 616 | "libc", 617 | "pkg-config", 618 | "vcpkg", 619 | ] 620 | 621 | [[package]] 622 | name = "link-cplusplus" 623 | version = "1.0.7" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" 626 | dependencies = [ 627 | "cc", 628 | ] 629 | 630 | [[package]] 631 | name = "linked-hash-map" 632 | version = "0.5.6" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 635 | 636 | [[package]] 637 | name = "lock_api" 638 | version = "0.4.9" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 641 | dependencies = [ 642 | "autocfg", 643 | "scopeguard", 644 | ] 645 | 646 | [[package]] 647 | name = "log" 648 | version = "0.4.17" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 651 | dependencies = [ 652 | "cfg-if", 653 | ] 654 | 655 | [[package]] 656 | name = "memchr" 657 | version = "2.5.0" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 660 | 661 | [[package]] 662 | name = "memoffset" 663 | version = "0.6.5" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 666 | dependencies = [ 667 | "autocfg", 668 | ] 669 | 670 | [[package]] 671 | name = "minimal-lexical" 672 | version = "0.2.1" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 675 | 676 | [[package]] 677 | name = "nom" 678 | version = "7.1.1" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" 681 | dependencies = [ 682 | "memchr", 683 | "minimal-lexical", 684 | ] 685 | 686 | [[package]] 687 | name = "num-integer" 688 | version = "0.1.45" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 691 | dependencies = [ 692 | "autocfg", 693 | "num-traits", 694 | ] 695 | 696 | [[package]] 697 | name = "num-traits" 698 | version = "0.2.15" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 701 | dependencies = [ 702 | "autocfg", 703 | ] 704 | 705 | [[package]] 706 | name = "once_cell" 707 | version = "1.16.0" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" 710 | 711 | [[package]] 712 | name = "openssl" 713 | version = "0.10.42" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" 716 | dependencies = [ 717 | "bitflags", 718 | "cfg-if", 719 | "foreign-types", 720 | "libc", 721 | "once_cell", 722 | "openssl-macros", 723 | "openssl-sys", 724 | ] 725 | 726 | [[package]] 727 | name = "openssl-macros" 728 | version = "0.1.0" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" 731 | dependencies = [ 732 | "proc-macro2", 733 | "quote", 734 | "syn", 735 | ] 736 | 737 | [[package]] 738 | name = "openssl-probe" 739 | version = "0.1.5" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 742 | 743 | [[package]] 744 | name = "openssl-sys" 745 | version = "0.9.77" 746 | source = "registry+https://github.com/rust-lang/crates.io-index" 747 | checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" 748 | dependencies = [ 749 | "autocfg", 750 | "cc", 751 | "libc", 752 | "pkg-config", 753 | "vcpkg", 754 | ] 755 | 756 | [[package]] 757 | name = "ordered-multimap" 758 | version = "0.4.3" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" 761 | dependencies = [ 762 | "dlv-list", 763 | "hashbrown", 764 | ] 765 | 766 | [[package]] 767 | name = "os_str_bytes" 768 | version = "6.3.1" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" 771 | 772 | [[package]] 773 | name = "parking_lot" 774 | version = "0.11.2" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" 777 | dependencies = [ 778 | "instant", 779 | "lock_api", 780 | "parking_lot_core", 781 | ] 782 | 783 | [[package]] 784 | name = "parking_lot_core" 785 | version = "0.8.5" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" 788 | dependencies = [ 789 | "cfg-if", 790 | "instant", 791 | "libc", 792 | "redox_syscall", 793 | "smallvec", 794 | "winapi", 795 | ] 796 | 797 | [[package]] 798 | name = "pathdiff" 799 | version = "0.2.1" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" 802 | 803 | [[package]] 804 | name = "percent-encoding" 805 | version = "2.2.0" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 808 | 809 | [[package]] 810 | name = "pest" 811 | version = "2.4.1" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" 814 | dependencies = [ 815 | "thiserror", 816 | "ucd-trie", 817 | ] 818 | 819 | [[package]] 820 | name = "pest_derive" 821 | version = "2.4.1" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "d5fd9bc6500181952d34bd0b2b0163a54d794227b498be0b7afa7698d0a7b18f" 824 | dependencies = [ 825 | "pest", 826 | "pest_generator", 827 | ] 828 | 829 | [[package]] 830 | name = "pest_generator" 831 | version = "2.4.1" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "d2610d5ac5156217b4ff8e46ddcef7cdf44b273da2ac5bca2ecbfa86a330e7c4" 834 | dependencies = [ 835 | "pest", 836 | "pest_meta", 837 | "proc-macro2", 838 | "quote", 839 | "syn", 840 | ] 841 | 842 | [[package]] 843 | name = "pest_meta" 844 | version = "2.4.1" 845 | source = "registry+https://github.com/rust-lang/crates.io-index" 846 | checksum = "824749bf7e21dd66b36fbe26b3f45c713879cccd4a009a917ab8e045ca8246fe" 847 | dependencies = [ 848 | "once_cell", 849 | "pest", 850 | "sha1", 851 | ] 852 | 853 | [[package]] 854 | name = "pkg-config" 855 | version = "0.3.26" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" 858 | 859 | [[package]] 860 | name = "ppv-lite86" 861 | version = "0.2.16" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 864 | 865 | [[package]] 866 | name = "proc-macro-error" 867 | version = "1.0.4" 868 | source = "registry+https://github.com/rust-lang/crates.io-index" 869 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 870 | dependencies = [ 871 | "proc-macro-error-attr", 872 | "proc-macro2", 873 | "quote", 874 | "syn", 875 | "version_check", 876 | ] 877 | 878 | [[package]] 879 | name = "proc-macro-error-attr" 880 | version = "1.0.4" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 883 | dependencies = [ 884 | "proc-macro2", 885 | "quote", 886 | "version_check", 887 | ] 888 | 889 | [[package]] 890 | name = "proc-macro2" 891 | version = "1.0.47" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 894 | dependencies = [ 895 | "unicode-ident", 896 | ] 897 | 898 | [[package]] 899 | name = "quote" 900 | version = "1.0.21" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 903 | dependencies = [ 904 | "proc-macro2", 905 | ] 906 | 907 | [[package]] 908 | name = "rand" 909 | version = "0.8.5" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 912 | dependencies = [ 913 | "libc", 914 | "rand_chacha", 915 | "rand_core", 916 | ] 917 | 918 | [[package]] 919 | name = "rand_chacha" 920 | version = "0.3.1" 921 | source = "registry+https://github.com/rust-lang/crates.io-index" 922 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 923 | dependencies = [ 924 | "ppv-lite86", 925 | "rand_core", 926 | ] 927 | 928 | [[package]] 929 | name = "rand_core" 930 | version = "0.6.4" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 933 | dependencies = [ 934 | "getrandom", 935 | ] 936 | 937 | [[package]] 938 | name = "redox_syscall" 939 | version = "0.2.16" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 942 | dependencies = [ 943 | "bitflags", 944 | ] 945 | 946 | [[package]] 947 | name = "redox_users" 948 | version = "0.4.3" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" 951 | dependencies = [ 952 | "getrandom", 953 | "redox_syscall", 954 | "thiserror", 955 | ] 956 | 957 | [[package]] 958 | name = "ron" 959 | version = "0.7.1" 960 | source = "registry+https://github.com/rust-lang/crates.io-index" 961 | checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" 962 | dependencies = [ 963 | "base64", 964 | "bitflags", 965 | "serde", 966 | ] 967 | 968 | [[package]] 969 | name = "rpassword" 970 | version = "6.0.1" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | checksum = "2bf099a1888612545b683d2661a1940089f6c2e5a8e38979b2159da876bfd956" 973 | dependencies = [ 974 | "libc", 975 | "serde", 976 | "serde_json", 977 | "winapi", 978 | ] 979 | 980 | [[package]] 981 | name = "rust-ini" 982 | version = "0.18.0" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" 985 | dependencies = [ 986 | "cfg-if", 987 | "ordered-multimap", 988 | ] 989 | 990 | [[package]] 991 | name = "ryu" 992 | version = "1.0.11" 993 | source = "registry+https://github.com/rust-lang/crates.io-index" 994 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 995 | 996 | [[package]] 997 | name = "same-file" 998 | version = "1.0.6" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1001 | dependencies = [ 1002 | "winapi-util", 1003 | ] 1004 | 1005 | [[package]] 1006 | name = "scopeguard" 1007 | version = "1.1.0" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1010 | 1011 | [[package]] 1012 | name = "scratch" 1013 | version = "1.0.2" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" 1016 | 1017 | [[package]] 1018 | name = "serde" 1019 | version = "1.0.147" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" 1022 | dependencies = [ 1023 | "serde_derive", 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "serde_derive" 1028 | version = "1.0.147" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" 1031 | dependencies = [ 1032 | "proc-macro2", 1033 | "quote", 1034 | "syn", 1035 | ] 1036 | 1037 | [[package]] 1038 | name = "serde_json" 1039 | version = "1.0.87" 1040 | source = "registry+https://github.com/rust-lang/crates.io-index" 1041 | checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" 1042 | dependencies = [ 1043 | "itoa", 1044 | "ryu", 1045 | "serde", 1046 | ] 1047 | 1048 | [[package]] 1049 | name = "serde_yaml" 1050 | version = "0.8.26" 1051 | source = "registry+https://github.com/rust-lang/crates.io-index" 1052 | checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" 1053 | dependencies = [ 1054 | "indexmap", 1055 | "ryu", 1056 | "serde", 1057 | "yaml-rust", 1058 | ] 1059 | 1060 | [[package]] 1061 | name = "sha1" 1062 | version = "0.10.5" 1063 | source = "registry+https://github.com/rust-lang/crates.io-index" 1064 | checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" 1065 | dependencies = [ 1066 | "cfg-if", 1067 | "cpufeatures", 1068 | "digest", 1069 | ] 1070 | 1071 | [[package]] 1072 | name = "size-display" 1073 | version = "0.1.4" 1074 | source = "registry+https://github.com/rust-lang/crates.io-index" 1075 | checksum = "9df415f09e1c4d4f58cd0cd08b2f5385bc07cf1878e7a7846abc2225fa00e1e1" 1076 | 1077 | [[package]] 1078 | name = "sled" 1079 | version = "0.34.7" 1080 | source = "registry+https://github.com/rust-lang/crates.io-index" 1081 | checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" 1082 | dependencies = [ 1083 | "crc32fast", 1084 | "crossbeam-epoch", 1085 | "crossbeam-utils", 1086 | "fs2", 1087 | "fxhash", 1088 | "libc", 1089 | "log", 1090 | "parking_lot", 1091 | ] 1092 | 1093 | [[package]] 1094 | name = "smallvec" 1095 | version = "1.10.0" 1096 | source = "registry+https://github.com/rust-lang/crates.io-index" 1097 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 1098 | 1099 | [[package]] 1100 | name = "strsim" 1101 | version = "0.10.0" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1104 | 1105 | [[package]] 1106 | name = "syn" 1107 | version = "1.0.103" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" 1110 | dependencies = [ 1111 | "proc-macro2", 1112 | "quote", 1113 | "unicode-ident", 1114 | ] 1115 | 1116 | [[package]] 1117 | name = "tar" 1118 | version = "0.4.38" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" 1121 | dependencies = [ 1122 | "filetime", 1123 | "libc", 1124 | "xattr", 1125 | ] 1126 | 1127 | [[package]] 1128 | name = "termcolor" 1129 | version = "1.1.3" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 1132 | dependencies = [ 1133 | "winapi-util", 1134 | ] 1135 | 1136 | [[package]] 1137 | name = "textwrap" 1138 | version = "0.16.0" 1139 | source = "registry+https://github.com/rust-lang/crates.io-index" 1140 | checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" 1141 | 1142 | [[package]] 1143 | name = "thiserror" 1144 | version = "1.0.37" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" 1147 | dependencies = [ 1148 | "thiserror-impl", 1149 | ] 1150 | 1151 | [[package]] 1152 | name = "thiserror-impl" 1153 | version = "1.0.37" 1154 | source = "registry+https://github.com/rust-lang/crates.io-index" 1155 | checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" 1156 | dependencies = [ 1157 | "proc-macro2", 1158 | "quote", 1159 | "syn", 1160 | ] 1161 | 1162 | [[package]] 1163 | name = "time" 1164 | version = "0.1.44" 1165 | source = "registry+https://github.com/rust-lang/crates.io-index" 1166 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 1167 | dependencies = [ 1168 | "libc", 1169 | "wasi 0.10.0+wasi-snapshot-preview1", 1170 | "winapi", 1171 | ] 1172 | 1173 | [[package]] 1174 | name = "tinyvec" 1175 | version = "1.6.0" 1176 | source = "registry+https://github.com/rust-lang/crates.io-index" 1177 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 1178 | dependencies = [ 1179 | "tinyvec_macros", 1180 | ] 1181 | 1182 | [[package]] 1183 | name = "tinyvec_macros" 1184 | version = "0.1.0" 1185 | source = "registry+https://github.com/rust-lang/crates.io-index" 1186 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1187 | 1188 | [[package]] 1189 | name = "toml" 1190 | version = "0.5.9" 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" 1192 | checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" 1193 | dependencies = [ 1194 | "serde", 1195 | ] 1196 | 1197 | [[package]] 1198 | name = "typenum" 1199 | version = "1.15.0" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" 1202 | 1203 | [[package]] 1204 | name = "ucd-trie" 1205 | version = "0.1.5" 1206 | source = "registry+https://github.com/rust-lang/crates.io-index" 1207 | checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" 1208 | 1209 | [[package]] 1210 | name = "unicode-bidi" 1211 | version = "0.3.8" 1212 | source = "registry+https://github.com/rust-lang/crates.io-index" 1213 | checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" 1214 | 1215 | [[package]] 1216 | name = "unicode-ident" 1217 | version = "1.0.5" 1218 | source = "registry+https://github.com/rust-lang/crates.io-index" 1219 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 1220 | 1221 | [[package]] 1222 | name = "unicode-normalization" 1223 | version = "0.1.22" 1224 | source = "registry+https://github.com/rust-lang/crates.io-index" 1225 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 1226 | dependencies = [ 1227 | "tinyvec", 1228 | ] 1229 | 1230 | [[package]] 1231 | name = "unicode-width" 1232 | version = "0.1.10" 1233 | source = "registry+https://github.com/rust-lang/crates.io-index" 1234 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 1235 | 1236 | [[package]] 1237 | name = "url" 1238 | version = "2.3.1" 1239 | source = "registry+https://github.com/rust-lang/crates.io-index" 1240 | checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" 1241 | dependencies = [ 1242 | "form_urlencoded", 1243 | "idna", 1244 | "percent-encoding", 1245 | ] 1246 | 1247 | [[package]] 1248 | name = "vcpkg" 1249 | version = "0.2.15" 1250 | source = "registry+https://github.com/rust-lang/crates.io-index" 1251 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1252 | 1253 | [[package]] 1254 | name = "version_check" 1255 | version = "0.9.4" 1256 | source = "registry+https://github.com/rust-lang/crates.io-index" 1257 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1258 | 1259 | [[package]] 1260 | name = "virtue" 1261 | version = "0.0.8" 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" 1263 | checksum = "7b60dcd6a64dd45abf9bd426970c9843726da7fc08f44cd6fcebf68c21220a63" 1264 | 1265 | [[package]] 1266 | name = "walkdir" 1267 | version = "2.3.2" 1268 | source = "registry+https://github.com/rust-lang/crates.io-index" 1269 | checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" 1270 | dependencies = [ 1271 | "same-file", 1272 | "winapi", 1273 | "winapi-util", 1274 | ] 1275 | 1276 | [[package]] 1277 | name = "wasi" 1278 | version = "0.10.0+wasi-snapshot-preview1" 1279 | source = "registry+https://github.com/rust-lang/crates.io-index" 1280 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 1281 | 1282 | [[package]] 1283 | name = "wasi" 1284 | version = "0.11.0+wasi-snapshot-preview1" 1285 | source = "registry+https://github.com/rust-lang/crates.io-index" 1286 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1287 | 1288 | [[package]] 1289 | name = "wasm-bindgen" 1290 | version = "0.2.83" 1291 | source = "registry+https://github.com/rust-lang/crates.io-index" 1292 | checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" 1293 | dependencies = [ 1294 | "cfg-if", 1295 | "wasm-bindgen-macro", 1296 | ] 1297 | 1298 | [[package]] 1299 | name = "wasm-bindgen-backend" 1300 | version = "0.2.83" 1301 | source = "registry+https://github.com/rust-lang/crates.io-index" 1302 | checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" 1303 | dependencies = [ 1304 | "bumpalo", 1305 | "log", 1306 | "once_cell", 1307 | "proc-macro2", 1308 | "quote", 1309 | "syn", 1310 | "wasm-bindgen-shared", 1311 | ] 1312 | 1313 | [[package]] 1314 | name = "wasm-bindgen-macro" 1315 | version = "0.2.83" 1316 | source = "registry+https://github.com/rust-lang/crates.io-index" 1317 | checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" 1318 | dependencies = [ 1319 | "quote", 1320 | "wasm-bindgen-macro-support", 1321 | ] 1322 | 1323 | [[package]] 1324 | name = "wasm-bindgen-macro-support" 1325 | version = "0.2.83" 1326 | source = "registry+https://github.com/rust-lang/crates.io-index" 1327 | checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" 1328 | dependencies = [ 1329 | "proc-macro2", 1330 | "quote", 1331 | "syn", 1332 | "wasm-bindgen-backend", 1333 | "wasm-bindgen-shared", 1334 | ] 1335 | 1336 | [[package]] 1337 | name = "wasm-bindgen-shared" 1338 | version = "0.2.83" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" 1341 | 1342 | [[package]] 1343 | name = "wateor" 1344 | version = "0.1.0" 1345 | dependencies = [ 1346 | "anyhow", 1347 | "bincode", 1348 | "bzip2", 1349 | "chrono", 1350 | "clap", 1351 | "config", 1352 | "directories", 1353 | "git2", 1354 | "openssl", 1355 | "rand", 1356 | "rpassword", 1357 | "serde", 1358 | "serde_yaml", 1359 | "size-display", 1360 | "sled", 1361 | "tar", 1362 | "walkdir", 1363 | ] 1364 | 1365 | [[package]] 1366 | name = "winapi" 1367 | version = "0.3.9" 1368 | source = "registry+https://github.com/rust-lang/crates.io-index" 1369 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1370 | dependencies = [ 1371 | "winapi-i686-pc-windows-gnu", 1372 | "winapi-x86_64-pc-windows-gnu", 1373 | ] 1374 | 1375 | [[package]] 1376 | name = "winapi-i686-pc-windows-gnu" 1377 | version = "0.4.0" 1378 | source = "registry+https://github.com/rust-lang/crates.io-index" 1379 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1380 | 1381 | [[package]] 1382 | name = "winapi-util" 1383 | version = "0.1.5" 1384 | source = "registry+https://github.com/rust-lang/crates.io-index" 1385 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1386 | dependencies = [ 1387 | "winapi", 1388 | ] 1389 | 1390 | [[package]] 1391 | name = "winapi-x86_64-pc-windows-gnu" 1392 | version = "0.4.0" 1393 | source = "registry+https://github.com/rust-lang/crates.io-index" 1394 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1395 | 1396 | [[package]] 1397 | name = "windows-sys" 1398 | version = "0.42.0" 1399 | source = "registry+https://github.com/rust-lang/crates.io-index" 1400 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 1401 | dependencies = [ 1402 | "windows_aarch64_gnullvm", 1403 | "windows_aarch64_msvc", 1404 | "windows_i686_gnu", 1405 | "windows_i686_msvc", 1406 | "windows_x86_64_gnu", 1407 | "windows_x86_64_gnullvm", 1408 | "windows_x86_64_msvc", 1409 | ] 1410 | 1411 | [[package]] 1412 | name = "windows_aarch64_gnullvm" 1413 | version = "0.42.0" 1414 | source = "registry+https://github.com/rust-lang/crates.io-index" 1415 | checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" 1416 | 1417 | [[package]] 1418 | name = "windows_aarch64_msvc" 1419 | version = "0.42.0" 1420 | source = "registry+https://github.com/rust-lang/crates.io-index" 1421 | checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" 1422 | 1423 | [[package]] 1424 | name = "windows_i686_gnu" 1425 | version = "0.42.0" 1426 | source = "registry+https://github.com/rust-lang/crates.io-index" 1427 | checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" 1428 | 1429 | [[package]] 1430 | name = "windows_i686_msvc" 1431 | version = "0.42.0" 1432 | source = "registry+https://github.com/rust-lang/crates.io-index" 1433 | checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" 1434 | 1435 | [[package]] 1436 | name = "windows_x86_64_gnu" 1437 | version = "0.42.0" 1438 | source = "registry+https://github.com/rust-lang/crates.io-index" 1439 | checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" 1440 | 1441 | [[package]] 1442 | name = "windows_x86_64_gnullvm" 1443 | version = "0.42.0" 1444 | source = "registry+https://github.com/rust-lang/crates.io-index" 1445 | checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" 1446 | 1447 | [[package]] 1448 | name = "windows_x86_64_msvc" 1449 | version = "0.42.0" 1450 | source = "registry+https://github.com/rust-lang/crates.io-index" 1451 | checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" 1452 | 1453 | [[package]] 1454 | name = "xattr" 1455 | version = "0.2.3" 1456 | source = "registry+https://github.com/rust-lang/crates.io-index" 1457 | checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" 1458 | dependencies = [ 1459 | "libc", 1460 | ] 1461 | 1462 | [[package]] 1463 | name = "yaml-rust" 1464 | version = "0.4.5" 1465 | source = "registry+https://github.com/rust-lang/crates.io-index" 1466 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 1467 | dependencies = [ 1468 | "linked-hash-map", 1469 | ] 1470 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wateor" 3 | version = "0.1.0" 4 | authors = ["Paul Sanford "] 5 | edition = "2021" 6 | license = "MIT" 7 | readme = "README.md" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | git2 = "0.14" 13 | anyhow = "1" 14 | tar = "0.4" 15 | bzip2 = "0.4" 16 | directories = "4" 17 | chrono = "0.4" 18 | sled = "0.34" 19 | bincode = "2.0.0-rc.1" 20 | openssl = "0.10" 21 | rand = "0.8" 22 | rpassword = "6" 23 | walkdir = "2.3" 24 | config = "0.13" 25 | size-display = "0.1" 26 | clap = { version = "3.1", features = ["derive"] } 27 | serde = { version = "1", features = ["derive"] } 28 | serde_yaml = "0.8" 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Paul Sanford 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wateor 2 | 3 | ## What is this? 4 | 5 | This command line tool will collect all the untracked files in your repo, create a bzip2-compressed tar file from them, encrypt them with a password-proteceted key, and store them in a specific folder on your computer. You can then easily restore them to their original locations, remove them, or decrypt them with the same tool. You can also run a command to clean up archives older than a certain number of days (perfect for a cronjob). It uses asymmetric cryptography to protect the files, so storing everything is one quick command, but you still need a password to restore or decrypt them. 6 | 7 | ## Why is this? 8 | 9 | I wrote this to solve a problem that may be one only I have: when I need to do some trouleshooting or ops-type stuff I'll open a new terminal (which, in my setup, defaults to the root of whatever repo I'm working in) and start working, spewing log files and temporary yaml configs all over the repo hierarchy. Then, I'll go to lunch, and come back ready to work on some code again, but my working tree is full of garbage. 10 | 11 | Sure, there's `git clean -f`, but.. what if there's something in there I need later? OK, so just copy it all to a `maybe_later` directory. But.. what if there's a password or key in there somewhere I don't want to leave rotting in a folder somewhere? I really just want one command to sweep it all into a box I can put on a shelf and forget about - and now I have one. 12 | 13 | ## Note 14 | 15 | This is a tool I developed in a few days to meet a need - I hope it's useful, but it's not a mature tool, so I wouldn't recommend using it to store or protect anything truly sensitive. 16 | 17 | ## Usage 18 | 19 | ``` 20 | wateor 0.1 21 | 22 | Paul Sanford 23 | 24 | Clean up files strewn about your git repo quickly and securely, with the option to restore them 25 | later or consign them to an (encrypted) black hole 26 | 27 | USAGE: 28 | wateor [OPTIONS] 29 | 30 | OPTIONS: 31 | -c, --config-file Path to the config file for the application. If not 32 | specified, looks in an OS-dependent default config directory 33 | -h, --help Print help information 34 | -V, --version Print version information 35 | 36 | SUBCOMMANDS: 37 | cleanup Remove archives older than a certain number of days 38 | config Serialize current config to yaml. This will be the combination of values 39 | specified in an existing config file, if any, and defaults for options not 40 | specified in the config file 41 | destroy Delete all data managed by wateor 42 | help Print this message or the help of the given subcommand(s) 43 | init Create the database and encryption keys used by wateor 44 | list List archives managed by wateor 45 | remove Remove a specific archive managed by wateor without restoring 46 | restore Decrypt an archive and restore its contents to their original locations in the 47 | repo 48 | store Gather, compress, and encrypt all untracked files in the repo 49 | ``` 50 | -------------------------------------------------------------------------------- /src/archive.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::File, 3 | io::Write, 4 | path::{Path, PathBuf}, 5 | }; 6 | 7 | use anyhow::{Context, Error, Result}; 8 | use bincode::{config, encode_to_vec}; 9 | use bzip2::{read::BzDecoder, write::BzEncoder, Compression}; 10 | use chrono::{DateTime, Local, Utc}; 11 | use git2::{Repository, Status}; 12 | use tar::{Archive, Builder}; 13 | 14 | use crate::{ 15 | conf::WateorConfig, 16 | data::{input_to_index, print_crate_description}, 17 | prompt, 18 | }; 19 | use crate::{ 20 | data::{Crate, WateorDb}, 21 | encryption::Crypto, 22 | }; 23 | 24 | pub struct Archiver { 25 | crypto: Crypto, 26 | db: WateorDb, 27 | repo: Repository, 28 | repo_path: PathBuf, 29 | config: WateorConfig, 30 | } 31 | 32 | impl Archiver { 33 | pub fn from_config(config: &WateorConfig) -> Result { 34 | let repo = Repository::discover(".") 35 | .context("Couldn't find a git repo from the current directory")?; 36 | let basepath = repo 37 | .workdir() 38 | .ok_or_else(|| Error::msg("Missing a work dir path. Is this a bare repo?"))?; 39 | 40 | let repo_path = PathBuf::from(basepath); 41 | 42 | let db = WateorDb::from_config(config)?; 43 | 44 | let crypto = Crypto::from_config(config)?; 45 | 46 | Ok(Self { 47 | crypto, 48 | db, 49 | repo, 50 | repo_path, 51 | config: config.clone(), 52 | }) 53 | } 54 | 55 | fn dirty_files(&self) -> Result> { 56 | Ok(self 57 | .repo 58 | .statuses(None)? 59 | .iter() 60 | .filter_map(|s| match (s.status(), s.path()) { 61 | (Status::CURRENT, _) | (_, None) => None, 62 | (_, Some(p)) => Some(PathBuf::from(p)), 63 | }) 64 | .collect()) 65 | } 66 | fn new_files(&self) -> Result> { 67 | Ok(self 68 | .repo 69 | .statuses(None)? 70 | .iter() 71 | .filter_map(|s| match (s.status(), s.path()) { 72 | (Status::WT_NEW, Some(p)) => Some(p.to_string()), 73 | _ => None, 74 | }) 75 | .collect()) 76 | } 77 | 78 | pub fn store(&self) -> Result<()> { 79 | let time = Utc::now().timestamp(); 80 | 81 | let new_paths = self.new_files()?; 82 | 83 | let archive = archive_files(&self.repo_path, new_paths, self.config.max_file_size_bytes)?; 84 | let encrypted = self.crypto.encrypt_archive(&archive.archive_data)?; 85 | 86 | let dt: DateTime = Local::now(); 87 | let file_name = dt.format("%Y-%m-%dT%H%M%S.tar.bz2").to_string(); 88 | let save_path = self.config.data_dir.join(file_name); 89 | let mut file = File::create(&save_path).with_context(|| { 90 | format!( 91 | "Failed to create archive file at {}", 92 | save_path.to_string_lossy() 93 | ) 94 | })?; 95 | 96 | file.write_all(&encrypted.encrypted_archive_data)?; 97 | 98 | let cr = Crate::new( 99 | time, 100 | save_path, 101 | &self.repo, 102 | &self.repo_path, 103 | archive.file_list.clone(), 104 | encrypted.encrypted_key, 105 | encrypted.iv, 106 | )?; 107 | 108 | self.db 109 | .db 110 | .insert(time.to_be_bytes(), encode_to_vec(cr, config::standard())?)?; 111 | 112 | for file in archive.file_list.iter().map(|p| self.repo_path.join(p)) { 113 | std::fs::remove_file(&file) 114 | .with_context(|| format!("Failed to remove file at {}", file.to_string_lossy()))?; 115 | } 116 | 117 | Ok(()) 118 | } 119 | 120 | fn iter_repo_crates(&self) -> impl Iterator + '_ { 121 | self.db 122 | .iter_crates() 123 | .filter(|cr| cr.repo_path == self.repo_path) 124 | } 125 | 126 | pub fn list(&self) { 127 | for (idx, cr) in self.iter_repo_crates().enumerate() { 128 | print_crate_description(cr, idx + 1); 129 | } 130 | } 131 | 132 | fn get_crate_description(&self, index: Option) -> Result { 133 | let index = input_to_index(index); 134 | self.iter_repo_crates() 135 | .nth(index) 136 | .ok_or_else(|| Error::msg(format!("Archive {} not found", index))) 137 | } 138 | 139 | pub fn remove(&self, index: Option) -> Result<()> { 140 | let cr = self.get_crate_description(index)?; 141 | self.db.delete(cr)?; 142 | Ok(()) 143 | } 144 | 145 | pub fn restore(&self, index: Option) -> Result { 146 | let cr = self.get_crate_description(index)?; 147 | let mut result = RestoreResult::Full; 148 | 149 | let pass = prompt("Passcode for key: ")?; 150 | let unencrypted = 151 | self.crypto 152 | .decrypt_archive(&pass, &cr.decryption_key, &cr.iv, &cr.archive_path)?; 153 | 154 | let decoder = BzDecoder::new(&*unencrypted); 155 | let mut tar = Archive::new(decoder); 156 | 157 | let non_current = self.dirty_files()?; 158 | 159 | println!("Restoring to {:#?}", self.repo_path); 160 | for entry in tar 161 | .entries() 162 | .context("Failed to read entries from archive")? 163 | { 164 | let mut entry = entry?; 165 | let path = entry.path()?; 166 | if non_current.iter().any(|p| p == &*path) { 167 | println!("{:#?} already in repo and dirty, skipping restore", path); 168 | result = RestoreResult::Partial; 169 | continue; 170 | } 171 | println!("Unpacking {:#?}", path); 172 | entry 173 | .unpack_in(&self.repo_path) 174 | .context("Couldn't unpack entry from archive")?; 175 | } 176 | 177 | Ok(result) 178 | } 179 | } 180 | 181 | #[derive(PartialEq, Eq)] 182 | pub enum RestoreResult { 183 | Full, 184 | Partial, 185 | } 186 | 187 | struct ArchiveResult { 188 | archive_data: Vec, 189 | file_list: Vec, 190 | } 191 | 192 | fn archive_files( 193 | base_path: &Path, 194 | paths: Vec, 195 | max_file_size_bytes: u64, 196 | ) -> Result { 197 | let mut back: Vec = Vec::new(); 198 | let mut encoder = BzEncoder::new(&mut back, Compression::default()); 199 | 200 | let mut stored_files = Vec::new(); 201 | 202 | { 203 | let mut tar = Builder::new(&mut encoder); 204 | 205 | for path in paths { 206 | let file_path = base_path.join(&path); 207 | { 208 | let f = File::open(&file_path).with_context(|| { 209 | format!("Couldn't open file at {}", file_path.to_string_lossy()) 210 | })?; 211 | let size = f.metadata()?.len(); 212 | if size > max_file_size_bytes { 213 | println!( 214 | "File {} is greater than {}, skipping", 215 | path, 216 | size_display::Size(max_file_size_bytes) 217 | ); 218 | continue; 219 | } 220 | } 221 | tar.append_path_with_name(file_path, &path)?; 222 | stored_files.push(path); 223 | } 224 | 225 | tar.finish()?; 226 | } 227 | 228 | encoder.finish()?; 229 | 230 | Ok(ArchiveResult { 231 | archive_data: back, 232 | file_list: stored_files, 233 | }) 234 | } 235 | -------------------------------------------------------------------------------- /src/conf.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Error, Result}; 2 | use config::{Config, Environment, File}; 3 | use directories::BaseDirs; 4 | use serde::Serialize; 5 | use std::path::PathBuf; 6 | 7 | #[derive(Clone, Serialize)] 8 | pub struct WateorConfig { 9 | pub data_dir: PathBuf, 10 | pub config_file: PathBuf, 11 | pub max_file_size_bytes: u64, 12 | pub remove_on_restore: bool, 13 | pub cleanup_older_than_days: i64, 14 | } 15 | 16 | fn get_default_config_path() -> Result { 17 | let bd = BaseDirs::new().ok_or_else(|| Error::msg("Couldn't init base dirs"))?; 18 | let mut config_dir = PathBuf::from(bd.config_dir()); 19 | config_dir.push("wateor"); 20 | config_dir.push("config.yaml"); 21 | Ok(config_dir) 22 | } 23 | 24 | fn get_default_data_dir() -> Result { 25 | let bd = BaseDirs::new().ok_or_else(|| Error::msg("Couldn't init base dirs"))?; 26 | let mut data_dir = PathBuf::from(bd.data_local_dir()); 27 | data_dir.push("wateor"); 28 | 29 | Ok(data_dir) 30 | } 31 | 32 | impl WateorConfig { 33 | pub fn read_config() -> Result { 34 | let config_path = get_default_config_path()?; 35 | WateorConfig::read_config_from(config_path) 36 | } 37 | 38 | pub fn read_config_from(config_path: PathBuf) -> Result { 39 | let settings = Config::builder() 40 | .add_source(File::from(config_path.clone()).required(false)) 41 | .add_source(Environment::with_prefix("WATEOR")) 42 | .build()?; 43 | 44 | let data_dir: PathBuf = if let Ok(data_dir) = settings.get("data_dir") { 45 | data_dir 46 | } else { 47 | get_default_data_dir()? 48 | }; 49 | 50 | let max_file_size_bytes = settings.get("max_file_size_bytes").unwrap_or(1_048_576_u64); 51 | 52 | let remove_on_restore = settings.get("remove_on_restore").unwrap_or(false); 53 | 54 | let cleanup_older_than_days = settings.get("cleanup_older_than_days").unwrap_or(30); 55 | 56 | Ok(Self { 57 | data_dir, 58 | config_file: config_path, 59 | max_file_size_bytes, 60 | remove_on_restore, 61 | cleanup_older_than_days, 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/data.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::File, 3 | io::Write, 4 | path::{Path, PathBuf}, 5 | }; 6 | 7 | use anyhow::{bail, Context, Error, Result}; 8 | use bincode::{config, decode_from_slice, Decode, Encode}; 9 | use chrono::{Duration, Local, TimeZone, Utc}; 10 | use git2::Repository; 11 | use openssl::{rsa::Rsa, symm::Cipher}; 12 | use sled::Db; 13 | 14 | use crate::{ 15 | conf::WateorConfig, 16 | encryption::{Crypto, PRIV_KEY_NAME, PUB_KEY_NAME}, 17 | prompt, 18 | }; 19 | 20 | pub static DB_FOLDER_NAME: &str = "wateor.db"; 21 | 22 | pub struct WateorDb { 23 | pub db: Db, 24 | } 25 | 26 | impl WateorDb { 27 | pub fn from_config(config: &WateorConfig) -> Result { 28 | let db = 29 | sled::open(config.data_dir.join(DB_FOLDER_NAME)).context("Couldn't open wateor db")?; 30 | 31 | Ok(Self { db }) 32 | } 33 | 34 | pub fn iter_crates(&self) -> impl Iterator { 35 | self.db 36 | .iter() 37 | .rev() 38 | .map::, _>(|def| Ok(decode_from_slice(&def?.1, config::standard())?.0)) 39 | .filter_map(std::result::Result::ok) 40 | } 41 | 42 | pub fn delete(&self, cr: Crate) -> Result<()> { 43 | std::fs::remove_file(cr.archive_path)?; 44 | self.db.remove(cr.timestamp.to_be_bytes())?; 45 | 46 | Ok(()) 47 | } 48 | 49 | pub fn list_all(&self) { 50 | for (idx, cr) in self.iter_crates().enumerate() { 51 | print_crate_description(cr, idx + 1); 52 | } 53 | } 54 | } 55 | 56 | #[derive(Encode, Decode)] 57 | pub struct Crate { 58 | pub timestamp: i64, 59 | pub archive_path: PathBuf, 60 | pub repo_path: PathBuf, 61 | pub branch: String, 62 | pub commit_id: String, 63 | pub file_list: Vec, 64 | pub decryption_key: Vec, 65 | pub iv: [u8; 16], 66 | } 67 | 68 | impl Crate { 69 | pub fn new( 70 | timestamp: i64, 71 | archive_path: PathBuf, 72 | repo: &Repository, 73 | repo_path: &Path, 74 | file_list: Vec, 75 | decryption_key: Vec, 76 | iv: [u8; 16], 77 | ) -> Result { 78 | let head = repo.head()?; 79 | let commit = head.peel_to_commit()?; 80 | 81 | Ok(Crate { 82 | timestamp, 83 | archive_path, 84 | repo_path: PathBuf::from(repo_path), 85 | branch: head 86 | .shorthand() 87 | .ok_or_else(|| Error::msg("Branch has non-utf8 shorthand"))? 88 | .to_string(), 89 | commit_id: commit.id().to_string(), 90 | file_list, 91 | decryption_key, 92 | iv, 93 | }) 94 | } 95 | } 96 | 97 | pub fn cleanup(config: &WateorConfig, days: Option) -> Result<()> { 98 | let db = WateorDb::from_config(config)?; 99 | let days = days.unwrap_or(config.cleanup_older_than_days); 100 | let ts = (Utc::now() - Duration::days(days as i64)).timestamp(); 101 | let crates_to_delete = db.iter_crates().filter(|cr| cr.timestamp < ts); 102 | 103 | for cr in crates_to_delete { 104 | db.delete(cr)?; 105 | } 106 | 107 | Ok(()) 108 | } 109 | 110 | pub fn check_init(config: &WateorConfig) -> bool { 111 | config.data_dir.join(DB_FOLDER_NAME).exists() 112 | } 113 | 114 | pub fn init(config: &WateorConfig) -> Result<()> { 115 | std::fs::create_dir_all(&config.data_dir).with_context(|| { 116 | format!( 117 | "Failed to create data directory at {}", 118 | config.data_dir.to_string_lossy() 119 | ) 120 | })?; 121 | let _db = WateorDb::from_config(config)?; 122 | println!("Initialized db"); 123 | let rsa = Rsa::generate(2048)?; 124 | let pass = prompt("Passcode for key: ")?; 125 | let confirm = prompt("Confirm password: ")?; 126 | if pass != confirm { 127 | bail!("Passwords don't match"); 128 | } 129 | let private_key: Vec = 130 | rsa.private_key_to_pem_passphrase(Cipher::aes_128_cbc(), pass.as_bytes())?; 131 | let key_path = config.data_dir.join(PRIV_KEY_NAME); 132 | let mut pkey = File::create(&key_path)?; 133 | let public_key = rsa.public_key_to_pem()?; 134 | pkey.write_all(&private_key) 135 | .context("Couldn't write private key file")?; 136 | println!("Created private key at {:#?}", key_path); 137 | let pub_path = config.data_dir.join(PUB_KEY_NAME); 138 | let mut public = File::create(&pub_path)?; 139 | public 140 | .write_all(&public_key) 141 | .context("Couldn't write public key file")?; 142 | println!("Created public key at {:#?}", pub_path); 143 | 144 | Ok(()) 145 | } 146 | 147 | pub fn destroy(config: &WateorConfig) -> Result<()> { 148 | let db = WateorDb::from_config(config)?; 149 | for archive in db.iter_crates() { 150 | std::fs::remove_file(&archive.archive_path).with_context(|| { 151 | format!( 152 | "Couldn't remove file at {}", 153 | archive.archive_path.to_string_lossy() 154 | ) 155 | })?; 156 | } 157 | println!( 158 | "Removing contents of data directory at {}", 159 | config.data_dir.to_string_lossy() 160 | ); 161 | std::fs::remove_dir_all(config.data_dir.join(DB_FOLDER_NAME)) 162 | .context("Couldn't remove wateor db folder")?; 163 | std::fs::remove_file(config.data_dir.join(PRIV_KEY_NAME)) 164 | .context("Couldn't remove private key file")?; 165 | std::fs::remove_file(config.data_dir.join(PUB_KEY_NAME)) 166 | .context("Couldn't remove public key file")?; 167 | Ok(()) 168 | } 169 | 170 | pub fn input_to_index(input: Option) -> usize { 171 | input.unwrap_or(1) - 1 172 | } 173 | 174 | pub fn print_crate_description(cr: Crate, idx: usize) { 175 | let dt = Local.timestamp(cr.timestamp, 0); 176 | println!("{}. Date: {}", idx, dt); 177 | println!(" Branch: {} (commit id {})", cr.branch, cr.commit_id); 178 | println!(" Files:"); 179 | for file in cr.file_list { 180 | println!(" {}", file); 181 | } 182 | } 183 | 184 | pub fn decrypt( 185 | config: &WateorConfig, 186 | index: Option, 187 | destination: Option, 188 | ) -> Result<()> { 189 | let db = WateorDb::from_config(config)?; 190 | let crypto = Crypto::from_config(config)?; 191 | let index = input_to_index(index); 192 | let cr = db 193 | .iter_crates() 194 | .nth(index) 195 | .ok_or_else(|| Error::msg(format!("Couldn't find archive {}", index)))?; 196 | let archive_name = PathBuf::from(&cr.archive_path); 197 | let archive_name = archive_name 198 | .file_name() 199 | .ok_or_else(|| Error::msg("Corrupt archive path"))?; 200 | let destination = destination 201 | .or_else(|| std::env::current_dir().ok()) 202 | .ok_or_else(|| Error::msg("Couldn't find an appropriate destination"))?; 203 | let mut destination = File::create(destination.join(archive_name))?; 204 | let pass = prompt("Passcode for key: ")?; 205 | let archive_data = 206 | crypto.decrypt_archive(&pass, &cr.decryption_key, &cr.iv, &cr.archive_path)?; 207 | destination.write_all(&archive_data)?; 208 | 209 | Ok(()) 210 | } 211 | -------------------------------------------------------------------------------- /src/encryption.rs: -------------------------------------------------------------------------------- 1 | use std::{fs::File, io::Read, path::Path}; 2 | 3 | use anyhow::{Context, Result}; 4 | use openssl::{ 5 | pkey::{Private, Public}, 6 | rsa::{Padding, Rsa}, 7 | symm::{decrypt, encrypt, Cipher}, 8 | }; 9 | use rand::Rng; 10 | 11 | use crate::conf::WateorConfig; 12 | 13 | pub static PRIV_KEY_NAME: &str = "key.pem"; 14 | pub static PUB_KEY_NAME: &str = "pub.pem"; 15 | 16 | pub struct Crypto { 17 | private_key_encrypted: Vec, 18 | public_key: Rsa, 19 | } 20 | 21 | impl Crypto { 22 | pub fn from_config(config: &WateorConfig) -> Result { 23 | let public_key = get_pub_key(&config.data_dir.join(PUB_KEY_NAME))?; 24 | let private_key_encrypted = get_priv_key_data(&config.data_dir.join(PRIV_KEY_NAME))?; 25 | 26 | Ok(Self { 27 | private_key_encrypted, 28 | public_key, 29 | }) 30 | } 31 | 32 | fn private_key(&self, pass: &str) -> Result> { 33 | Rsa::private_key_from_pem_passphrase(&self.private_key_encrypted, pass.as_bytes()) 34 | .context("Couldn't parse private key") 35 | } 36 | 37 | pub fn encrypt_archive(&self, unencrypted_data: &[u8]) -> Result { 38 | let key = rand::thread_rng().gen::<[u8; 16]>(); 39 | let iv = rand::thread_rng().gen::<[u8; 16]>(); 40 | 41 | let encrypted = encrypt(Cipher::aes_128_cbc(), &key, Some(&iv), unencrypted_data) 42 | .context("Failed to encrypt archive")?; 43 | 44 | let mut encrypted_key = vec![0; self.public_key.size() as usize]; 45 | self.public_key 46 | .public_encrypt(&key, &mut encrypted_key, Padding::PKCS1) 47 | .context("Failed to encrypt archive encryption key")?; 48 | 49 | Ok(EncryptedArchive { 50 | encrypted_archive_data: encrypted, 51 | encrypted_key, 52 | iv, 53 | }) 54 | } 55 | 56 | pub fn decrypt_archive( 57 | &self, 58 | pass: &str, 59 | encrypted_key: &[u8], 60 | iv: &[u8; 16], 61 | archive_path: &Path, 62 | ) -> Result> { 63 | let private_key = self.private_key(pass)?; 64 | let mut decryption_key = vec![0_u8; private_key.size() as usize]; 65 | private_key 66 | .private_decrypt(encrypted_key, &mut decryption_key, Padding::PKCS1) 67 | .context("Failed to decrypt archive encryption key")?; 68 | 69 | let mut file = File::open(archive_path).with_context(|| { 70 | format!( 71 | "Failed to open archive file at {}", 72 | archive_path.to_string_lossy() 73 | ) 74 | })?; 75 | let mut encrypted = Vec::new(); 76 | file.read_to_end(&mut encrypted)?; 77 | decrypt( 78 | Cipher::aes_128_cbc(), 79 | &decryption_key[..16], 80 | Some(iv), 81 | &encrypted, 82 | ) 83 | .with_context(|| { 84 | format!( 85 | "Failed to decrypt archive at {}", 86 | archive_path.to_string_lossy() 87 | ) 88 | }) 89 | } 90 | } 91 | 92 | fn get_pub_key(key_path: &Path) -> Result> { 93 | let mut key_cont = Vec::new(); 94 | File::open(key_path) 95 | .context("Couldn't open public key file")? 96 | .read_to_end(&mut key_cont)?; 97 | Rsa::public_key_from_pem(&key_cont).context("Couldn't parse public key") 98 | } 99 | 100 | fn get_priv_key_data(key_path: &Path) -> Result> { 101 | let mut key_cont = Vec::new(); 102 | File::open(key_path) 103 | .context("Couldn't open private key file")? 104 | .read_to_end(&mut key_cont)?; 105 | 106 | Ok(key_cont) 107 | } 108 | 109 | pub struct EncryptedArchive { 110 | pub encrypted_archive_data: Vec, 111 | pub encrypted_key: Vec, 112 | pub iv: [u8; 16], 113 | } 114 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | mod archive; 4 | mod conf; 5 | mod data; 6 | mod encryption; 7 | 8 | use anyhow::{bail, Context, Result}; 9 | use archive::{Archiver, RestoreResult}; 10 | use clap::Parser; 11 | use conf::WateorConfig; 12 | 13 | use crate::data::{check_init, cleanup, decrypt, destroy, init, WateorDb}; 14 | 15 | /// Clean up files strewn about your git repo quickly and securely, with 16 | /// the option to restore them later or consign them to an (encrypted) 17 | /// black hole. 18 | #[derive(Parser, Debug)] 19 | #[clap(version = "0.1", author = "Paul Sanford ")] 20 | struct Opts { 21 | /// Path to the config file for the application. If not specified, looks 22 | /// in an OS-dependent default config directory. 23 | #[clap(short, long)] 24 | config_file: Option, 25 | #[clap(subcommand)] 26 | command: Command, 27 | } 28 | 29 | #[derive(Parser, PartialEq, Debug)] 30 | enum Command { 31 | /// Create the database and encryption keys used by wateor. 32 | Init, 33 | /// Gather, compress, and encrypt all untracked files in the repo. 34 | #[clap(alias = "s")] 35 | Store, 36 | /// Decrypt an archive and restore its contents to their original locations 37 | /// in the repo. 38 | Restore(Restore), 39 | /// Decrypt a single archive without extracting it. 40 | Decrypt(Decrypt), 41 | /// List archives managed by wateor. 42 | #[clap(alias = "ls")] 43 | List(List), 44 | /// Remove a specific archive managed by wateor without restoring. 45 | #[clap(alias = "rm")] 46 | Remove(Remove), 47 | /// Remove archives older than a certain number of days. 48 | Cleanup(Cleanup), 49 | /// Serialize current config to yaml. This will be the combination of 50 | /// values specified in an existing config file, if any, and defaults for 51 | /// options not specified in the config file. 52 | Config, 53 | /// Delete all data managed by wateor. 54 | Destroy, 55 | } 56 | 57 | #[derive(Parser, PartialEq, Debug)] 58 | struct List { 59 | #[clap(short, long)] 60 | all: bool, 61 | } 62 | 63 | #[derive(Parser, PartialEq, Debug)] 64 | struct Decrypt { 65 | /// The index of the archive to decrypt. If not specified, the most recent 66 | /// archive is removed. Note that these indicies are not repo-specific, so 67 | /// you'll need to use list --all to find the index. 68 | index: Option, 69 | /// Directory to store the decrypted archive. If not specified, uses the 70 | /// current working directory. 71 | destination: Option, 72 | } 73 | 74 | #[derive(Parser, PartialEq, Debug)] 75 | struct Cleanup { 76 | /// Delete archives older than this number of days. 77 | older_than: Option, 78 | } 79 | 80 | #[derive(Parser, PartialEq, Debug)] 81 | struct Remove { 82 | /// The index of the archive to remove. If not specified, the most recent 83 | /// archive is removed. Find the index with the list command. 84 | index: Option, 85 | } 86 | 87 | #[derive(Parser, PartialEq, Debug)] 88 | struct Restore { 89 | /// The index of the archive to restore. If not specified, the most recent 90 | /// archive is restored. Find the index with the list command. 91 | index: Option, 92 | /// Remove the archive after restoration. 93 | #[clap(long, default_missing_value = "true")] 94 | rm: Option, 95 | } 96 | 97 | fn main() -> Result<()> { 98 | let opts: Opts = Opts::parse(); 99 | let config = WateorConfig::read_config().context("Couldn't read config file")?; 100 | if opts.command != Command::Init && !check_init(&config) { 101 | bail!("You must run init first"); 102 | } 103 | match opts.command { 104 | Command::Init => init(&config)?, 105 | Command::Store => Archiver::from_config(&config)?.store()?, 106 | Command::Restore(restore) => { 107 | let archiver = Archiver::from_config(&config)?; 108 | let result = archiver.restore(restore.index)?; 109 | if restore.rm.unwrap_or(config.remove_on_restore) { 110 | if result == RestoreResult::Full { 111 | archiver.remove(restore.index)?; 112 | } else { 113 | eprintln!("Some files could not be restored; archive not deleted"); 114 | } 115 | } 116 | } 117 | Command::Decrypt(d) => decrypt(&config, d.index, d.destination)?, 118 | Command::Remove(remove) => Archiver::from_config(&config)?.remove(remove.index)?, 119 | Command::List(list) if list.all => WateorDb::from_config(&config)?.list_all(), 120 | Command::List(_) => Archiver::from_config(&config)?.list(), 121 | Command::Cleanup(c) => cleanup(&config, c.older_than)?, 122 | Command::Config => println!("{}", serde_yaml::to_string(&config)?), 123 | Command::Destroy => destroy(&config)?, 124 | } 125 | 126 | Ok(()) 127 | } 128 | 129 | fn prompt(prompt: &str) -> Result { 130 | Ok(rpassword::prompt_password(prompt)?) 131 | } 132 | --------------------------------------------------------------------------------