Compare commits
757 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9eb4703d7a | |||
| 105752fc65 | |||
| 2747e93316 | |||
| 9548f984eb | |||
| cb871ce4bc | |||
| 8ca849b7a8 | |||
| 4bb29b1b5c | |||
| e55e893c94 | |||
| 5ab63a290e | |||
| 7c3414b86f | |||
| cec8829032 | |||
| 78f9f49a8a | |||
| 5a7722fd18 | |||
| d111a979f7 | |||
| 31514c8e31 | |||
| af5ce101ef | |||
| 075da27d13 | |||
| 7b19fb44a4 | |||
| c991946ea7 | |||
| f960a3ae38 | |||
| 73f8811a4b | |||
| bc6ec2579a | |||
| 35bc7263da | |||
| cc3db00a06 | |||
| 7f7961ae0c | |||
| aae60b2ef8 | |||
| ab700543b9 | |||
| 413488f5f4 | |||
| 0ceee14952 | |||
| b4b998df08 | |||
| d6bb165de5 | |||
| ac69f63c89 | |||
| ce5b6c9f64 | |||
| 9d800324af | |||
| e0603f741f | |||
| ce006d0e5b | |||
| 5fb9a9f164 | |||
| 351cd29050 | |||
| 6c160b719a | |||
| d1a7ca7822 | |||
| ee515394c0 | |||
| b2efed71d3 | |||
| 9035dc6bf7 | |||
| 6e5a25dac4 | |||
| af80b07b01 | |||
| 0d93fdf23d | |||
| db2379e2fd | |||
| 9a3900114b | |||
| 6b1d689621 | |||
| 2f7ce565f0 | |||
| 77b9cab07b | |||
| f9fe4e9c3d | |||
| cd32f0ff6b | |||
| 756a796e1d | |||
| 72e949c644 | |||
| 58ba3b012e | |||
| 1854256a93 | |||
| a31bf17469 | |||
| ca7d7ab675 | |||
| 20c802a1e5 | |||
| d1cbf4f06c | |||
| 86443252b1 | |||
| 653727fd12 | |||
| 7a3354f654 | |||
| e9ebee180e | |||
| a93259f3bd | |||
| 8f6c012fb3 | |||
| a635b023f6 | |||
| 1cc7ea5ca7 | |||
| 1b9f874db5 | |||
| cf5ae8f291 | |||
| 96878e2247 | |||
| 80fad573fa | |||
| 1d2a1eee81 | |||
| baecdc4d4f | |||
| 310e6ffc0d | |||
| 13f6e50354 | |||
| f76aec8b5a | |||
| 40fb9de15e | |||
| 0630edc626 | |||
| f2ef6fa12f | |||
| e9616a2d3e | |||
| de5a4cd8cb | |||
| 3b18f12ff2 | |||
| 88bb7a7e5b | |||
| 8fe4ce456f | |||
| 8a7c56e8fd | |||
| 43ac21fd66 | |||
| 17a854e8e1 | |||
| 09c67dd557 | |||
| d837b409e8 | |||
| 994a000e36 | |||
| 5ae50047e0 | |||
| 4e47e7ac2a | |||
| 22a3549599 | |||
| 8bb2a399cc | |||
| 2780dc6a67 | |||
| 421129029d | |||
| cd35df6cc5 | |||
| 61c787b1c7 | |||
| 3c6f80e520 | |||
| 8592153c0f | |||
| 958334bd49 | |||
| 6bbe2d0e00 | |||
| 9786deef48 | |||
| 9af1c1671c | |||
| ce743fe95d | |||
| a9c038bcb6 | |||
| 35bc5de40f | |||
| fb1494fc81 | |||
| e3da0fe255 | |||
| 4443e39785 | |||
| 796c617569 | |||
| 275a92ae93 | |||
| 35d2cc9be7 | |||
| 264c2b2f90 | |||
| a520d636e8 | |||
| 090aaf8ee3 | |||
| 34a9d1d125 | |||
| 40b3f77db0 | |||
| 0c7453684b | |||
| 310c6a1ccf | |||
| 4c52a12507 | |||
| 1a8e4c953d | |||
| 8bf33e211d | |||
| c49c296d2b | |||
| ee5a126c1c | |||
| e4f08f79c3 | |||
| 2aaec3b6bd | |||
| 743a2f8dac | |||
| aa5c3042da | |||
| f221fead4a | |||
| af51018e02 | |||
| ed904c2bdd | |||
| 4ed9625959 | |||
| 42e9b6d2f3 | |||
| a8788feb50 | |||
| 22a8aab151 | |||
| f44d1c4b9d | |||
| a28bd09365 | |||
| 345cc45a3e | |||
| 3f189c430b | |||
| 207ff70680 | |||
| 5113d52444 | |||
| fd8abc168d | |||
| 033139677b | |||
| 2e4128dcfe | |||
| 0a1f349901 | |||
| 62a589b6ad | |||
| 055829dcf8 | |||
| 649364beb5 | |||
| d3f9756bdb | |||
| 7447d9a55a | |||
| 70511dd0f2 | |||
| 664f81249c | |||
| 8f2e616e07 | |||
| 72708d6e2c | |||
| 7a633ee8c8 | |||
| c11fe3e1ab | |||
| a4e54f063d | |||
| b5321f8993 | |||
| bcf799732f | |||
| 13ba2182c2 | |||
| 0d25c607e7 | |||
| b3f8866ef7 | |||
| 9bb16dec48 | |||
| bdb35f1c1d | |||
| d421b5aa5a | |||
| 1ec05e8a6c | |||
| 5b941013de | |||
| a93ed35eee | |||
| 76469969f3 | |||
| 8b39ea4acb | |||
| 252ca9a5f9 | |||
| c4eb1a0f5b | |||
| 1e2f4e9ebb | |||
| 2a7aefac45 | |||
| ea39e2d842 | |||
| fc5879a204 | |||
| 5ae2229e37 | |||
| 12e5ce0ff0 | |||
| 5ef3774d11 | |||
| 654e816e6b | |||
| 7cad7bcddb | |||
| 136d514cf7 | |||
| 6e48345d54 | |||
| 8ebdb466f7 | |||
| 1ed7b690a5 | |||
| 5c28a3eda7 | |||
| f40f002bf9 | |||
| 69d1789a03 | |||
| 4edf2eb92c | |||
| 098956b81a | |||
| 2aa665ae38 | |||
| ba712516ff | |||
| 865ac44037 | |||
| 7d41062ae9 | |||
| f3c69faf8b | |||
| e353dc554d | |||
| 415d08b411 | |||
| 62499a5630 | |||
| d6d7ea592e | |||
| 5033e9718c | |||
| 16f9dc43cb | |||
| b8f27cc7d2 | |||
| 6b10da524c | |||
| 51eb2c42cd | |||
| c94d839fbb | |||
| de586e5f12 | |||
| c32a106898 | |||
| 5b20b6a3d0 | |||
| 3b07121f08 | |||
| a53bc4b027 | |||
| 478345e277 | |||
| 0ed78f1ccb | |||
| 6671dd38ea | |||
| 2d5ea669a5 | |||
| c7eb7234a2 | |||
| 73d1fe2f65 | |||
| cf75ea739f | |||
| c920c53243 | |||
| 63379001e3 | |||
| aa8cc3fc4b | |||
| 61e4ca5814 | |||
| 8e0693ab03 | |||
| a3d2df9d38 | |||
| f9f4ce996d | |||
| fc69b9aabb | |||
| f7ed3abcfe | |||
| c2ffae3799 | |||
| 437b7a4cfe | |||
| df601ecbbd | |||
| e9c05c5a6b | |||
| 22f427d522 | |||
| d356f306d9 | |||
| e717b69139 | |||
| 1dbd37d05b | |||
| 7dad6cc9a4 | |||
| 0332a3f873 | |||
| 20a0404efb | |||
| 29b7530ddf | |||
| 27e7d7967d | |||
| d40fbda2ab | |||
| 3bb9146d9f | |||
| f0d05aeb79 | |||
| ad6b84d4e0 | |||
| 38031b2fb9 | |||
| b74dba884f | |||
| 7276c23b2b | |||
| f0b1ab55a2 | |||
| f851f4d5c2 | |||
| f2d568d92f | |||
| a0dc764bb9 | |||
| 55beb9227f | |||
| 6ed97a0347 | |||
| 7ce3529f5d | |||
| ed9edb3620 | |||
| f30269865d | |||
| d7c5ace8e4 | |||
| b82e2ca176 | |||
| 5af3e930ec | |||
| 72cd641c72 | |||
| 4a2ac813d3 | |||
| 961742fa53 | |||
| 9984165798 | |||
| 551f5c3c18 | |||
| 41f2ffa4ec | |||
| 778b17c44e | |||
| 5d51cc1739 | |||
| a7270102af | |||
| ca5c45b1c5 | |||
| 31920a4468 | |||
| 2e52b8db87 | |||
| 2899e7bb15 | |||
| 41e15db442 | |||
| b5b477a3ce | |||
| 07b7fa7364 | |||
| 5637ca2529 | |||
| a93a8e7be9 | |||
| db7ead3901 | |||
| 42ced6694e | |||
| af0c5e6bae | |||
| cf6ed81a00 | |||
| 8c9d5c54fc | |||
| 36ec9b07e0 | |||
| d9847ddd6a | |||
| e1747357bc | |||
| 77e352a101 | |||
| b41c4d2fa6 | |||
| b6ad1fe490 | |||
| bc21bb1d8d | |||
| 8ea610c625 | |||
| 10da4f284c | |||
| e49d2e1be7 | |||
| b259de238e | |||
| ea821b1bd8 | |||
| 94347d95df | |||
| ef051d5ed6 | |||
| e40e8e3e75 | |||
| 9d0368de97 | |||
| c5699700b3 | |||
| 1141ea27e2 | |||
| ecc1c34b16 | |||
| 3601adcae6 | |||
| d11cf57879 | |||
| 2c8feff97a | |||
| b7adccf651 | |||
| 726c8918ab | |||
| 29af8e7178 | |||
| 18257f0302 | |||
| aeceb7d593 | |||
| 85c06809d2 | |||
| f65e050588 | |||
| e0d07d67a0 | |||
| 0a9748a15d | |||
| 6bd0739013 | |||
| 5cb893fc1b | |||
| f5624c9932 | |||
| 2b1daa60bb | |||
| ffb18adfd0 | |||
| 649195cc2b | |||
| b0ce46ca8a | |||
| 6435f7b09a | |||
| 59075f2e26 | |||
| 1d9855a190 | |||
| cea33bebe2 | |||
| 9d405a1549 | |||
| 47f468e4b7 | |||
| b9c6c00709 | |||
| 5ce9cb8eec | |||
| bc7133e401 | |||
| a219ecf3cb | |||
| 8061b1e6fa | |||
| 6509df523f | |||
| 0d1abaec0d | |||
| 4d1ace5de7 | |||
| 1250621a4d | |||
| 8d6e55ba54 | |||
| a4a29cbf82 | |||
| 39bccc2747 | |||
| 0cf1b38c2b | |||
| 6b7e706100 | |||
| bb90ed5446 | |||
| 107843d58f | |||
| 63f089540e | |||
| c35ff4f913 | |||
| cd6b5cdcc3 | |||
| 8f1ca00c9d | |||
| f21f583d05 | |||
| e940d9f6fe | |||
| 1ed8e939b8 | |||
| b5d63783f7 | |||
| e0113ec267 | |||
| 22d2bcc21d | |||
| 91dcb2f773 | |||
| c676c732ab | |||
| 444f2d8a12 | |||
| f10da3c7f0 | |||
| b8dd9f82bd | |||
| 1157e60972 | |||
| e9e4d8c725 | |||
| 186fa24106 | |||
| 63780b7b8d | |||
| e3e4769d78 | |||
| b2e9c4e4e9 | |||
| db4cb36538 | |||
| 984864553e | |||
| 2707a5627c | |||
| 8e0d6d41a6 | |||
| 2b76a45e17 | |||
| 7ab54da8c4 | |||
| 2cce29e858 | |||
| ef1223391b | |||
| fb98a797ba | |||
| 6be31bdeb2 | |||
| fce5990d19 | |||
| 1d4ee0c33e | |||
| a3e102e456 | |||
| 21dcac9fac | |||
| f0ee82fdd2 | |||
| 0c6a098af9 | |||
| 5bf359d34f | |||
| 6d784f2444 | |||
| 835bf1e77f | |||
| df5fbda72f | |||
| c482f768d9 | |||
| 21cf7459c9 | |||
| cf1ba6588a | |||
| 858f2c7f29 | |||
| f63238faed | |||
| f6ff85f69d | |||
| ec5b5939b9 | |||
| dec00ff9cc | |||
| 9fddd77f0d | |||
| aae65c9d38 | |||
| f3b197fa56 | |||
| 0a9ce5f526 | |||
| a2029002c4 | |||
| 7c41c8e23a | |||
| 36fdb88d96 | |||
| c69239ca16 | |||
| e10aa89313 | |||
| d0a97a3f4a | |||
| e01dc77a61 | |||
| 509ba52ba2 | |||
| c37a0338c5 | |||
| 9f23d5a6f4 | |||
| 885fb95454 | |||
| 629d6c5e4d | |||
| 3f50bf66f4 | |||
| 4072205709 | |||
| 233c55ab19 | |||
| 5d82c218ca | |||
| cb30dd91e3 | |||
| 41d82e10f9 | |||
| 8496c9e181 | |||
| 3dadad5131 | |||
| 00146e7474 | |||
| 12ac47e949 | |||
| 6ff4c8a738 | |||
| dd66b7f8d0 | |||
| 0b95ed4dea | |||
| ce64aeb05f | |||
| 27cfda680d | |||
| 323303a98b | |||
| 8109831c07 | |||
| 2284e9ede1 | |||
| 1d538e8540 | |||
| 8ccaac8090 | |||
| 22bf8f62ce | |||
| fed031ebaa | |||
| 7a15ebbd54 | |||
| 94b5799ba7 | |||
| 286f51a4e7 | |||
| ee961ae4a8 | |||
| 4038752a9a | |||
| ebf724412b | |||
| 14d42b5e76 | |||
| 2b8d92e82d | |||
| 11b1e3acf5 | |||
| c5eb660315 | |||
| 5ad23715ec | |||
| 8ab05a000c | |||
| 454d248819 | |||
| 6c8e5f7cd3 | |||
| f5aba717b2 | |||
| 1359c39bc0 | |||
| 4850681f1d | |||
| aa55c69307 | |||
| 1f19d4df75 | |||
| c0f6af9eb5 | |||
| ef6a3d4999 | |||
| 50550d42b4 | |||
| 8db89a1a6c | |||
| ba1dfb1bf4 | |||
| d243880753 | |||
| cccaaa3d82 | |||
| 2d95f21567 | |||
| 7d0af7624c | |||
| 2f35c453a1 | |||
| 05dd137bc8 | |||
| 767628946f | |||
| d4efa7131f | |||
| 144cf6e40c | |||
| a205d8c046 | |||
| cccadaee42 | |||
| bbb365f8a5 | |||
| 1f18d9d917 | |||
| 59e0d63485 | |||
| 72fe5a636e | |||
| 45a83133ba | |||
| 215eb4d6eb | |||
| 479b951c50 | |||
| a94c8a943f | |||
| ea306f405e | |||
| 1b405506b8 | |||
| 38c6132f81 | |||
| b7351dfaf8 | |||
| 7e8f6943f2 | |||
| a0132e8440 | |||
| 27541784aa | |||
| 9e567f08b2 | |||
| bf274f984e | |||
| 3b60bbe13b | |||
| a73a1b623a | |||
| c0a8877018 | |||
| 904166c01c | |||
| 4761bc935a | |||
| 71301d891f | |||
| d47be3c4c0 | |||
| 199a4d1e3a | |||
| 18668aafc9 | |||
| fd73ec6861 | |||
| feeb7179f5 | |||
| 0e5a45671f | |||
| 2beb0d298e | |||
| 22a6fcd87f | |||
| f499252444 | |||
| b27e3fdb28 | |||
| 415e56d928 | |||
| 845074f421 | |||
| 28f46deef9 | |||
| 2a078b76e6 | |||
| 3428557b15 | |||
| 1f25aeab31 | |||
| 4e531d4524 | |||
| 7fc7083c76 | |||
| 0fe69d9de1 | |||
| 8b436186a4 | |||
| 4d000c2376 | |||
| 56bce8e06f | |||
| 6fd614595d | |||
| 7bb7e1a518 | |||
| fb89fb7b31 | |||
| e6ae344f1f | |||
| bad8cad97d | |||
| 77cd2955f1 | |||
| 567b65df8d | |||
| 06b3ed9b85 | |||
| 565c0b6ddf | |||
| df318382b7 | |||
| 341487d839 | |||
| 7468ed7dc0 | |||
| c6107dbd4b | |||
| bcef1c36ba | |||
| 6d9d5f35ca | |||
| 6299a6d390 | |||
| 4ab5635293 | |||
| 5c9e9caa2f | |||
| e055acb8eb | |||
| 72c01046e3 | |||
| 46bc8b08dc | |||
| 00b5046653 | |||
| 21d8ef649f | |||
| 6c96643d12 | |||
| 9193205834 | |||
| 15df130d76 | |||
| 4554369292 | |||
| 50d167a983 | |||
| 7065211064 | |||
| 0069eb9a0c | |||
| 35b5b925cf | |||
| d4df2ea348 | |||
| 0159f24f17 | |||
| 619d5eaec9 | |||
| 52804c7039 | |||
| 837e0d3758 | |||
| fa3829cbfe | |||
| 0679b99a65 | |||
| 4ffa62f6ca | |||
| 0c458f709f | |||
| d1daa02b35 | |||
| 26cdfdeba9 | |||
| f4405b5186 | |||
| 76dda10572 | |||
| 0cde1ab801 | |||
| d9c9edf4d7 | |||
| 29f034abdc | |||
| 62a64cde61 | |||
| 9747145a3c | |||
| e2a30d1ac6 | |||
| 3168cbb77d | |||
| 0a0cc0a62c | |||
| adcf0827ee | |||
| b9ee4a152a | |||
| cb839ff149 | |||
| 45efdad27e | |||
| 6ef2bb254d | |||
| 645a8257d9 | |||
| 8ab852277c | |||
| 516ca018d3 | |||
| 5117672388 | |||
| a468ce635c | |||
| 5c58089fb7 | |||
| b3a64892fe | |||
| 3e9c4ba614 | |||
| 8cd17addbe | |||
| 2feaba8888 | |||
| 1909ceed67 | |||
| 07c100bd66 | |||
| ab4776c332 | |||
| f17e0d761e | |||
| a5b9f4c3f1 | |||
| a72f52a5ed | |||
| 6523b906af | |||
| 5d246d449c | |||
| 0b39b2adf6 | |||
| 25a8c1962b | |||
| 9bb7c828cd | |||
| f18dcf9db3 | |||
| 44f8a49b47 | |||
| 10301b8600 | |||
| 32db6b8d44 | |||
| debf015dd0 | |||
| 4013892a47 | |||
| d5277454c6 | |||
| a9f44731dc | |||
| 48808992ec | |||
| 036bc88789 | |||
| 67a7d556ec | |||
| 5ad338e835 | |||
| e442c47eed | |||
| 5380edeeb9 | |||
| e50d1d01da | |||
| 082a803e47 | |||
| 07d9bc0831 | |||
| 4e5a1d4b30 | |||
| 4a54f878c4 | |||
| dc3b4d53e1 | |||
| be583c431e | |||
| 805544ffb0 | |||
| 7b84038bf4 | |||
| e8cbbaa832 | |||
| 56e32e67de | |||
| b36ac532c9 | |||
| d3b0871cf1 | |||
| 7b4204591c | |||
| 4514d72d70 | |||
| dfbf25a9f4 | |||
| 122eac50a6 | |||
| 839708dcfe | |||
| d2066173f0 | |||
| eccad4bbfd | |||
| 98ab794f13 | |||
| b7b2297635 | |||
| dc3f61acee | |||
| 6fffb460b8 | |||
| c677b78f16 | |||
| 014c8af560 | |||
| 2f149eb545 | |||
| 175f0977f9 | |||
| 8fe042218c | |||
| 9fe3718d3f | |||
| 1839f072b4 | |||
| a2cf1b6022 | |||
| 0a4fb4594a | |||
| eb1176eba6 | |||
| a89dfc4524 | |||
| 1ba378dace | |||
| 25c1014ab0 | |||
| 363f553d3c | |||
| 36a6f1ed4b | |||
| 7b112fc448 | |||
| 2a0052dda6 | |||
| a7b32e1330 | |||
| 5adfdc19aa | |||
| 351c86ee7a | |||
| dce7888408 | |||
| a2d99fa6b7 | |||
| 79465571d7 | |||
| 9d576beeb8 | |||
| e3332d1cb6 | |||
| f59f68f894 | |||
| 9c881a02d6 | |||
| 7b21c2d734 | |||
| 9fdc5960bf | |||
| fe853efe32 | |||
| 9b82c03959 | |||
| 914d1b27b5 | |||
| f295d03641 | |||
| 8515f6e6ac | |||
| 4d330e24c1 | |||
| a7a52bc57e | |||
| 3cef7985d3 | |||
| 40db822450 | |||
| 2de202ca02 | |||
| 38eb9fdac7 | |||
| f469d34781 | |||
| 33dfc5ce09 | |||
| 2100e2ff7c | |||
| e9b7cce138 | |||
| 6877a5a15d | |||
| 64206e69bd | |||
| 7643c76cb1 | |||
| b0f59273d3 | |||
| af8eb9d37d | |||
| 635e51f32f | |||
| ca962ce5ad | |||
| a50266cdc0 | |||
| 6230200218 | |||
| f96cd167ef | |||
| 072ce54fe1 | |||
| d043cb9086 | |||
| 1f31df3a94 | |||
| 9ee30e4923 | |||
| 7b44f12ab1 | |||
| 874882b554 | |||
| 945bdf4c60 | |||
| 6e1e5a2afe | |||
| b709b51790 | |||
| d380485bb6 | |||
| 87c8228cd0 | |||
| 152046bf97 | |||
| a0fbed5859 | |||
| 89e9e17d26 | |||
| b595247392 | |||
| 9d50a8cef2 | |||
| f888176485 | |||
| 2f9876ad74 | |||
| 53404122cc | |||
| ba65494fce | |||
| 70645c1732 | |||
| 1055e60d27 | |||
| e04196f8a0 | |||
| 11a0dec047 | |||
| b9740e1b7d | |||
| f0695eb870 | |||
| a40018cdf9 | |||
| 5b7eabe21a | |||
| d5d60aa11b | |||
| a62fa132e6 | |||
| 052395f917 | |||
| 9a77650004 | |||
| f1d70361c9 | |||
| 3496599723 | |||
| 9e0635a6a4 | |||
| 10509621ce | |||
| 3727ecdfe5 | |||
| ac71d22e86 | |||
| bc81356d53 | |||
| 881cb64beb | |||
| 1286e57b63 | |||
| fe5f73d96e | |||
| 8f7a8b31a3 | |||
| 68db35d5d4 | |||
| df17017ced | |||
| 5c48332b0e | |||
| 8985738af5 | |||
| 2d8a676dd5 | |||
| 7e0a9f398c | |||
| 9af5769510 | |||
| bb46d9a009 | |||
| 606b42a6e7 | |||
| d547f5ea22 | |||
| 563b4889e3 | |||
| b449beb68c | |||
| f9d58f4f9c | |||
| 1dfec9902e | |||
| 79cafee2eb | |||
| 64fbcdc1ca | |||
| e4a341af3a | |||
| e0292fe957 | |||
| ef85c8df24 | |||
| 719d369c2a | |||
| 51b6f95342 | |||
| 26fb1fc34d | |||
| cfd8e56277 | |||
| 4893931a8d | |||
| 932928ddc8 | |||
| a33e414f01 | |||
| 43d54c8f4f | |||
| 4912c27be8 | |||
| 288ba11452 | |||
| 7874183052 | |||
| dc9851f8ea | |||
| 68616e470c | |||
| 53cd2ff524 |
2
.gitattributes
vendored
@ -1 +1 @@
|
|||||||
Changelog.md merge=union
|
unreleased.md merge=union
|
||||||
|
|||||||
@ -27,6 +27,9 @@ Issue tracker is ONLY used for reporting bugs with technical details. "It doesn'
|
|||||||
3.
|
3.
|
||||||
4.
|
4.
|
||||||
|
|
||||||
|
## Version Information
|
||||||
|
<!--- Which version of the app(s) were you using when you experienced this issue? -->
|
||||||
|
|
||||||
## Context (Environment)
|
## Context (Environment)
|
||||||
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
||||||
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
|
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
|
||||||
|
|||||||
45
.gitignore
vendored
@ -5,43 +5,38 @@
|
|||||||
# Editor files
|
# Editor files
|
||||||
.*.sw?
|
.*.sw?
|
||||||
*~
|
*~
|
||||||
|
.idea
|
||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
vendor
|
|
||||||
|
|
||||||
# Test files
|
# Test files
|
||||||
godog.test
|
godog.test
|
||||||
debug.test
|
debug.test
|
||||||
coverage.html
|
coverage.html
|
||||||
|
gobinsec-cache*.yml
|
||||||
|
|
||||||
# Run files
|
# Run files
|
||||||
mem.pprof
|
mem.pprof
|
||||||
|
|
||||||
# Auto generated frontend
|
# Auto generated
|
||||||
internal/frontend/qml/BridgeUI/*.qmlc
|
internal/**/credits.go
|
||||||
internal/frontend/qml/ImportExportUI/*.qmlc
|
vendor
|
||||||
internal/frontend/qml/ProtonUI/*.qmlc
|
vendor-cache
|
||||||
internal/frontend/qml/ProtonUI/fontawesome.ttf
|
/main.go
|
||||||
internal/frontend/qml/ProtonUI/images
|
|
||||||
internal/frontend/qml/ImportExportUI/images
|
|
||||||
frontend/qml/*.qmlc
|
|
||||||
|
|
||||||
# Build files
|
# Build files
|
||||||
bridge_darwin_*.tgz
|
/launcher-*
|
||||||
|
/bridge_*_*.tgz
|
||||||
|
/ie_*_*.tgz
|
||||||
|
/versioner
|
||||||
|
/hasher
|
||||||
cmd/Desktop-Bridge/deploy
|
cmd/Desktop-Bridge/deploy
|
||||||
cmd/Import-Export/deploy
|
cmd/Import-Export/deploy
|
||||||
internal/frontend/qt*/moc.cpp
|
proton-bridge
|
||||||
internal/frontend/qt*/moc.go
|
cmd/Desktop-Bridge/*.exe
|
||||||
internal/frontend/qt*/moc.h
|
cmd/launcher/*.exe
|
||||||
internal/frontend/qt*/moc_cgo_*.go
|
|
||||||
internal/frontend/qt*/moc_moc.h
|
|
||||||
internal/frontend/qt*/rcc.cpp
|
|
||||||
internal/frontend/qt*/rcc.qrc
|
|
||||||
internal/frontend/qt*/rcc_cgo_*.go
|
|
||||||
|
|
||||||
internal/frontend/rcc.cpp
|
# Jetbrains (CLion, Golang) cmake build dirs
|
||||||
internal/frontend/rcc.qrc
|
cmake-build-*/
|
||||||
internal/frontend/rcc_cgo_*.go
|
|
||||||
vendor-cache/
|
|
||||||
|
|
||||||
/main.go
|
# Doxygen doc files
|
||||||
|
_doc/
|
||||||
|
|||||||
253
.gitlab-ci.yml
@ -1,17 +1,34 @@
|
|||||||
image: gitlab.protontech.ch:4567/go/bridge-internal
|
# Copyright (c) 2022 Proton Technologies AG
|
||||||
|
#
|
||||||
|
# This file is part of ProtonMail Bridge.
|
||||||
|
#
|
||||||
|
# ProtonMail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# ProtonMail Bridge is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
---
|
||||||
|
image: gitlab.protontech.ch:4567/go/bridge-internal:go18
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- eval $(ssh-agent -s)
|
- eval $(ssh-agent -s)
|
||||||
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
|
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
|
||||||
|
|
||||||
- mkdir -p .cache/bin
|
- mkdir -p .cache/bin
|
||||||
- export PATH=$(pwd)/.cache/bin:$PATH
|
- export PATH=$(pwd)/.cache/bin:$PATH
|
||||||
- export GOPATH="$CI_PROJECT_DIR/.cache"
|
- export GOPATH="$CI_PROJECT_DIR/.cache"
|
||||||
|
|
||||||
- make install-dev-dependencies
|
- make install-dev-dependencies
|
||||||
|
- git checkout .
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
key: go-mod
|
key: go18-mod
|
||||||
paths:
|
paths:
|
||||||
- .cache
|
- .cache
|
||||||
policy: pull
|
policy: pull
|
||||||
@ -20,20 +37,46 @@ stages:
|
|||||||
- cache
|
- cache
|
||||||
- test
|
- test
|
||||||
- build
|
- build
|
||||||
|
- check
|
||||||
- mirror
|
- mirror
|
||||||
|
|
||||||
|
|
||||||
|
.rules-branch-and-MR-always:
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_BRANCH || $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
when: always
|
||||||
|
allow_failure: false
|
||||||
|
- when: never
|
||||||
|
|
||||||
|
.rules-branch-and-MR-manual:
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_BRANCH || $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
when: manual
|
||||||
|
allow_failure: true
|
||||||
|
- when: never
|
||||||
|
|
||||||
|
.rules-branch-manual-MR-always:
|
||||||
|
rules:
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
when: always
|
||||||
|
allow_failure: false
|
||||||
|
- if: $CI_COMMIT_BRANCH
|
||||||
|
when: manual
|
||||||
|
allow_failure: true
|
||||||
|
- when: never
|
||||||
|
|
||||||
# Stage: CACHE
|
# Stage: CACHE
|
||||||
|
|
||||||
# This will ensure latest dependency versions and updates the cache for
|
# This will ensure latest dependency versions and updates the cache for
|
||||||
# all other following jobs which only pull the cache.
|
# all other following jobs which only pull the cache.
|
||||||
cache-push:
|
cache-push:
|
||||||
stage: cache
|
stage: cache
|
||||||
only:
|
extends:
|
||||||
- branches
|
- .rules-branch-and-MR-always
|
||||||
script:
|
script:
|
||||||
- echo ""
|
- echo ""
|
||||||
cache:
|
cache:
|
||||||
key: go-mod
|
key: go18-mod
|
||||||
paths:
|
paths:
|
||||||
- .cache
|
- .cache
|
||||||
|
|
||||||
@ -41,15 +84,21 @@ cache-push:
|
|||||||
|
|
||||||
lint:
|
lint:
|
||||||
stage: test
|
stage: test
|
||||||
only:
|
extends:
|
||||||
- branches
|
- .rules-branch-and-MR-always
|
||||||
|
before_script:
|
||||||
|
- mkdir -p .cache/bin
|
||||||
|
- export PATH=$(pwd)/.cache/bin:$PATH
|
||||||
|
- export GOPATH="$CI_PROJECT_DIR/.cache"
|
||||||
script:
|
script:
|
||||||
- make lint
|
- env GOMAXPROCS=$(( ${CI_TAG_CPU} / 2 )) make lint
|
||||||
|
tags:
|
||||||
|
- medium
|
||||||
|
|
||||||
test:
|
test-linux:
|
||||||
stage: test
|
stage: test
|
||||||
only:
|
extends:
|
||||||
- branches
|
- .rules-branch-manual-MR-always
|
||||||
script:
|
script:
|
||||||
- apt-get -y install pass gnupg rng-tools
|
- apt-get -y install pass gnupg rng-tools
|
||||||
# First have enough of entropy (cat /proc/sys/kernel/random/entropy_avail).
|
# First have enough of entropy (cat /proc/sys/kernel/random/entropy_avail).
|
||||||
@ -60,13 +109,26 @@ test:
|
|||||||
- pass init `gpg --list-keys | grep "^ " | tail -1 | tr -d '[:space:]'`
|
- pass init `gpg --list-keys | grep "^ " | tail -1 | tr -d '[:space:]'`
|
||||||
# Then finally run the tests
|
# Then finally run the tests
|
||||||
- make test
|
- make test
|
||||||
|
tags:
|
||||||
|
- medium
|
||||||
|
|
||||||
|
test-windows:
|
||||||
|
extends:
|
||||||
|
- .build-windows-base
|
||||||
|
- .rules-branch-and-MR-always
|
||||||
|
stage: test
|
||||||
|
needs: []
|
||||||
|
script:
|
||||||
|
- make test
|
||||||
|
|
||||||
test-integration:
|
test-integration:
|
||||||
stage: test
|
stage: test
|
||||||
only:
|
extends:
|
||||||
- branches
|
- .rules-branch-manual-MR-always
|
||||||
script:
|
script:
|
||||||
- VERBOSITY=debug make -C test test
|
- VERBOSITY=debug make -C test test
|
||||||
|
tags:
|
||||||
|
- large
|
||||||
|
|
||||||
dependency-updates:
|
dependency-updates:
|
||||||
stage: test
|
stage: test
|
||||||
@ -77,45 +139,59 @@ dependency-updates:
|
|||||||
|
|
||||||
.build-base:
|
.build-base:
|
||||||
stage: build
|
stage: build
|
||||||
only:
|
needs: ["lint"]
|
||||||
- branches
|
rules:
|
||||||
|
# GODT-1833: use `=~ /qa/` after mac and windows runners are fixed
|
||||||
|
- if: $CI_JOB_NAME =~ /build-linux-qa/ && $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
when: always
|
||||||
|
allow_failure: false
|
||||||
|
- if: $CI_COMMIT_BRANCH || $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
when: manual
|
||||||
|
allow_failure: true
|
||||||
|
- when: never
|
||||||
|
before_script:
|
||||||
|
- mkdir -p .cache/bin
|
||||||
|
- export PATH=$(pwd)/.cache/bin:$PATH
|
||||||
|
- export GOPATH="$CI_PROJECT_DIR/.cache"
|
||||||
|
- export PATH=$PATH:$QT6DIR/bin
|
||||||
script:
|
script:
|
||||||
- make build
|
- make build
|
||||||
|
- git diff && git diff-index --quiet HEAD
|
||||||
artifacts:
|
artifacts:
|
||||||
expire_in: 2 week
|
# Note: The latest artifacts for refs are locked against deletion, and kept
|
||||||
|
# regardless of the expiry time. Introduced in GitLab 13.0 behind a
|
||||||
|
# disabled feature flag, and made the default behavior in GitLab 13.4.
|
||||||
|
expire_in: 1 day
|
||||||
|
when: always
|
||||||
|
paths:
|
||||||
|
- bridge_*.tgz
|
||||||
|
tags:
|
||||||
|
- large
|
||||||
|
|
||||||
build-linux:
|
build-linux:
|
||||||
extends: .build-base
|
extends: .build-base
|
||||||
|
image: gitlab.protontech.ch:4567/go/bridge-internal:qt6
|
||||||
|
variables:
|
||||||
|
VCPKG_DEFAULT_BINARY_CACHE: ${CI_PROJECT_DIR}/.cache
|
||||||
|
cache:
|
||||||
|
key: linux-vcpkg
|
||||||
|
paths:
|
||||||
|
- .cache
|
||||||
|
when: 'always'
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "bridge-linux-$CI_COMMIT_SHORT_SHA"
|
name: "bridge-linux-$CI_COMMIT_SHORT_SHA"
|
||||||
paths:
|
|
||||||
- bridge_*.tgz
|
|
||||||
|
|
||||||
build-linux-qa:
|
build-linux-qa:
|
||||||
extends: .build-base
|
extends: build-linux
|
||||||
only:
|
variables:
|
||||||
- web
|
BUILD_TAGS: "build_qa"
|
||||||
script:
|
|
||||||
- BUILD_TAGS="build_qa pmapi_qa" make build
|
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "bridge-linux-qa-$CI_COMMIT_SHORT_SHA"
|
name: "bridge-linux-qa-$CI_COMMIT_SHORT_SHA"
|
||||||
paths:
|
|
||||||
- bridge_*.tgz
|
|
||||||
|
|
||||||
build-ie-linux:
|
|
||||||
extends: .build-base
|
|
||||||
script:
|
|
||||||
- make build-ie
|
|
||||||
artifacts:
|
|
||||||
name: "ie-linux-$CI_COMMIT_SHORT_SHA"
|
|
||||||
paths:
|
|
||||||
- ie_*.tgz
|
|
||||||
|
|
||||||
.build-darwin-base:
|
.build-darwin-base:
|
||||||
extends: .build-base
|
extends: .build-base
|
||||||
before_script:
|
before_script:
|
||||||
- eval $(ssh-agent -s)
|
|
||||||
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
|
|
||||||
- export PATH=/usr/local/bin:$PATH
|
- export PATH=/usr/local/bin:$PATH
|
||||||
- export PATH=/usr/local/opt/git/bin:$PATH
|
- export PATH=/usr/local/opt/git/bin:$PATH
|
||||||
- export PATH=/usr/local/opt/make/libexec/gnubin:$PATH
|
- export PATH=/usr/local/opt/make/libexec/gnubin:$PATH
|
||||||
@ -124,6 +200,10 @@ build-ie-linux:
|
|||||||
- export GOPATH=~/go
|
- export GOPATH=~/go
|
||||||
- export PATH=$GOPATH/bin:$PATH
|
- export PATH=$GOPATH/bin:$PATH
|
||||||
- export CGO_CPPFLAGS='-Wno-error -Wno-nullability-completeness -Wno-expansion-to-defined -Wno-builtin-requires-header'
|
- export CGO_CPPFLAGS='-Wno-error -Wno-nullability-completeness -Wno-expansion-to-defined -Wno-builtin-requires-header'
|
||||||
|
script:
|
||||||
|
- go version
|
||||||
|
- make build
|
||||||
|
- git diff && git diff-index --quiet HEAD
|
||||||
cache: {}
|
cache: {}
|
||||||
tags:
|
tags:
|
||||||
- macOS
|
- macOS
|
||||||
@ -132,82 +212,71 @@ build-darwin:
|
|||||||
extends: .build-darwin-base
|
extends: .build-darwin-base
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "bridge-darwin-$CI_COMMIT_SHORT_SHA"
|
name: "bridge-darwin-$CI_COMMIT_SHORT_SHA"
|
||||||
paths:
|
|
||||||
- bridge_*.tgz
|
|
||||||
|
|
||||||
build-darwin-qa:
|
build-darwin-qa:
|
||||||
extends: .build-darwin-base
|
extends: .build-darwin-base
|
||||||
only:
|
variables:
|
||||||
- web
|
BUILD_TAGS: "build_qa"
|
||||||
script:
|
|
||||||
- BUILD_TAGS="build_qa pmapi_qa" make build
|
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "bridge-darwin-qa-$CI_COMMIT_SHORT_SHA"
|
name: "bridge-darwin-qa-$CI_COMMIT_SHORT_SHA"
|
||||||
paths:
|
|
||||||
- bridge_*.tgz
|
|
||||||
|
|
||||||
build-ie-darwin:
|
|
||||||
extends: .build-darwin-base
|
|
||||||
script:
|
|
||||||
- make build-ie
|
|
||||||
artifacts:
|
|
||||||
name: "ie-darwin-$CI_COMMIT_SHORT_SHA"
|
|
||||||
paths:
|
|
||||||
- ie_*.tgz
|
|
||||||
|
|
||||||
.build-windows-base:
|
.build-windows-base:
|
||||||
extends: .build-base
|
extends: .build-base
|
||||||
services:
|
before_script:
|
||||||
- docker:dind
|
- export GOROOT=/c/Go1.18/
|
||||||
variables:
|
- export PATH=$GOROOT/bin:$PATH
|
||||||
DOCKER_HOST: tcp://docker:2375
|
- export GOARCH=amd64
|
||||||
|
- export GOPATH=~/go18
|
||||||
|
- export GO111MODULE=on
|
||||||
|
- export PATH="${GOPATH}/bin:${PATH}"
|
||||||
|
- export MSYSTEM=
|
||||||
|
- export QT6DIR=/c/grrrQt/6.3.1/msvc2019_64
|
||||||
|
- export PATH=$PATH:${QT6DIR}/bin
|
||||||
|
- export PATH="/c/Program Files/Microsoft Visual Studio/2022/Community/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/bin:$PATH"
|
||||||
|
script:
|
||||||
|
- make build
|
||||||
|
- git diff && git diff-index --quiet HEAD
|
||||||
|
tags:
|
||||||
|
- windows-bridge
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
extends: .build-windows-base
|
extends: .build-windows-base
|
||||||
script:
|
|
||||||
# We need to install docker because qtdeploy builds for windows inside a docker container.
|
|
||||||
# Docker will connect to the dockerd daemon provided by the runner service docker:dind at tcp://docker:2375.
|
|
||||||
- curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh
|
|
||||||
- apt-get update && apt-get -y install binutils-mingw-w64 tar gzip
|
|
||||||
- ln -s /usr/bin/x86_64-w64-mingw32-windres /usr/bin/windres
|
|
||||||
- go mod download
|
|
||||||
- TARGET_OS=windows make build
|
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "bridge-windows-$CI_COMMIT_SHORT_SHA"
|
name: "bridge-windows-$CI_COMMIT_SHORT_SHA"
|
||||||
paths:
|
|
||||||
- bridge_*.tgz
|
|
||||||
|
|
||||||
build-windows-qa:
|
build-windows-qa:
|
||||||
extends: .build-windows-base
|
extends: .build-windows-base
|
||||||
only:
|
variables:
|
||||||
- web
|
BUILD_TAGS: "build_qa"
|
||||||
script:
|
|
||||||
# We need to install docker because qtdeploy builds for windows inside a docker container.
|
|
||||||
# Docker will connect to the dockerd daemon provided by the runner service docker:dind at tcp://docker:2375.
|
|
||||||
- curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh
|
|
||||||
- apt-get update && apt-get -y install binutils-mingw-w64 tar gzip
|
|
||||||
- ln -s /usr/bin/x86_64-w64-mingw32-windres /usr/bin/windres
|
|
||||||
- go mod download
|
|
||||||
- TARGET_OS=windows BUILD_TAGS="build_qa pmapi_qa" make build
|
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "bridge-windows-qa-$CI_COMMIT_SHORT_SHA"
|
name: "bridge-windows-qa-$CI_COMMIT_SHORT_SHA"
|
||||||
paths:
|
|
||||||
- bridge_*.tgz
|
|
||||||
|
|
||||||
build-ie-windows:
|
# Stage: CHECK
|
||||||
extends: .build-windows-base
|
|
||||||
script:
|
check-gobinsec:
|
||||||
# We need to install docker because qtdeploy builds for windows inside a docker container.
|
stage: check
|
||||||
# Docker will connect to the dockerd daemon provided by the runner service docker:dind at tcp://docker:2375.
|
needs: ["build-linux-qa"]
|
||||||
- curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh
|
extends:
|
||||||
- apt-get update && apt-get -y install binutils-mingw-w64 tar gzip
|
- .rules-branch-manual-MR-always
|
||||||
- ln -s /usr/bin/x86_64-w64-mingw32-windres /usr/bin/windres
|
cache:
|
||||||
- go mod download
|
key: gobinsec-cache-v3
|
||||||
- TARGET_OS=windows make build-ie
|
|
||||||
artifacts:
|
|
||||||
name: "ie-windows-$CI_COMMIT_SHORT_SHA"
|
|
||||||
paths:
|
paths:
|
||||||
- ie_*.tgz
|
- ./gobinsec-cache-valid.yml
|
||||||
|
policy: pull-push
|
||||||
|
before_script:
|
||||||
|
- mkdir build
|
||||||
|
- tar -xzf bridge_linux_*.tgz -C build
|
||||||
|
- "[ ! -f ./gobinsec-cache-valid.yml ] && wget bridgeteam.protontech.ch/bridgeteam/gobinsec-cache-valid.yml"
|
||||||
|
- mv ./gobinsec-cache-valid.yml ./utils/gobinsec_update/gobinsec-cache-valid.yml
|
||||||
|
script:
|
||||||
|
- ./utils/gobinsec_update.sh
|
||||||
|
- cp ./utils/gobinsec_update/gobinsec-cache-valid.yml ./gobinsec-cache.yml
|
||||||
|
- cat ./gobinsec-cache.yml
|
||||||
|
- gobinsec -wait -cache -config utils/gobinsec_conf.yml build/bridge
|
||||||
|
- cp ./gobinsec-cache.yml ./gobinsec-cache-valid.yml # Only update cache file if gobinsec succeeds
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Stage: MIRROR
|
# Stage: MIRROR
|
||||||
|
|
||||||
|
|||||||
3
.gitmodules
vendored
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "submodules/vcpkg"]
|
||||||
|
path = extern/vcpkg
|
||||||
|
url = https://github.com/Microsoft/vcpkg.git
|
||||||
|
|||||||
@ -1,16 +1,20 @@
|
|||||||
|
---
|
||||||
run:
|
run:
|
||||||
timeout: 10m
|
timeout: 10m
|
||||||
build-tags:
|
|
||||||
- nogui
|
|
||||||
skip-dirs:
|
skip-dirs:
|
||||||
- pkg/mime
|
- pkg/mime
|
||||||
|
- extern
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
exclude-use-default: false
|
exclude-use-default: false
|
||||||
exclude:
|
exclude:
|
||||||
- Using the variable on range scope `tt` in function literal
|
- Using the variable on range scope `tt` in function literal
|
||||||
- should have comment (\([^)]+\) )?or be unexported # For now we are missing a lot of comments.
|
# For now we are missing a lot of comments.
|
||||||
- at least one file in a package should have a package comment # For now we are missing a lot of comments.
|
- should have comment (\([^)]+\) )?or be unexported
|
||||||
|
# For now we are missing a lot of comments.
|
||||||
|
- at least one file in a package should have a package comment
|
||||||
|
# Package comments.
|
||||||
|
- "package-comments: should have a package comment"
|
||||||
|
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
- path: _test\.go
|
- path: _test\.go
|
||||||
@ -30,7 +34,7 @@ linters-settings:
|
|||||||
linters:
|
linters:
|
||||||
# setting disable-all will make only explicitly enabled linters run
|
# setting disable-all will make only explicitly enabled linters run
|
||||||
disable-all: true
|
disable-all: true
|
||||||
|
|
||||||
enable:
|
enable:
|
||||||
- deadcode # Finds unused code [fast: true, auto-fix: false]
|
- deadcode # Finds unused code [fast: true, auto-fix: false]
|
||||||
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: true, auto-fix: false]
|
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: true, auto-fix: false]
|
||||||
@ -49,24 +53,58 @@ linters:
|
|||||||
- funlen # Tool for detection of long functions [fast: true, auto-fix: false]
|
- funlen # Tool for detection of long functions [fast: true, auto-fix: false]
|
||||||
- gochecknoglobals # Checks that no globals are present in Go code [fast: true, auto-fix: false]
|
- gochecknoglobals # Checks that no globals are present in Go code [fast: true, auto-fix: false]
|
||||||
- gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
|
- gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
|
||||||
#- gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false]
|
|
||||||
- goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
|
- goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
|
||||||
- gocritic # The most opinionated Go source code linter [fast: true, auto-fix: false]
|
- gocritic # The most opinionated Go source code linter [fast: true, auto-fix: false]
|
||||||
- gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false]
|
- gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false]
|
||||||
- godox # Tool for detection of FIXME, TODO and other comment keywords [fast: true, auto-fix: false]
|
- godox # Tool for detection of FIXME, TODO and other comment keywords [fast: true, auto-fix: false]
|
||||||
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true]
|
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true]
|
||||||
- goimports # Goimports does everything that gofmt does. Additionally it checks unused imports [fast: true, auto-fix: true]
|
- goimports # Goimports does everything that gofmt does. Additionally it checks unused imports [fast: true, auto-fix: true]
|
||||||
- golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes [fast: true, auto-fix: false]
|
|
||||||
- gosec # Inspects source code for security problems [fast: true, auto-fix: false]
|
- gosec # Inspects source code for security problems [fast: true, auto-fix: false]
|
||||||
- interfacer # Linter that suggests narrower interface types [fast: true, auto-fix: false]
|
|
||||||
- maligned # Tool to detect Go structs that would take less memory if their fields were sorted [fast: true, auto-fix: false]
|
|
||||||
- misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true]
|
- misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true]
|
||||||
- nakedret # Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false]
|
- nakedret # Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false]
|
||||||
- prealloc # Finds slice declarations that could potentially be preallocated [fast: true, auto-fix: false]
|
- prealloc # Finds slice declarations that could potentially be preallocated [fast: true, auto-fix: false]
|
||||||
- scopelint # Scopelint checks for unpinned variables in go programs [fast: true, auto-fix: false]
|
|
||||||
- stylecheck # Stylecheck is a replacement for golint [fast: true, auto-fix: false]
|
- stylecheck # Stylecheck is a replacement for golint [fast: true, auto-fix: false]
|
||||||
- unconvert # Remove unnecessary type conversions [fast: true, auto-fix: false]
|
- unconvert # Remove unnecessary type conversions [fast: true, auto-fix: false]
|
||||||
- unparam # Reports unused function parameters [fast: true, auto-fix: false]
|
- unparam # Reports unused function parameters [fast: true, auto-fix: false]
|
||||||
- whitespace # Tool for detection of leading and trailing whitespace [fast: true, auto-fix: true]
|
- whitespace # Tool for detection of leading and trailing whitespace [fast: true, auto-fix: true]
|
||||||
#- wsl # Whitespace Linter - Forces you to use empty lines! [fast: true, auto-fix: false]
|
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers [fast: true, auto-fix: false]
|
||||||
#- lll # Reports long lines [fast: true, auto-fix: false]
|
- durationcheck # check for two durations multiplied together [fast: false, auto-fix: false]
|
||||||
|
- exhaustive # check exhaustiveness of enum switch statements [fast: false, auto-fix: false]
|
||||||
|
- exportloopref # checks for pointers to enclosing loop variables [fast: false, auto-fix: false]
|
||||||
|
- forcetypeassert # finds forced type assertions [fast: true, auto-fix: false]
|
||||||
|
- godot # Check if comments end in a period [fast: true, auto-fix: true]
|
||||||
|
- goheader # Checks is file header matches to pattern [fast: true, auto-fix: false]
|
||||||
|
- gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. [fast: true, auto-fix: false]
|
||||||
|
- goprintffuncname # Checks that printf-like functions are named with `f` at the end [fast: true, auto-fix: false]
|
||||||
|
- importas # Enforces consistent import aliases [fast: false, auto-fix: false]
|
||||||
|
- makezero # Finds slice declarations with non-zero initial length [fast: false, auto-fix: false]
|
||||||
|
- nilerr # Finds the code that returns nil even if it checks that the error is not nil. [fast: false, auto-fix: false]
|
||||||
|
- predeclared # find code that shadows one of Go's predeclared identifiers [fast: true, auto-fix: false]
|
||||||
|
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false]
|
||||||
|
- rowserrcheck # checks whether Err of rows is checked successfully [fast: false, auto-fix: false]
|
||||||
|
- sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. [fast: false, auto-fix: false]
|
||||||
|
- tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes [fast: false, auto-fix: false]
|
||||||
|
- wastedassign # wastedassign finds wasted assignment statements. [fast: false, auto-fix: false]
|
||||||
|
# - wsl # Whitespace Linter - Forces you to use empty lines! [fast: true, auto-fix: false]
|
||||||
|
# - lll # Reports long lines [fast: true, auto-fix: false]
|
||||||
|
# Consider to include:
|
||||||
|
# - gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false]
|
||||||
|
# - cyclop # checks function and package cyclomatic complexity [fast: false, auto-fix: false]
|
||||||
|
# - errorlint # go-errorlint is a source code linter for Go software that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. [fast: false, auto-fix: false]
|
||||||
|
# - exhaustivestruct # Checks if all struct's fields are initialized [fast: false, auto-fix: false]
|
||||||
|
# - forbidigo # Forbids identifiers [fast: true, auto-fix: false]
|
||||||
|
# - gci # Gci control golang package import order and make it always deterministic. [fast: true, auto-fix: true]
|
||||||
|
# - gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false]
|
||||||
|
# - goerr113 # Golang linter to check the errors handling expressions [fast: false, auto-fix: false]
|
||||||
|
# - gofumpt # Gofumpt checks whether code was gofumpt-ed. [fast: true, auto-fix: true]
|
||||||
|
# - gomnd # An analyzer to detect magic numbers. [fast: true, auto-fix: false]
|
||||||
|
# - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. [fast: true, auto-fix: false]
|
||||||
|
# - ifshort # Checks that your code uses short syntax for if-statements whenever possible [fast: true, auto-fix: false]
|
||||||
|
# - nestif # Reports deeply nested if statements [fast: true, auto-fix: false]
|
||||||
|
# - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity [fast: true, auto-fix: false]
|
||||||
|
# - noctx # noctx finds sending http request without context.Context [fast: false, auto-fix: false]
|
||||||
|
# - nolintlint # Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: false]
|
||||||
|
# - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test [fast: true, auto-fix: false]
|
||||||
|
# - testpackage # linter that makes you use a separate _test package [fast: true, auto-fix: false]
|
||||||
|
# - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers [fast: false, auto-fix: false]
|
||||||
|
# - wrapcheck # Checks that errors returned from external packages are wrapped [fast: false, auto-fix: false]
|
||||||
|
|||||||
41
BUILDS.md
@ -1,10 +1,12 @@
|
|||||||
# Building ProtonMail Bridge and Import-Export app
|
# Building Proton Mail Bridge
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
* Go 1.13
|
* 64-bit OS:
|
||||||
|
- the go-rfc5322 module cannot currently be compiled for 32-bit OSes
|
||||||
|
* Go 1.18
|
||||||
* Bash with basic build utils: make, gcc, sed, find, grep, ...
|
* Bash with basic build utils: make, gcc, sed, find, grep, ...
|
||||||
* For Windows it is recommended to use MinGW 64bit shell from [MSYS2](https://www.msys2.org/)
|
- For Windows it is recommended to use MinGW 64bit shell from [MSYS2](https://www.msys2.org/)
|
||||||
* GCC (linux, windows) or Xcode (macOS)
|
* GCC (linux), msvc (windows) or Xcode (macOS)
|
||||||
* Windres (windows)
|
* Windres (windows)
|
||||||
* libglvnd and libsecret development files (linux)
|
* libglvnd and libsecret development files (linux)
|
||||||
|
|
||||||
@ -13,13 +15,16 @@ To enable the sending of crash reports using Sentry please set the
|
|||||||
Otherwise, the sending of crash reports will be disabled.
|
Otherwise, the sending of crash reports will be disabled.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
* for Windows please unset the `MSYSTEM` variable
|
In order to build Bridge app with Qt interface we are using
|
||||||
|
[Qt 6.3](https://doc.qt.io/qt-6/gettingstarted.html).
|
||||||
|
|
||||||
|
Please note that qmake path must be in your `PATH` to ensure Qt to be found.
|
||||||
|
Also, before you start build **on Windows**, please unset the `MSYSTEM` variable
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export MSYSTEM=
|
export MSYSTEM=
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Build Bridge
|
### Build Bridge
|
||||||
* in project root run
|
* in project root run
|
||||||
|
|
||||||
@ -32,18 +37,28 @@ make build
|
|||||||
* for `windows`, the binary will have the file extension `.exe` (e.g `proton-bridge.exe`)
|
* for `windows`, the binary will have the file extension `.exe` (e.g `proton-bridge.exe`)
|
||||||
* for `darwin`, the application will be created with name of the project directory (e.g `proton-bridge.app`)
|
* for `darwin`, the application will be created with name of the project directory (e.g `proton-bridge.app`)
|
||||||
|
|
||||||
### Build Import-Export
|
#### Build Bridge without GUI
|
||||||
* in project root run
|
* If you need to build bridge without Qt dependencies, you can do so by running
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make build-ie
|
make build-nogui
|
||||||
```
|
```
|
||||||
|
|
||||||
* The result will be stored in `./cmd/Import-Export/deploy/${GOOS}/`
|
* Bridge without GUI will start by default without any interface (i.e., there is no way to add or remove client, get bridge password, etc)
|
||||||
* for `linux`, the binary will have the name of the project directory (e.g `proton-bridge`)
|
* Bridge always has the option (whether built with Qt or without) to use a CLI interface by starting it with the argument `-c`
|
||||||
* for `windows`, the binary will have the file extension `.exe` (e.g `proton-bridge.exe`)
|
* NOTE: You still need to setup supported keychain on your system
|
||||||
* for `darwin`, the application will be created with name of the project directory (e.g `proton-bridge.app`)
|
|
||||||
|
|
||||||
|
## Launchers
|
||||||
|
Launchers are only included in official distributions and provide the public
|
||||||
|
key used to verify signed app binaries, allowing the automatic update feature.
|
||||||
|
See README for more information.
|
||||||
|
|
||||||
|
## Tags
|
||||||
|
Note that repository contains both Bridge and Import-Export apps and they are
|
||||||
|
not released together. Therefore, each app has own tag prefix. Bridge tags
|
||||||
|
starts with `br-` and Import-Export tags starts with `ie-`. Both tags continue
|
||||||
|
with semantic versioning `MAJOR.MINOR.PATCH`. An example of full tag is
|
||||||
|
`br-1.4.4` or `ie-1.1.2` (current versions in October 2020).
|
||||||
|
|
||||||
## Useful tests, lints and checks
|
## Useful tests, lints and checks
|
||||||
In order to be able to run following commands please install the development dependencies:
|
In order to be able to run following commands please install the development dependencies:
|
||||||
|
|||||||
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
By making a contribution to this project:
|
By making a contribution to this project:
|
||||||
|
|
||||||
1. I assign any and all copyright related to the contribution to
|
1. I assign any and all copyright related to the contribution to Proton AG;
|
||||||
Proton Technologies AG;
|
|
||||||
2. I certify that the contribution was created in whole by me;
|
2. I certify that the contribution was created in whole by me;
|
||||||
3. I understand and agree that this project and the contribution are public
|
3. I understand and agree that this project and the contribution are public
|
||||||
and that a record of the contribution (including all personal information I
|
and that a record of the contribution (including all personal information I
|
||||||
|
|||||||
73
COPYING.md
@ -1,73 +0,0 @@
|
|||||||
# Copying
|
|
||||||
Copyright (c) 2020 Proton Technologies AG
|
|
||||||
|
|
||||||
ProtonMail Bridge is free software: you can redistribute it and/or modify it
|
|
||||||
under the terms of the GNU General Public License as published by the Free
|
|
||||||
Software Foundation, either version 3 of the License, or (at your option) any
|
|
||||||
later version.
|
|
||||||
|
|
||||||
ProtonMail Bridge is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along with
|
|
||||||
ProtonMail Bridge. If not, see https://www.gnu.org/licenses.
|
|
||||||
|
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
ProtonMail Bridge app includes the following libraries from Proton Technologies AG:
|
|
||||||
|
|
||||||
* [GopenPGP library](https://gopenpgp.org/) | The [MIT License](https://github.com/ProtonMail/gopenpgp/blob/master/LICENSE).
|
|
||||||
|
|
||||||
ProtonMail Bridge includes the following 3rd party software:
|
|
||||||
|
|
||||||
* [The Go Project libraries](https://golang.org/project/) | Available under [BSD license](https://golang.org/LICENSE)
|
|
||||||
* [Qt Go binding](https://github.com/therecipe/qt) | Available under [LGPLv3 license](https://github.com/therecipe/qt/blob/master/LICENSE)
|
|
||||||
* [Qt](https://www.qt.io/) | Available under [multiple licences](https://www.qt.io/licensing)
|
|
||||||
* [Font Awesome 4.7.0](https://fontawesome.com/v4.7.0/) | Available under [multiple licenses](https://fontawesome.com/v4.7.0/license/)
|
|
||||||
|
|
||||||
* [notificator](https://github.com/0xAX/notificator) | Available under [license](https://github.com/0xAX/notificator/blob/master/LICENSE)
|
|
||||||
* [ishell](https://github.com/abiosoft/ishell) | Available under [license](https://github.com/abiosoft/ishell/blob/master/LICENSE)
|
|
||||||
* [readline](https://github.com/abiosoft/readline) | Available under [license](https://github.com/abiosoft/readline/blob/master/LICENSE)
|
|
||||||
* [singleinstance](https://github.com/allan-simon/go-singleinstance) | Available under [license](https://github.com/allan-simon/go-singleinstance/blob/master/LICENSE)
|
|
||||||
* [cascadia](https://github.com/andybalholm/cascadia) | Available under [license](https://github.com/andybalholm/cascadia/blob/master/LICENSE)
|
|
||||||
* [gocertifi](https://github.com/certifi/gocertifi) | Available under [license](https://github.com/certifi/gocertifi/blob/master/LICENSE)
|
|
||||||
* [logex](https://github.com/chzyer/logex) | Available under [license](https://github.com/chzyer/logex/blob/master/LICENSE)
|
|
||||||
* [test](https://github.com/chzyer/test) | Available under [license](https://github.com/chzyer/test/blob/master/LICENSE)
|
|
||||||
* [godog](https://github.com/cucumber/godog) | Available under [license](https://github.com/cucumber/godog/blob/master/LICENSE)
|
|
||||||
* [wincred](https://github.com/danieljoos/wincred) | Available under [license](https://github.com/danieljoos/wincred/blob/master/LICENSE)
|
|
||||||
* [credential-helpers](https://github.com/docker/docker-credential-helpers) | Available under [license](https://github.com/docker/docker-credential-helpers/blob/master/LICENSE)
|
|
||||||
* [imap](https://github.com/emersion/go-imap) | Available under [license](https://github.com/emersion/go-imap/blob/master/LICENSE)
|
|
||||||
* [imap-appendlimit](https://github.com/emersion/go-imap-appendlimit) | Available under [license](https://github.com/emersion/go-imap-appendlimit/blob/master/LICENSE)
|
|
||||||
* [imap-idle](https://github.com/emersion/go-imap-idle) | Available under [license](https://github.com/emersion/go-imap-idle/blob/master/LICENSE)
|
|
||||||
* [imap-quota](https://github.com/emersion/go-imap-quota) | Available under [license](https://github.com/emersion/go-imap-quota/blob/master/LICENSE)
|
|
||||||
* [imap-specialuse](https://github.com/emersion/go-imap-specialuse) | Available under [license](https://github.com/emersion/go-imap-specialuse/blob/master/LICENSE)
|
|
||||||
* [sasl](https://github.com/emersion/go-sasl) | Available under [license](https://github.com/emersion/go-sasl/blob/master/LICENSE)
|
|
||||||
* [smtp](https://github.com/emersion/go-smtp) | Available under [license](https://github.com/emersion/go-smtp/blob/master/LICENSE)
|
|
||||||
* [textwrapper](https://github.com/emersion/go-textwrapper) | Available under [license](https://github.com/emersion/go-textwrapper/blob/master/LICENSE)
|
|
||||||
* [vcard](https://github.com/emersion/go-vcard) | Available under [license](https://github.com/emersion/go-vcard/blob/master/LICENSE)
|
|
||||||
* [color](https://github.com/fatih/color) | Available under [license](https://github.com/fatih/color/blob/master/LICENSE.md)
|
|
||||||
* [shlex](https://github.com/flynn-archive/go-shlex) | Available under [license](https://github.com/flynn-archive/go-shlex/blob/master/COPYING)
|
|
||||||
* [raven](https://github.com/getsentry/raven-go) | Available under [license](https://github.com/getsentry/raven-go/blob/master/LICENSE)
|
|
||||||
* [resty](https://github.com/go-resty/resty) | Available under [license](https://github.com/go-resty/resty/blob/master/LICENSE)
|
|
||||||
* [mock](https://github.com/golang/mock) | Available under [license](https://github.com/golang/mock/blob/master/LICENSE)
|
|
||||||
* [cmp](https://github.com/google/go-cmp) | Available under [license](https://github.com/google/go-cmp/blob/master/LICENSE)
|
|
||||||
* [gopherjs](https://github.com/gopherjs/gopherjs) | Available under [license](https://github.com/gopherjs/gopherjs/blob/master/LICENSE)
|
|
||||||
* [multierror](https://github.com/hashicorp/go-multierror) | Available under [license](https://github.com/hashicorp/go-multierror/blob/master/LICENSE)
|
|
||||||
* [bcrypt](https://github.com/jameskeane/bcrypt) | Available under [license](https://github.com/jameskeane/bcrypt/blob/master/LICENSE)
|
|
||||||
* [html2text](https://github.com/jaytaylor/html2text) | Available under [license](https://github.com/jaytaylor/html2text/blob/master/LICENSE)
|
|
||||||
* [enmime](https://github.com/jhillyerd/enmime) | Available under [license](https://github.com/jhillyerd/enmime/blob/master/LICENSE)
|
|
||||||
* [osext](https://github.com/kardianos/osext) | Available under [license](https://github.com/kardianos/osext/blob/master/LICENSE)
|
|
||||||
* [keychain](https://github.com/keybase/go-keychain) | Available under [license](https://github.com/keybase/go-keychain/blob/master/LICENSE)
|
|
||||||
* [aurora](https://github.com/logrusorgru/aurora) | Available under [license](https://github.com/logrusorgru/aurora/blob/master/LICENSE)
|
|
||||||
* [dns](https://github.com/miekg/dns) | Available under [license](https://github.com/miekg/dns/blob/master/LICENSE)
|
|
||||||
* [uuid](https://github.com/myesui/uuid) | Available under [license](https://github.com/myesui/uuid/blob/master/LICENSE)
|
|
||||||
* [jsondiff](https://github.com/nsf/jsondiff) | Available under [license](https://github.com/nsf/jsondiff/blob/master/LICENSE)
|
|
||||||
* [logrus](https://github.com/sirupsen/logrus) | Available under [license](https://github.com/sirupsen/logrus/blob/master/LICENSE)
|
|
||||||
* [golang](https://github.com/skratchdot/open-golang) | Available under [license](https://github.com/skratchdot/open-golang/blob/master/LICENSE)
|
|
||||||
* [testify](https://github.com/stretchr/testify) | Available under [license](https://github.com/stretchr/testify/blob/master/LICENSE)
|
|
||||||
* [uuid](https://github.com/twinj/uuid) | Available under [license](https://github.com/twinj/uuid/blob/master/LICENSE)
|
|
||||||
* [cli](https://github.com/urfave/cli) | Available under [license](https://github.com/urfave/cli/blob/master/LICENSE)
|
|
||||||
|
|
||||||
* [BBolt](https://pkg.go.dev/go.etcd.io/bbolt/?tab=doc) | Available under [license](https://pkg.go.dev/go.etcd.io/bbolt?tab=licenses#LICENSE)
|
|
||||||
* [testify.v1](https://gopkg.in/stretchr/testify.v1) | Available under [license](https://github.com/stretchr/testify/blob/master/LICENSE)
|
|
||||||
120
COPYING_NOTES.md
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
# Copying
|
||||||
|
Copyright (c) 2022 Proton AG
|
||||||
|
|
||||||
|
Proton Mail Bridge is free software: you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option) any
|
||||||
|
later version.
|
||||||
|
|
||||||
|
Proton Mail Bridge is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
Proton Mail Bridge. If not, see https://www.gnu.org/licenses.
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
Proton Mail Bridge includes the following 3rd party software:
|
||||||
|
|
||||||
|
* [The Go Project libraries](https://golang.org/project/) | Available under [BSD license](https://golang.org/LICENSE)
|
||||||
|
* [Qt](https://www.qt.io/) | Available under [multiple licences](https://www.qt.io/licensing)
|
||||||
|
|
||||||
|
<!-- START AUTOGEN -->
|
||||||
|
* [notificator](https://github.com/0xAX/notificator) available under [license](https://github.com/0xAX/notificator/blob/master/LICENSE)
|
||||||
|
* [semver](https://github.com/Masterminds/semver/v3) available under [license](https://github.com/Masterminds/semver/v3/blob/master/LICENSE)
|
||||||
|
* [go-autostart](https://github.com/ProtonMail/go-autostart) available under [license](https://github.com/ProtonMail/go-autostart/blob/master/LICENSE)
|
||||||
|
* [go-crypto](https://github.com/ProtonMail/go-crypto) available under [license](https://github.com/ProtonMail/go-crypto/blob/master/LICENSE)
|
||||||
|
* [go-imap-id](https://github.com/ProtonMail/go-imap-id) available under [license](https://github.com/ProtonMail/go-imap-id/blob/master/LICENSE)
|
||||||
|
* [go-rfc5322](https://github.com/ProtonMail/go-rfc5322) available under [license](https://github.com/ProtonMail/go-rfc5322/blob/master/LICENSE)
|
||||||
|
* [go-srp](https://github.com/ProtonMail/go-srp) available under [license](https://github.com/ProtonMail/go-srp/blob/master/LICENSE)
|
||||||
|
* [go-vcard](https://github.com/ProtonMail/go-vcard) available under [license](https://github.com/ProtonMail/go-vcard/blob/master/LICENSE)
|
||||||
|
* [gopenpgp](https://github.com/ProtonMail/gopenpgp/v2) available under [license](https://github.com/ProtonMail/gopenpgp/v2/blob/master/LICENSE)
|
||||||
|
* [goquery](https://github.com/PuerkitoBio/goquery) available under [license](https://github.com/PuerkitoBio/goquery/blob/master/LICENSE)
|
||||||
|
* [ishell](https://github.com/abiosoft/ishell) available under [license](https://github.com/abiosoft/ishell/blob/master/LICENSE)
|
||||||
|
* [go-singleinstance](https://github.com/allan-simon/go-singleinstance) available under [license](https://github.com/allan-simon/go-singleinstance/blob/master/LICENSE)
|
||||||
|
* [juniper](https://github.com/bradenaw/juniper) available under [license](https://github.com/bradenaw/juniper/blob/master/LICENSE)
|
||||||
|
* [godog](https://github.com/cucumber/godog) available under [license](https://github.com/cucumber/godog/blob/master/LICENSE)
|
||||||
|
* [messages-go](https://github.com/cucumber/messages-go/v16) available under [license](https://github.com/cucumber/messages-go/v16/blob/master/LICENSE)
|
||||||
|
* [docker-credential-helpers](https://github.com/docker/docker-credential-helpers) available under [license](https://github.com/docker/docker-credential-helpers/blob/master/LICENSE)
|
||||||
|
* [go-sysinfo](https://github.com/elastic/go-sysinfo) available under [license](https://github.com/elastic/go-sysinfo/blob/master/LICENSE)
|
||||||
|
* [go-imap](https://github.com/emersion/go-imap) available under [license](https://github.com/emersion/go-imap/blob/master/LICENSE)
|
||||||
|
* [go-imap-appendlimit](https://github.com/emersion/go-imap-appendlimit) available under [license](https://github.com/emersion/go-imap-appendlimit/blob/master/LICENSE)
|
||||||
|
* [go-imap-move](https://github.com/emersion/go-imap-move) available under [license](https://github.com/emersion/go-imap-move/blob/master/LICENSE)
|
||||||
|
* [go-imap-quota](https://github.com/emersion/go-imap-quota) available under [license](https://github.com/emersion/go-imap-quota/blob/master/LICENSE)
|
||||||
|
* [go-imap-unselect](https://github.com/emersion/go-imap-unselect) available under [license](https://github.com/emersion/go-imap-unselect/blob/master/LICENSE)
|
||||||
|
* [go-message](https://github.com/emersion/go-message) available under [license](https://github.com/emersion/go-message/blob/master/LICENSE)
|
||||||
|
* [go-sasl](https://github.com/emersion/go-sasl) available under [license](https://github.com/emersion/go-sasl/blob/master/LICENSE)
|
||||||
|
* [go-smtp](https://github.com/emersion/go-smtp) available under [license](https://github.com/emersion/go-smtp/blob/master/LICENSE)
|
||||||
|
* [go-textwrapper](https://github.com/emersion/go-textwrapper) available under [license](https://github.com/emersion/go-textwrapper/blob/master/LICENSE)
|
||||||
|
* [color](https://github.com/fatih/color) available under [license](https://github.com/fatih/color/blob/master/LICENSE)
|
||||||
|
* [sentry-go](https://github.com/getsentry/sentry-go) available under [license](https://github.com/getsentry/sentry-go/blob/master/LICENSE)
|
||||||
|
* [resty](https://github.com/go-resty/resty/v2) available under [license](https://github.com/go-resty/resty/v2/blob/master/LICENSE)
|
||||||
|
* [dbus](https://github.com/godbus/dbus) available under [license](https://github.com/godbus/dbus/blob/master/LICENSE)
|
||||||
|
* [mock](https://github.com/golang/mock) available under [license](https://github.com/golang/mock/blob/master/LICENSE)
|
||||||
|
* [go-cmp](https://github.com/google/go-cmp) available under [license](https://github.com/google/go-cmp/blob/master/LICENSE)
|
||||||
|
* [uuid](https://github.com/google/uuid) available under [license](https://github.com/google/uuid/blob/master/LICENSE)
|
||||||
|
* [go-multierror](https://github.com/hashicorp/go-multierror) available under [license](https://github.com/hashicorp/go-multierror/blob/master/LICENSE)
|
||||||
|
* [html2text](https://github.com/jaytaylor/html2text) available under [license](https://github.com/jaytaylor/html2text/blob/master/LICENSE)
|
||||||
|
* [go-keychain](https://github.com/keybase/go-keychain) available under [license](https://github.com/keybase/go-keychain/blob/master/LICENSE)
|
||||||
|
* [aurora](https://github.com/logrusorgru/aurora) available under [license](https://github.com/logrusorgru/aurora/blob/master/LICENSE)
|
||||||
|
* [dns](https://github.com/miekg/dns) available under [license](https://github.com/miekg/dns/blob/master/LICENSE)
|
||||||
|
* [jsondiff](https://github.com/nsf/jsondiff) available under [license](https://github.com/nsf/jsondiff/blob/master/LICENSE)
|
||||||
|
* [errors](https://github.com/pkg/errors) available under [license](https://github.com/pkg/errors/blob/master/LICENSE)
|
||||||
|
* [du](https://github.com/ricochet2200/go-disk-usage/du) available under [license](https://github.com/ricochet2200/go-disk-usage/du/blob/master/LICENSE)
|
||||||
|
* [logrus](https://github.com/sirupsen/logrus) available under [license](https://github.com/sirupsen/logrus/blob/master/LICENSE)
|
||||||
|
* [testify](https://github.com/stretchr/testify) available under [license](https://github.com/stretchr/testify/blob/master/LICENSE)
|
||||||
|
* [cli](https://github.com/urfave/cli/v2) available under [license](https://github.com/urfave/cli/v2/blob/master/LICENSE)
|
||||||
|
* [msgpack](https://github.com/vmihailenco/msgpack/v5) available under [license](https://github.com/vmihailenco/msgpack/v5/blob/master/LICENSE)
|
||||||
|
* [bbolt](https://go.etcd.io/bbolt) available under [license](https://github.com/etcd-io/bbolt/blob/master/LICENSE)
|
||||||
|
* [exp](https://golang.org/x/exp) available under [license](https://cs.opensource.google/go/x/exp/+/master:LICENSE)
|
||||||
|
* [net](https://golang.org/x/net) available under [license](https://cs.opensource.google/go/x/net/+/master:LICENSE)
|
||||||
|
* [sys](https://golang.org/x/sys) available under [license](https://cs.opensource.google/go/x/sys/+/master:LICENSE)
|
||||||
|
* [text](https://golang.org/x/text) available under [license](https://cs.opensource.google/go/x/text/+/master:LICENSE)
|
||||||
|
* [grpc](https://google.golang.org/grpc) available under [license](https://github.com/grpc/grpc-go/blob/master/LICENSE)
|
||||||
|
* [protobuf](https://google.golang.org/protobuf) available under [license](https://github.com/protocolbuffers/protobuf/blob/main/LICENSE)
|
||||||
|
* [plist](https://howett.net/plist) available under [license](https://github.com/DHowett/go-plist/blob/main/LICENSE)
|
||||||
|
* [bcrypt](https://github.com/ProtonMail/bcrypt) available under [license](https://github.com/ProtonMail/bcrypt/blob/master/LICENSE)
|
||||||
|
* [go-mime](https://github.com/ProtonMail/go-mime) available under [license](https://github.com/ProtonMail/go-mime/blob/master/LICENSE)
|
||||||
|
* [readline](https://github.com/abiosoft/readline) available under [license](https://github.com/abiosoft/readline/blob/master/LICENSE)
|
||||||
|
* [cascadia](https://github.com/andybalholm/cascadia) available under [license](https://github.com/andybalholm/cascadia/blob/master/LICENSE)
|
||||||
|
* [antlr](https://github.com/antlr/antlr4/runtime/Go/antlr) available under [license](https://github.com/antlr/antlr4/runtime/Go/antlr/blob/master/LICENSE)
|
||||||
|
* [test](https://github.com/chzyer/test) available under [license](https://github.com/chzyer/test/blob/master/LICENSE)
|
||||||
|
* [circl](https://github.com/cloudflare/circl) available under [license](https://github.com/cloudflare/circl/blob/master/LICENSE)
|
||||||
|
* [go-md2man](https://github.com/cpuguy83/go-md2man/v2) available under [license](https://github.com/cpuguy83/go-md2man/v2/blob/master/LICENSE)
|
||||||
|
* [saferith](https://github.com/cronokirby/saferith) available under [license](https://github.com/cronokirby/saferith/blob/master/LICENSE)
|
||||||
|
* [gherkin-go](https://github.com/cucumber/gherkin-go/v19) available under [license](https://github.com/cucumber/gherkin-go/v19/blob/master/LICENSE)
|
||||||
|
* [wincred](https://github.com/danieljoos/wincred) available under [license](https://github.com/danieljoos/wincred/blob/master/LICENSE)
|
||||||
|
* [go-spew](https://github.com/davecgh/go-spew) available under [license](https://github.com/davecgh/go-spew/blob/master/LICENSE)
|
||||||
|
* [go-windows](https://github.com/elastic/go-windows) available under [license](https://github.com/elastic/go-windows/blob/master/LICENSE)
|
||||||
|
* [go-vcard](https://github.com/emersion/go-vcard) available under [license](https://github.com/emersion/go-vcard/blob/master/LICENSE)
|
||||||
|
* [go-shlex](https://github.com/flynn-archive/go-shlex) available under [license](https://github.com/flynn-archive/go-shlex/blob/master/LICENSE)
|
||||||
|
* [uuid](https://github.com/gofrs/uuid) available under [license](https://github.com/gofrs/uuid/blob/master/LICENSE)
|
||||||
|
* [protobuf](https://github.com/golang/protobuf) available under [license](https://github.com/golang/protobuf/blob/master/LICENSE)
|
||||||
|
* [errwrap](https://github.com/hashicorp/errwrap) available under [license](https://github.com/hashicorp/errwrap/blob/master/LICENSE)
|
||||||
|
* [go-immutable-radix](https://github.com/hashicorp/go-immutable-radix) available under [license](https://github.com/hashicorp/go-immutable-radix/blob/master/LICENSE)
|
||||||
|
* [go-memdb](https://github.com/hashicorp/go-memdb) available under [license](https://github.com/hashicorp/go-memdb/blob/master/LICENSE)
|
||||||
|
* [golang-lru](https://github.com/hashicorp/golang-lru) available under [license](https://github.com/hashicorp/golang-lru/blob/master/LICENSE)
|
||||||
|
* [multierror](https://github.com/joeshaw/multierror) available under [license](https://github.com/joeshaw/multierror/blob/master/LICENSE)
|
||||||
|
* [go-colorable](https://github.com/mattn/go-colorable) available under [license](https://github.com/mattn/go-colorable/blob/master/LICENSE)
|
||||||
|
* [go-isatty](https://github.com/mattn/go-isatty) available under [license](https://github.com/mattn/go-isatty/blob/master/LICENSE)
|
||||||
|
* [go-runewidth](https://github.com/mattn/go-runewidth) available under [license](https://github.com/mattn/go-runewidth/blob/master/LICENSE)
|
||||||
|
* [tablewriter](https://github.com/olekukonko/tablewriter) available under [license](https://github.com/olekukonko/tablewriter/blob/master/LICENSE)
|
||||||
|
* [go-difflib](https://github.com/pmezard/go-difflib) available under [license](https://github.com/pmezard/go-difflib/blob/master/LICENSE)
|
||||||
|
* [procfs](https://github.com/prometheus/procfs) available under [license](https://github.com/prometheus/procfs/blob/master/LICENSE)
|
||||||
|
* [uniseg](https://github.com/rivo/uniseg) available under [license](https://github.com/rivo/uniseg/blob/master/LICENSE)
|
||||||
|
* [blackfriday](https://github.com/russross/blackfriday/v2) available under [license](https://github.com/russross/blackfriday/v2/blob/master/LICENSE)
|
||||||
|
* [pflag](https://github.com/spf13/pflag) available under [license](https://github.com/spf13/pflag/blob/master/LICENSE)
|
||||||
|
* [bom](https://github.com/ssor/bom) available under [license](https://github.com/ssor/bom/blob/master/LICENSE)
|
||||||
|
* [tagparser](https://github.com/vmihailenco/tagparser/v2) available under [license](https://github.com/vmihailenco/tagparser/v2/blob/master/LICENSE)
|
||||||
|
* [smetrics](https://github.com/xrash/smetrics) available under [license](https://github.com/xrash/smetrics/blob/master/LICENSE)
|
||||||
|
* [crypto](https://golang.org/x/crypto) available under [license](https://cs.opensource.google/go/x/crypto/+/master:LICENSE)
|
||||||
|
* [mod](https://golang.org/x/mod) available under [license](https://cs.opensource.google/go/x/mod/+/master:LICENSE)
|
||||||
|
* [tools](https://golang.org/x/tools) available under [license](https://cs.opensource.google/go/x/tools/+/master:LICENSE)
|
||||||
|
* [genproto](https://google.golang.org/genproto)
|
||||||
|
gopkg.in/yaml.v3
|
||||||
|
* [docker-credential-helpers](https://github.com/ProtonMail/docker-credential-helpers) available under [license](https://github.com/ProtonMail/docker-credential-helpers/blob/master/LICENSE)
|
||||||
|
* [go-imap](https://github.com/ProtonMail/go-imap) available under [license](https://github.com/ProtonMail/go-imap/blob/master/LICENSE)
|
||||||
|
* [go-message](https://github.com/ProtonMail/go-message) available under [license](https://github.com/ProtonMail/go-message/blob/master/LICENSE)
|
||||||
|
* [go-keychain](https://github.com/cuthix/go-keychain) available under [license](https://github.com/cuthix/go-keychain/blob/master/LICENSE)
|
||||||
|
<!-- END AUTOGEN -->
|
||||||
970
Changelog.md
380
Makefile
@ -5,142 +5,175 @@ export GO111MODULE=on
|
|||||||
GOOS:=$(shell go env GOOS)
|
GOOS:=$(shell go env GOOS)
|
||||||
TARGET_CMD?=Desktop-Bridge
|
TARGET_CMD?=Desktop-Bridge
|
||||||
TARGET_OS?=${GOOS}
|
TARGET_OS?=${GOOS}
|
||||||
|
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
.PHONY: build build-ie build-nogui build-ie-nogui check-has-go
|
.PHONY: build build-gui build-nogui build-launcher versioner hasher
|
||||||
|
|
||||||
# Keep version hardcoded so app build works also without Git repository.
|
# Keep version hardcoded so app build works also without Git repository.
|
||||||
BRIDGE_APP_VERSION?=1.4.0-git
|
BRIDGE_APP_VERSION?=2.4.5+git
|
||||||
IE_APP_VERSION?=1.1.0-git
|
|
||||||
APP_VERSION:=${BRIDGE_APP_VERSION}
|
APP_VERSION:=${BRIDGE_APP_VERSION}
|
||||||
SRC_ICO:=logo.ico
|
APP_FULL_NAME:=Proton Mail Bridge
|
||||||
|
APP_VENDOR:=Proton AG
|
||||||
|
SRC_ICO:=bridge.ico
|
||||||
SRC_ICNS:=Bridge.icns
|
SRC_ICNS:=Bridge.icns
|
||||||
SRC_SVG:=logo.svg
|
SRC_SVG:=bridge.svg
|
||||||
TGT_ICNS:=Bridge.icns
|
EXE_NAME:=proton-bridge
|
||||||
ifeq "${TARGET_CMD}" "Import-Export"
|
|
||||||
APP_VERSION:=${IE_APP_VERSION}
|
|
||||||
SRC_ICO:=ie.ico
|
|
||||||
SRC_ICNS:=ie.icns
|
|
||||||
SRC_SVG:=ie.svg
|
|
||||||
TGT_ICNS:=ImportExport.icns
|
|
||||||
endif
|
|
||||||
REVISION:=$(shell git rev-parse --short=10 HEAD)
|
REVISION:=$(shell git rev-parse --short=10 HEAD)
|
||||||
BUILD_TIME:=$(shell date +%FT%T%z)
|
BUILD_TIME:=$(shell date +%FT%T%z)
|
||||||
|
MACOS_MIN_VERSION=11.0
|
||||||
|
|
||||||
BUILD_TAGS?=pmapi_prod
|
|
||||||
BUILD_FLAGS:=-tags='${BUILD_TAGS}'
|
BUILD_FLAGS:=-tags='${BUILD_TAGS}'
|
||||||
BUILD_FLAGS_NOGUI:=-tags='${BUILD_TAGS} nogui'
|
BUILD_FLAGS_LAUNCHER:=${BUILD_FLAGS}
|
||||||
GO_LDFLAGS:=$(addprefix -X github.com/ProtonMail/proton-bridge/pkg/constants.,Version=${APP_VERSION} Revision=${REVISION} BuildTime=${BUILD_TIME})
|
BUILD_FLAGS_GUI:=-tags='${BUILD_TAGS} build_qt'
|
||||||
ifneq "${BUILD_LDFLAGS}" ""
|
GO_LDFLAGS:=$(addprefix -X github.com/ProtonMail/proton-bridge/v2/internal/constants., Version=${APP_VERSION} Revision=${REVISION} BuildTime=${BUILD_TIME})
|
||||||
GO_LDFLAGS+= ${BUILD_LDFLAGS}
|
GO_LDFLAGS+=-X "github.com/ProtonMail/proton-bridge/v2/internal/constants.FullAppName=${APP_FULL_NAME}"
|
||||||
endif
|
|
||||||
GO_LDFLAGS:=-ldflags '${GO_LDFLAGS}'
|
|
||||||
BUILD_FLAGS+= ${GO_LDFLAGS}
|
|
||||||
BUILD_FLAGS_NOGUI+= ${GO_LDFLAGS}
|
|
||||||
|
|
||||||
|
ifneq "${BUILD_LDFLAGS}" ""
|
||||||
|
GO_LDFLAGS+=${BUILD_LDFLAGS}
|
||||||
|
endif
|
||||||
|
GO_LDFLAGS_LAUNCHER:=${GO_LDFLAGS}
|
||||||
|
ifeq "${TARGET_OS}" "windows"
|
||||||
|
GO_LDFLAGS+=-H=windowsgui
|
||||||
|
GO_LDFLAGS_LAUNCHER+=-H=windowsgui
|
||||||
|
endif
|
||||||
|
|
||||||
|
BUILD_FLAGS+=-ldflags '${GO_LDFLAGS}'
|
||||||
|
BUILD_FLAGS_GUI+=-ldflags "${GO_LDFLAGS}"
|
||||||
|
BUILD_FLAGS_LAUNCHER+=-ldflags '${GO_LDFLAGS_LAUNCHER}'
|
||||||
DEPLOY_DIR:=cmd/${TARGET_CMD}/deploy
|
DEPLOY_DIR:=cmd/${TARGET_CMD}/deploy
|
||||||
ICO_FILES:=
|
DIRNAME:=$(shell basename ${CURDIR})
|
||||||
EXE:=$(shell basename ${CURDIR})
|
|
||||||
|
LAUNCHER_EXE:=proton-bridge
|
||||||
|
BRIDGE_EXE=bridge
|
||||||
|
BRIDGE_GUI_EXE_NAME=bridge-gui
|
||||||
|
BRIDGE_GUI_EXE=${BRIDGE_GUI_EXE_NAME}
|
||||||
|
LAUNCHER_PATH:=cmd/launcher
|
||||||
|
|
||||||
ifeq "${TARGET_OS}" "windows"
|
ifeq "${TARGET_OS}" "windows"
|
||||||
EXE:=${EXE}.exe
|
BRIDGE_EXE:=${BRIDGE_EXE}.exe
|
||||||
ICO_FILES:=${SRC_ICO} icon.rc icon_windows.syso
|
BRIDGE_GUI_EXE:=${BRIDGE_GUI_EXE}.exe
|
||||||
|
LAUNCHER_EXE:=${LAUNCHER_EXE}.exe
|
||||||
|
RESOURCE_FILE:=resource.syso
|
||||||
endif
|
endif
|
||||||
ifeq "${TARGET_OS}" "darwin"
|
ifeq "${TARGET_OS}" "darwin"
|
||||||
DARWINAPP_CONTENTS:=${DEPLOY_DIR}/darwin/${EXE}.app/Contents
|
BRIDGE_EXE_NAME:=${BRIDGE_EXE}
|
||||||
EXE:=${EXE}.app/Contents/MacOS/${EXE}
|
BRIDGE_EXE:=${BRIDGE_EXE}.app
|
||||||
|
BRIDGE_GUI_EXE:=${BRIDGE_GUI_EXE}.app
|
||||||
|
EXE_BINARY_DARWIN:=Contents/MacOS/${BRIDGE_GUI_EXE_NAME}
|
||||||
|
EXE_TARGET_DARWIN:=${DEPLOY_DIR}/${TARGET_OS}/${LAUNCHER_EXE}.app
|
||||||
|
DARWINAPP_CONTENTS:=${EXE_TARGET_DARWIN}/Contents
|
||||||
endif
|
endif
|
||||||
EXE_TARGET:=${DEPLOY_DIR}/${TARGET_OS}/${EXE}
|
EXE_TARGET:=${DEPLOY_DIR}/${TARGET_OS}/${BRIDGE_EXE}
|
||||||
|
EXE_GUI_TARGET:=${DEPLOY_DIR}/${TARGET_OS}/${BRIDGE_GUI_EXE}
|
||||||
|
|
||||||
TGZ_TARGET:=bridge_${TARGET_OS}_${REVISION}.tgz
|
TGZ_TARGET:=bridge_${TARGET_OS}_${REVISION}.tgz
|
||||||
ifeq "${TARGET_CMD}" "Import-Export"
|
|
||||||
TGZ_TARGET:=ie_${TARGET_OS}_${REVISION}.tgz
|
ifdef QT_API
|
||||||
|
VENDOR_TARGET:=prepare-vendor update-qt-docs
|
||||||
|
else
|
||||||
|
VENDOR_TARGET=update-vendor
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
build: build-gui
|
||||||
|
|
||||||
build: ${TGZ_TARGET}
|
build-gui: ${TGZ_TARGET}
|
||||||
build-ie:
|
|
||||||
TARGET_CMD=Import-Export $(MAKE) build
|
|
||||||
|
|
||||||
build-nogui:
|
build-nogui: ${EXE_NAME} build-launcher
|
||||||
go build ${BUILD_FLAGS_NOGUI} -o ${TARGET_CMD} cmd/${TARGET_CMD}/main.go
|
ifeq "${TARGET_OS}" "darwin"
|
||||||
|
mv ${BRIDGE_EXE} ${BRIDGE_EXE_NAME}
|
||||||
|
endif
|
||||||
|
|
||||||
build-ie-nogui:
|
go-build=go build $(1) -o $(2) $(3)
|
||||||
TARGET_CMD=Import-Export $(MAKE) build-nogui
|
go-build-finalize=${go-build}
|
||||||
|
ifeq "${GOOS}-$(shell uname -m)" "darwin-arm64"
|
||||||
|
go-build-finalize= \
|
||||||
|
MACOSX_DEPLOYMENT_TARGET=${MACOS_MIN_VERSION} CGO_ENABLED=1 CGO_CFLAGS="-mmacosx-version-min=${MACOS_MIN_VERSION}" GOARCH=arm64 $(call go-build,$(1),$(2)_arm,$(3)) && \
|
||||||
|
MACOSX_DEPLOYMENT_TARGET=${MACOS_MIN_VERSION} CGO_ENABLED=1 CGO_CFLAGS="-mmacosx-version-min=${MACOS_MIN_VERSION}" GOARCH=amd64 $(call go-build,$(1),$(2)_amd,$(3)) && \
|
||||||
|
lipo -create -output $(2) $(2)_arm $(2)_amd && rm -f $(2)_arm $(2)_amd
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq "${GOOS}" "windows"
|
||||||
|
go-build-finalize= \
|
||||||
|
powershell Copy-Item ${ROOT_DIR}/${RESOURCE_FILE} ${4} && \
|
||||||
|
$(call go-build,$(1),$(2),$(3)) && \
|
||||||
|
powershell Remove-Item ${4} -Force
|
||||||
|
endif
|
||||||
|
|
||||||
|
${EXE_NAME}: gofiles ${RESOURCE_FILE}
|
||||||
|
$(call go-build-finalize,${BUILD_FLAGS},"${LAUNCHER_EXE}","./cmd/${TARGET_CMD}/","${ROOT_DIR}/cmd/${TARGET_CMD}/${RESOURCE_FILE}")
|
||||||
|
mv ${LAUNCHER_EXE} ${BRIDGE_EXE}
|
||||||
|
|
||||||
|
build-launcher: ${RESOURCE_FILE}
|
||||||
|
$(call go-build-finalize,${BUILD_FLAGS_LAUNCHER},"${LAUNCHER_EXE}","${ROOT_DIR}/${LAUNCHER_PATH}/","${ROOT_DIR}/${LAUNCHER_PATH}/${RESOURCE_FILE}")
|
||||||
|
|
||||||
|
versioner:
|
||||||
|
go build ${BUILD_FLAGS} -o versioner utils/versioner/main.go
|
||||||
|
|
||||||
|
hasher:
|
||||||
|
go build -o hasher utils/hasher/main.go
|
||||||
|
|
||||||
${TGZ_TARGET}: ${DEPLOY_DIR}/${TARGET_OS}
|
${TGZ_TARGET}: ${DEPLOY_DIR}/${TARGET_OS}
|
||||||
rm -f $@
|
rm -f $@
|
||||||
cd ${DEPLOY_DIR} && tar czf ../../../$@ ${TARGET_OS}
|
tar -czvf $@ -C ${DEPLOY_DIR}/${TARGET_OS} .
|
||||||
|
|
||||||
${DEPLOY_DIR}/linux: ${EXE_TARGET}
|
${DEPLOY_DIR}/linux: ${EXE_TARGET} build-launcher
|
||||||
cp -pf ./internal/frontend/share/icons/${SRC_SVG} ${DEPLOY_DIR}/linux/logo.svg
|
cp -pf ./dist/${SRC_SVG} ${DEPLOY_DIR}/linux/logo.svg
|
||||||
cp -pf ./LICENSE ${DEPLOY_DIR}/linux/
|
cp -pf ./LICENSE ${DEPLOY_DIR}/linux/
|
||||||
cp -pf ./Changelog.md ${DEPLOY_DIR}/linux/
|
cp -pf ./Changelog.md ${DEPLOY_DIR}/linux/
|
||||||
|
cp -pf ./dist/${EXE_NAME}.desktop ${DEPLOY_DIR}/linux/
|
||||||
|
mv ${LAUNCHER_EXE} ${DEPLOY_DIR}/linux/
|
||||||
|
|
||||||
${DEPLOY_DIR}/darwin: ${EXE_TARGET}
|
${DEPLOY_DIR}/darwin: ${EXE_TARGET} build-launcher
|
||||||
cp ./internal/frontend/share/icons/${SRC_ICNS} ${DARWINAPP_CONTENTS}/Resources/${TGT_ICNS}
|
mv ${EXE_GUI_TARGET} ${EXE_TARGET_DARWIN}
|
||||||
|
mv ${EXE_TARGET} ${DARWINAPP_CONTENTS}/MacOS/${BRIDGE_EXE_NAME}
|
||||||
|
perl -i -pe"s/>${BRIDGE_GUI_EXE_NAME}/>${LAUNCHER_EXE}/g" ${DARWINAPP_CONTENTS}/Info.plist
|
||||||
|
cp ./dist/${SRC_ICNS} ${DARWINAPP_CONTENTS}/Resources/${SRC_ICNS}
|
||||||
cp LICENSE ${DARWINAPP_CONTENTS}/Resources/
|
cp LICENSE ${DARWINAPP_CONTENTS}/Resources/
|
||||||
rm -rf "${DARWINAPP_CONTENTS}/Frameworks/QtWebEngine.framework"
|
rm -rf "${DARWINAPP_CONTENTS}/Frameworks/QtWebEngine.framework"
|
||||||
rm -rf "${DARWINAPP_CONTENTS}/Frameworks/QtWebView.framework"
|
rm -rf "${DARWINAPP_CONTENTS}/Frameworks/QtWebView.framework"
|
||||||
rm -rf "${DARWINAPP_CONTENTS}/Frameworks/QtWebEngineCore.framework"
|
rm -rf "${DARWINAPP_CONTENTS}/Frameworks/QtWebEngineCore.framework"
|
||||||
./utils/remove_non_relative_links_darwin.sh "${EXE_TARGET}"
|
mv ${LAUNCHER_EXE} ${DARWINAPP_CONTENTS}/MacOS/${LAUNCHER_EXE}
|
||||||
|
./utils/remove_non_relative_links_darwin.sh "${EXE_TARGET_DARWIN}/${EXE_BINARY_DARWIN}"
|
||||||
|
|
||||||
${DEPLOY_DIR}/windows: ${EXE_TARGET}
|
${DEPLOY_DIR}/windows: ${EXE_TARGET} build-launcher
|
||||||
cp ./internal/frontend/share/icons/${SRC_ICO} ${DEPLOY_DIR}/windows/logo.ico
|
cp ./dist/${SRC_ICO} ${DEPLOY_DIR}/windows/logo.ico
|
||||||
cp LICENSE ${DEPLOY_DIR}/windows/
|
cp LICENSE ${DEPLOY_DIR}/windows/LICENSE.txt
|
||||||
|
mv ${LAUNCHER_EXE} ${DEPLOY_DIR}/windows/$(notdir ${LAUNCHER_EXE})
|
||||||
|
# plugins are installed in a plugins folder while needs to be near the exe
|
||||||
|
cp -rf ${DEPLOY_DIR}/windows/plugins/* ${DEPLOY_DIR}/windows/.
|
||||||
|
rm -rf ${DEPLOY_DIR}/windows/plugins
|
||||||
|
|
||||||
QT_BUILD_TARGET:=build desktop
|
${EXE_TARGET}: check-build-essentials ${EXE_NAME}
|
||||||
ifneq "${GOOS}" "${TARGET_OS}"
|
cd internal/frontend/bridge-gui/bridge-gui && \
|
||||||
ifeq "${TARGET_OS}" "windows"
|
BRIDGE_APP_FULL_NAME="${APP_FULL_NAME}" \
|
||||||
QT_BUILD_TARGET:=-docker build windows_64_shared
|
BRIDGE_VENDOR="${APP_VENDOR}" \
|
||||||
endif
|
BRIDGE_APP_VERSION=${APP_VERSION} \
|
||||||
endif
|
BRIDGE_REVISION=${REVISION} \
|
||||||
|
BRIDGE_BUILD_TIME=${BUILD_TIME} \
|
||||||
${EXE_TARGET}: check-has-go gofiles ${ICO_FILES} update-vendor
|
BRIDGE_GUI_BUILD_CONFIG=Release \
|
||||||
rm -rf deploy ${TARGET_OS} ${DEPLOY_DIR}
|
BRIDGE_INSTALL_PATH=${ROOT_DIR}/${DEPLOY_DIR}/${GOOS} \
|
||||||
cp cmd/${TARGET_CMD}/main.go .
|
./build.sh install
|
||||||
qtdeploy ${BUILD_FLAGS} ${QT_BUILD_TARGET}
|
mv "${ROOT_DIR}/${BRIDGE_EXE}" "$(ROOT_DIR)/${EXE_TARGET}"
|
||||||
mv deploy cmd/${TARGET_CMD}
|
|
||||||
rm -rf ${TARGET_OS} main.go
|
|
||||||
|
|
||||||
logo.ico ie.ico: ./internal/frontend/share/icons/${SRC_ICO}
|
|
||||||
cp $^ $@
|
|
||||||
icon.rc: ./internal/frontend/share/icon.rc
|
|
||||||
cp $^ .
|
|
||||||
icon_windows.syso: icon.rc logo.ico
|
|
||||||
windres --target=pe-x86-64 -o $@ $<
|
|
||||||
|
|
||||||
|
|
||||||
## Rules for therecipe/qt
|
|
||||||
.PHONY: prepare-vendor update-vendor
|
|
||||||
THERECIPE_ENV:=github.com/therecipe/env_${TARGET_OS}_amd64_513
|
|
||||||
|
|
||||||
# vendor folder will be deleted by gomod hence we cache the big repo
|
|
||||||
# therecipe/env in order to download it only once
|
|
||||||
vendor-cache/${THERECIPE_ENV}:
|
|
||||||
git clone https://${THERECIPE_ENV}.git vendor-cache/${THERECIPE_ENV}
|
|
||||||
|
|
||||||
# The command used to make symlinks is different on windows.
|
|
||||||
# So if the GOOS is windows and we aren't crossbuilding (in which case the host os would still be *nix)
|
|
||||||
# we need to change the LINKCMD to something windowsy.
|
|
||||||
LINKCMD:=ln -sf ${CURDIR}/vendor-cache/${THERECIPE_ENV} vendor/${THERECIPE_ENV}
|
|
||||||
ifeq "${GOOS}" "windows"
|
|
||||||
WINDIR:=$(subst /c/,c:\\,${CURDIR})/vendor-cache/${THERECIPE_ENV}
|
|
||||||
LINKCMD:=cmd //c 'mklink $(subst /,\,vendor\${THERECIPE_ENV} ${WINDIR})'
|
|
||||||
endif
|
|
||||||
|
|
||||||
prepare-vendor:
|
|
||||||
go install -v -tags=no_env github.com/therecipe/qt/cmd/...
|
|
||||||
go mod vendor
|
|
||||||
|
|
||||||
# update-vendor is PHONY because we need to make sure that we always have updated vendor
|
|
||||||
update-vendor: vendor-cache/${THERECIPE_ENV} prepare-vendor
|
|
||||||
${LINKCMD}
|
|
||||||
|
|
||||||
|
WINDRES_YEAR:=$(shell date +%Y)
|
||||||
|
APP_VERSION_COMMA:=$(shell echo "${APP_VERSION}" | sed -e 's/[^0-9,.]*//g' -e 's/\./,/g')
|
||||||
|
${RESOURCE_FILE}: ./dist/info.rc ./dist/${SRC_ICO} .FORCE
|
||||||
|
rm -f ./*.syso
|
||||||
|
windres --target=pe-x86-64 \
|
||||||
|
-I ./internal/frontend/share/ \
|
||||||
|
-D ICO_FILE=${SRC_ICO} \
|
||||||
|
-D EXE_NAME="${EXE_NAME}" \
|
||||||
|
-D FILE_VERSION="${APP_VERSION}" \
|
||||||
|
-D ORIGINAL_FILE_NAME="${EXE}" \
|
||||||
|
-D PRODUCT_VERSION="${APP_VERSION}" \
|
||||||
|
-D FILE_VERSION_COMMA=${APP_VERSION_COMMA} \
|
||||||
|
-D YEAR=${WINDRES_YEAR} \
|
||||||
|
-o ./${RESOURCE_FILE} $<
|
||||||
|
|
||||||
## Dev dependencies
|
## Dev dependencies
|
||||||
.PHONY: install-devel-tools install-linter install-go-mod-outdated
|
.PHONY: install-devel-tools install-linter install-go-mod-outdated install-git-hooks
|
||||||
LINTVER:="v1.29.0"
|
LINTVER:="v1.50.0"
|
||||||
LINTSRC:="https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh"
|
LINTSRC:="https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh"
|
||||||
|
|
||||||
install-dev-dependencies: install-devel-tools install-linter install-go-mod-outdated
|
install-dev-dependencies: install-devel-tools install-linter install-go-mod-outdated
|
||||||
@ -154,13 +187,29 @@ install-linter: check-has-go
|
|||||||
curl -sfL $(LINTSRC) | sh -s -- -b $(shell go env GOPATH)/bin $(LINTVER)
|
curl -sfL $(LINTSRC) | sh -s -- -b $(shell go env GOPATH)/bin $(LINTVER)
|
||||||
|
|
||||||
install-go-mod-outdated:
|
install-go-mod-outdated:
|
||||||
which go-mod-outdated || go get -u github.com/psampaz/go-mod-outdated
|
which go-mod-outdated || go install github.com/psampaz/go-mod-outdated@latest
|
||||||
|
|
||||||
|
install-git-hooks:
|
||||||
|
cp utils/githooks/* .git/hooks/
|
||||||
|
chmod +x .git/hooks/*
|
||||||
|
|
||||||
## Checks, mocks and docs
|
## Checks, mocks and docs
|
||||||
.PHONY: check-has-go add-license change-copyright-year test bench coverage mocks lint-license lint-golang lint updates doc
|
.PHONY: check-has-go check-build-essentials add-license change-copyright-year test bench coverage mocks lint-license lint-golang lint updates doc release-notes
|
||||||
check-has-go:
|
check-has-go:
|
||||||
@which go || (echo "Install Go-lang!" && exit 1)
|
@which go || (echo "Install Go-lang!" && exit 1)
|
||||||
|
go version
|
||||||
|
|
||||||
|
|
||||||
|
check_is_installed=if ! which $(1) > /dev/null; then echo "Please install $(1)"; exit 1; fi
|
||||||
|
check-build-essentials:
|
||||||
|
@$(call check_is_installed,zip)
|
||||||
|
@$(call check_is_installed,unzip)
|
||||||
|
@$(call check_is_installed,tar)
|
||||||
|
@$(call check_is_installed,curl)
|
||||||
|
ifneq "${GOOS}" "windows"
|
||||||
|
@$(call check_is_installed,cmake)
|
||||||
|
@$(call check_is_installed,ninja)
|
||||||
|
endif
|
||||||
|
|
||||||
add-license:
|
add-license:
|
||||||
./utils/missing_license.sh add
|
./utils/missing_license.sh add
|
||||||
@ -169,22 +218,8 @@ change-copyright-year:
|
|||||||
./utils/missing_license.sh change-year
|
./utils/missing_license.sh change-year
|
||||||
|
|
||||||
test: gofiles
|
test: gofiles
|
||||||
@# Listing packages manually to not run Qt folder (which needs to run qtsetup first) and integration tests.
|
|
||||||
go test -coverprofile=/tmp/coverage.out -run=${TESTRUN} \
|
go test -coverprofile=/tmp/coverage.out -run=${TESTRUN} \
|
||||||
./internal/api/... \
|
./internal/...\
|
||||||
./internal/bridge/... \
|
|
||||||
./internal/events/... \
|
|
||||||
./internal/frontend/autoconfig/... \
|
|
||||||
./internal/frontend/cli/... \
|
|
||||||
./internal/imap/... \
|
|
||||||
./internal/metrics/... \
|
|
||||||
./internal/importexport/... \
|
|
||||||
./internal/preferences/... \
|
|
||||||
./internal/smtp/... \
|
|
||||||
./internal/store/... \
|
|
||||||
./internal/transfer/... \
|
|
||||||
./internal/updates/... \
|
|
||||||
./internal/users/... \
|
|
||||||
./pkg/...
|
./pkg/...
|
||||||
|
|
||||||
bench:
|
bench:
|
||||||
@ -195,22 +230,40 @@ bench:
|
|||||||
coverage: test
|
coverage: test
|
||||||
go tool cover -html=/tmp/coverage.out -o=coverage.html
|
go tool cover -html=/tmp/coverage.out -o=coverage.html
|
||||||
|
|
||||||
mocks:
|
integration-test-bridge:
|
||||||
mockgen --package mocks github.com/ProtonMail/proton-bridge/internal/users Configer,PanicHandler,ClientManager,CredentialsStorer,StoreMaker > internal/users/mocks/mocks.go
|
${MAKE} -C test test-bridge
|
||||||
mockgen --package mocks github.com/ProtonMail/proton-bridge/internal/transfer PanicHandler,ClientManager > internal/transfer/mocks/mocks.go
|
|
||||||
mockgen --package mocks github.com/ProtonMail/proton-bridge/internal/store PanicHandler,ClientManager,BridgeUser > internal/store/mocks/mocks.go
|
|
||||||
mockgen --package mocks github.com/ProtonMail/proton-bridge/pkg/listener Listener > internal/store/mocks/utils_mocks.go
|
|
||||||
mockgen --package mocks github.com/ProtonMail/proton-bridge/pkg/pmapi Client > pkg/pmapi/mocks/mocks.go
|
|
||||||
|
|
||||||
lint: lint-golang lint-license
|
mocks:
|
||||||
|
mockgen --package mocks github.com/ProtonMail/proton-bridge/v2/internal/users Locator,PanicHandler,CredentialsStorer,StoreMaker > internal/users/mocks/mocks.go
|
||||||
|
mockgen --package mocks github.com/ProtonMail/proton-bridge/v2/pkg/listener Listener > internal/users/mocks/listener_mocks.go
|
||||||
|
mockgen --package mocks github.com/ProtonMail/proton-bridge/v2/internal/store PanicHandler,BridgeUser,ChangeNotifier,Storer > internal/store/mocks/mocks.go
|
||||||
|
mockgen --package mocks github.com/ProtonMail/proton-bridge/v2/pkg/listener Listener > internal/store/mocks/utils_mocks.go
|
||||||
|
mockgen --package mocks github.com/ProtonMail/proton-bridge/v2/pkg/pmapi Client,Manager > pkg/pmapi/mocks/mocks.go
|
||||||
|
mockgen --package mocks github.com/ProtonMail/proton-bridge/v2/pkg/message Fetcher > pkg/message/mocks/mocks.go
|
||||||
|
|
||||||
|
lint: gofiles lint-golang lint-license lint-dependencies lint-changelog
|
||||||
|
|
||||||
lint-license:
|
lint-license:
|
||||||
./utils/missing_license.sh check
|
./utils/missing_license.sh check
|
||||||
|
|
||||||
|
lint-dependencies:
|
||||||
|
./utils/dependency_license.sh check
|
||||||
|
|
||||||
|
lint-changelog:
|
||||||
|
./utils/changelog_linter.sh Changelog.md
|
||||||
|
|
||||||
lint-golang:
|
lint-golang:
|
||||||
which golangci-lint || $(MAKE) install-linter
|
which golangci-lint || $(MAKE) install-linter
|
||||||
|
$(info linting with GOMAXPROCS=${GOMAXPROCS})
|
||||||
golangci-lint run ./...
|
golangci-lint run ./...
|
||||||
|
|
||||||
|
gobinsec: gobinsec-cache.yml build
|
||||||
|
gobinsec -wait -cache -config utils/gobinsec_conf.yml ${EXE_TARGET} ${DEPLOY_DIR}/${TARGET_OS}/${LAUNCHER_EXE}
|
||||||
|
|
||||||
|
gobinsec-cache.yml:
|
||||||
|
./utils/gobinsec_update.sh
|
||||||
|
cp ./utils/gobinsec_update/gobinsec-cache-valid.yml ./gobinsec-cache.yml
|
||||||
|
|
||||||
updates: install-go-mod-outdated
|
updates: install-go-mod-outdated
|
||||||
# Uncomment the "-ci" to fail the job if something can be updated.
|
# Uncomment the "-ci" to fail the job if something can be updated.
|
||||||
go list -u -m -json all | go-mod-outdated -update -direct #-ci
|
go list -u -m -json all | go-mod-outdated -update -direct #-ci
|
||||||
@ -218,67 +271,64 @@ updates: install-go-mod-outdated
|
|||||||
doc:
|
doc:
|
||||||
godoc -http=:6060
|
godoc -http=:6060
|
||||||
|
|
||||||
|
release-notes: release-notes/bridge_stable.html release-notes/bridge_early.html
|
||||||
|
|
||||||
|
release-notes/%.html: release-notes/%.md
|
||||||
|
./utils/release_notes.sh $^
|
||||||
|
|
||||||
.PHONY: gofiles
|
.PHONY: gofiles
|
||||||
# Following files are for the whole app so it makes sense to have them in bridge package.
|
# Following files are for the whole app so it makes sense to have them in bridge package.
|
||||||
# (Options like cmd or internal were considered and bridge package is the best place for them.)
|
# (Options like cmd or internal were considered and bridge package is the best place for them.)
|
||||||
gofiles: ./internal/bridge/credits.go ./internal/bridge/release_notes.go ./internal/importexport/credits.go ./internal/importexport/release_notes.go
|
gofiles: ./internal/bridge/credits.go
|
||||||
./internal/bridge/credits.go: ./utils/credits.sh go.mod
|
./internal/bridge/credits.go: ./utils/credits.sh go.mod
|
||||||
cd ./utils/ && ./credits.sh bridge
|
cd ./utils/ && ./credits.sh bridge
|
||||||
./internal/bridge/release_notes.go: ./utils/release-notes.sh ./release-notes/notes-bridge.txt ./release-notes/bugs-bridge.txt
|
|
||||||
cd ./utils/ && ./release-notes.sh bridge
|
|
||||||
./internal/importexport/credits.go: ./utils/credits.sh go.mod
|
|
||||||
cd ./utils/ && ./credits.sh importexport
|
|
||||||
./internal/importexport/release_notes.go: ./utils/release-notes.sh ./release-notes/notes-importexport.txt ./release-notes/bugs-importexport.txt
|
|
||||||
cd ./utils/ && ./release-notes.sh importexport
|
|
||||||
|
|
||||||
|
|
||||||
## Run and debug
|
## Run and debug
|
||||||
.PHONY: run run-qt run-qt-cli run-nogui run-nogui-cli run-debug run-qml-preview run-ie-qml-preview run-ie run-ie-qt run-ie-qt-cli run-ie-nogui run-ie-nogui-cli clean-vendor clean-frontend-qt clean-frontend-qt-ie clean-frontend-qt-common clean
|
.PHONY: run run-qt run-qt-cli run-nogui run-nogui-cli run-debug run-qml-preview clean-vendor clean-frontend-qt clean-frontend-qt-common clean
|
||||||
|
|
||||||
VERBOSITY?=debug-client
|
LOG?=debug
|
||||||
RUN_FLAGS:=-m -l=${VERBOSITY}
|
LOG_IMAP?=client # client/server/all, or empty to turn it off
|
||||||
|
LOG_SMTP?=--log-smtp # empty to turn it off
|
||||||
|
RUN_FLAGS?=-l=${LOG} --log-imap=${LOG_IMAP} ${LOG_SMTP}
|
||||||
|
|
||||||
run: run-nogui-cli
|
run: run-qt
|
||||||
|
|
||||||
run-qt: ${EXE_TARGET}
|
run-cli: run-nogui
|
||||||
PROTONMAIL_ENV=dev ./$< ${RUN_FLAGS} | tee last.log
|
|
||||||
run-qt-cli: ${EXE_TARGET}
|
|
||||||
PROTONMAIL_ENV=dev ./$< ${RUN_FLAGS} -c
|
|
||||||
|
|
||||||
run-nogui: clean-vendor gofiles
|
run-qt: build-gui
|
||||||
PROTONMAIL_ENV=dev go run ${BUILD_FLAGS_NOGUI} cmd/${TARGET_CMD}/main.go ${RUN_FLAGS} | tee last.log
|
PROTONMAIL_ENV=dev ./${DEPLOY_DIR}/${TARGET_OS}/${LAUNCHER_EXE} ${RUN_FLAGS}
|
||||||
run-nogui-cli: clean-vendor gofiles
|
|
||||||
PROTONMAIL_ENV=dev go run ${BUILD_FLAGS_NOGUI} cmd/${TARGET_CMD}/main.go ${RUN_FLAGS} -c
|
|
||||||
|
|
||||||
run-debug:
|
run-nogui: build-nogui clean-vendor gofiles
|
||||||
PROTONMAIL_ENV=dev dlv debug --build-flags "${BUILD_FLAGS_NOGUI}" cmd/${TARGET_CMD}/main.go -- ${RUN_FLAGS}
|
PROTONMAIL_ENV=dev ./${LAUNCHER_EXE} ${RUN_FLAGS} -c
|
||||||
|
|
||||||
run-qml-preview:
|
clean-vendor:
|
||||||
$(MAKE) -C internal/frontend/qt -f Makefile.local qmlpreview
|
|
||||||
run-ie-qml-preview:
|
|
||||||
$(MAKE) -C internal/frontend/qt-ie -f Makefile.local qmlpreview
|
|
||||||
|
|
||||||
run-ie:
|
|
||||||
TARGET_CMD=Import-Export $(MAKE) run
|
|
||||||
run-ie-qt:
|
|
||||||
TARGET_CMD=Import-Export $(MAKE) run-qt
|
|
||||||
run-ie-nogui:
|
|
||||||
TARGET_CMD=Import-Export $(MAKE) run-nogui
|
|
||||||
|
|
||||||
|
|
||||||
clean-frontend-qt:
|
|
||||||
$(MAKE) -C internal/frontend/qt -f Makefile.local clean
|
|
||||||
clean-frontend-qt-ie:
|
|
||||||
$(MAKE) -C internal/frontend/qt-ie -f Makefile.local clean
|
|
||||||
clean-frontend-qt-common:
|
|
||||||
$(MAKE) -C internal/frontend/qt-common -f Makefile.local clean
|
|
||||||
|
|
||||||
clean-vendor: clean-frontend-qt clean-frontend-qt-ie clean-frontend-qt-common
|
|
||||||
rm -rf ./vendor
|
rm -rf ./vendor
|
||||||
|
|
||||||
clean: clean-vendor
|
clean-gui:
|
||||||
|
cd internal/frontend/bridge-gui/ && \
|
||||||
|
rm -f Version.h && \
|
||||||
|
rm -rf cmake-build-*/
|
||||||
|
|
||||||
|
clean-vcpkg:
|
||||||
|
git submodule deinit -f ./extern/vcpkg
|
||||||
|
rm -rf ./.git/submodule/vcpkg
|
||||||
|
rm -rf ./extern/vcpkg
|
||||||
|
git checkout -- extern/vcpkg
|
||||||
|
|
||||||
|
clean: clean-vendor clean-gui clean-vcpkg
|
||||||
rm -rf vendor-cache
|
rm -rf vendor-cache
|
||||||
rm -rf cmd/Desktop-Bridge/deploy
|
rm -rf cmd/Desktop-Bridge/deploy
|
||||||
rm -rf cmd/Import-Export/deploy
|
rm -rf cmd/Import-Export/deploy
|
||||||
rm -f build last.log mem.pprof main.go
|
rm -f build last.log mem.pprof main.go
|
||||||
rm -rf logo.ico icon.rc icon_windows.syso internal/frontend/qt/icon_windows.syso
|
rm -f ./*.syso
|
||||||
|
rm -f release-notes/bridge.html
|
||||||
|
rm -f release-notes/import-export.html
|
||||||
|
rm -f ${LAUNCHER_EXE} ${BRIDGE_EXE} ${BRIDGE_EXE_NAME}
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: generate
|
||||||
|
generate:
|
||||||
|
go generate ./...
|
||||||
|
$(MAKE) add-license
|
||||||
|
|
||||||
|
.FORCE:
|
||||||
|
|||||||
52
README.md
@ -1,46 +1,49 @@
|
|||||||
# ProtonMail Bridge and Import Export app
|
# Proton Mail Bridge and Import Export app
|
||||||
Copyright (c) 2020 Proton Technologies AG
|
Copyright (c) 2022 Proton AG
|
||||||
|
|
||||||
This repository holds the ProtonMail Bridge application.
|
This repository holds the Proton Mail Bridge and the Proton Mail Import-Export applications.
|
||||||
For a detailed build information see [BUILDS](./BUILDS.md).
|
For a detailed build information see [BUILDS](./BUILDS.md).
|
||||||
For licensing information see [COPYING](./COPYING.md).
|
The license can be found in [LICENSE](./LICENSE) file, for more licensing information see [COPYING_NOTES](./COPYING_NOTES.md).
|
||||||
For contribution policy see [CONTRIBUTING](./CONTRIBUTING.md).
|
For contribution policy see [CONTRIBUTING](./CONTRIBUTING.md).
|
||||||
|
|
||||||
|
|
||||||
## Description Bridge
|
## Description Bridge
|
||||||
ProtonMail Bridge for e-mail clients.
|
Proton Mail Bridge for e-mail clients.
|
||||||
|
|
||||||
When launched, Bridge will initialize local IMAP/SMTP servers and render
|
When launched, Bridge will initialize local IMAP/SMTP servers and render
|
||||||
its GUI.
|
its GUI.
|
||||||
|
|
||||||
To configure an e-mail client, firstly log in using your ProtonMail credentials.
|
To configure an e-mail client, firstly log in using your Proton Mail credentials.
|
||||||
Open your e-mail client and add a new account using the settings which are
|
Open your e-mail client and add a new account using the settings which are
|
||||||
located in the Bridge GUI. The client will only be able to sync with
|
located in the Bridge GUI. The client will only be able to sync with
|
||||||
your ProtonMail account when the Bridge is running, thus the option
|
your Proton Mail account when the Bridge is running, thus the option
|
||||||
to start Bridge on startup is enabled by default.
|
to start Bridge on startup is enabled by default.
|
||||||
|
|
||||||
When the main window is closed, Bridge will continue to run in the
|
When the main window is closed, Bridge will continue to run in the
|
||||||
background.
|
background.
|
||||||
|
|
||||||
More details [on the public website](https://protonmail.com/bridge).
|
More details [on the public website](https://proton.me/mail/bridge).
|
||||||
|
|
||||||
## Description Import-Export app
|
## Launchers
|
||||||
ProtonMail Import-Export app for importing and exporting messages.
|
Launchers are binaries used to run the Proton Mail Bridge or Import-Export apps.
|
||||||
|
|
||||||
To transfer messages, firstly log in using your ProtonMail credentials.
|
Official distributions of the Proton Mail Bridge and Import-Export apps contain
|
||||||
For import, expand your account, and pick the address to which to import
|
both a launcher and the app itself. The launcher is installed in a protected
|
||||||
messages from IMAP server or local EML or MBOX files. For export, pick
|
area of the system (i.e. an area accessible only with admin privileges) and is
|
||||||
the whole account or only a specific address. Then, in both cases,
|
used to run the app. The launcher ensures that nobody tampered with the app's
|
||||||
configure transfer rules (match source and target mailboxes, set time
|
files by verifying their signature using a hardcoded public key. App files are
|
||||||
range limits and so on) and hit start. Once the transfer is complete,
|
placed in regular userspace and are signed by Proton's private key. This
|
||||||
check the results.
|
feature enables the app to securely update itself automatically without asking
|
||||||
|
the user for a password.
|
||||||
|
|
||||||
## Keychain
|
## Keychain
|
||||||
You need to have a keychain in order to run the ProtonMail Bridge. On Mac or
|
You need to have a keychain in order to run the Proton Mail Bridge. On Mac or
|
||||||
Windows, Bridge uses native credential managers. On Linux, use
|
Windows, Bridge uses native credential managers. On Linux, use `secret-service` freedesktop.org API
|
||||||
[Gnome keyring](https://wiki.gnome.org/Projects/GnomeKeyring/)
|
(e.g. [Gnome keyring](https://wiki.gnome.org/Projects/GnomeKeyring/))
|
||||||
or
|
or
|
||||||
[pass](https://www.passwordstore.org/).
|
[pass](https://www.passwordstore.org/). We are working on allowing other secret
|
||||||
|
services (e.g. KeepassXC), but for now only gnome-keyring is usable without
|
||||||
|
major problems.
|
||||||
|
|
||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
@ -55,7 +58,6 @@ or
|
|||||||
|
|
||||||
### Integration testing
|
### Integration testing
|
||||||
- `TEST_ENV`: set which env to use (fake or live)
|
- `TEST_ENV`: set which env to use (fake or live)
|
||||||
- `TEST_APP`: set which app to test (bridge or ie)
|
|
||||||
- `TEST_ACCOUNTS`: set JSON file with configured accounts
|
- `TEST_ACCOUNTS`: set JSON file with configured accounts
|
||||||
- `TAGS`: set build tags for tests
|
- `TAGS`: set build tags for tests
|
||||||
- `FEATURES`: set feature dir, file or scenario to test
|
- `FEATURES`: set feature dir, file or scenario to test
|
||||||
@ -70,9 +72,9 @@ The database stores metadata necessary for presenting messages and mailboxes to
|
|||||||
|
|
||||||
### Preferences
|
### Preferences
|
||||||
User preferences are stored in json at the following location:
|
User preferences are stored in json at the following location:
|
||||||
- Linux: `~/.cache/protonmail/bridge/<cacheVersion>/prefs.json` (unless `XDG_CACHE_HOME` is set, in which case that is used as your `~`)
|
- Linux: `~/.config/protonmail/bridge/prefs.json`
|
||||||
- macOS: `~/Library/Caches/protonmail/bridge/<cacheVersion>/prefs.json`
|
- macOS: `~/Library/ApplicationSupport/protonmail/bridge/prefs.json`
|
||||||
- Windows: `%LOCALAPPDATA%\protonmail\bridge\<cacheVersion>\prefs.json`
|
- Windows: `%APPDATA%\protonmail\bridge\prefs.json`
|
||||||
|
|
||||||
### IMAP Cache
|
### IMAP Cache
|
||||||
The currently subscribed mailboxes are held in a json file:
|
The currently subscribed mailboxes are held in a json file:
|
||||||
|
|||||||
1
TODO.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
- when cache is full, we need to stop the watcher? don't want to keep downloading messages and throwing them away when we try to cache them.
|
||||||
@ -1,19 +1,19 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
// Copyright (c) 2022 Proton AG
|
||||||
//
|
//
|
||||||
// This file is part of ProtonMail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@ -35,261 +35,40 @@ package main
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime/pprof"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/api"
|
"github.com/ProtonMail/proton-bridge/v2/internal/app/base"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
"github.com/ProtonMail/proton-bridge/v2/internal/app/bridge"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/cmd"
|
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/cookies"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/events"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/frontend"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/imap"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/preferences"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/smtp"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/updates"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/users/credentials"
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/config"
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/constants"
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
|
||||||
"github.com/allan-simon/go-singleinstance"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// cacheVersion is used for cache files such as lock, events, preferences, user_info, db files.
|
appUsage = "Proton Mail IMAP and SMTP Bridge"
|
||||||
// Different number will drop old files and create new ones.
|
configName = "bridge"
|
||||||
cacheVersion = "c11"
|
updateURLName = "bridge"
|
||||||
|
keychainName = "bridge"
|
||||||
appName = "bridge"
|
cacheVersion = "c11"
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
log = logrus.WithField("pkg", "main") //nolint[gochecknoglobals]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cmd.Main(
|
base, err := base.New(
|
||||||
"ProtonMail Bridge",
|
constants.FullAppName,
|
||||||
"ProtonMail IMAP and SMTP Bridge",
|
appUsage,
|
||||||
[]cli.Flag{
|
configName,
|
||||||
cli.BoolFlag{
|
updateURLName,
|
||||||
Name: "no-window",
|
keychainName,
|
||||||
Usage: "Don't show window after start"},
|
cacheVersion,
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "noninteractive",
|
|
||||||
Usage: "Start Bridge entirely noninteractively"},
|
|
||||||
},
|
|
||||||
run,
|
|
||||||
)
|
)
|
||||||
}
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatal("Failed to create app base")
|
||||||
// run initializes and starts everything in a precise order.
|
}
|
||||||
//
|
// Other instance already running.
|
||||||
// IMPORTANT: ***Read the comments before CHANGING the order ***
|
if base == nil {
|
||||||
func run(context *cli.Context) (contextError error) { // nolint[funlen]
|
return
|
||||||
// We need to have config instance to setup a logs, panic handler, etc ...
|
}
|
||||||
cfg := config.New(appName, constants.Version, constants.Revision, cacheVersion)
|
|
||||||
|
if err := bridge.New(base).Run(os.Args); err != nil {
|
||||||
// We want to know about any problem. Our PanicHandler calls sentry which is
|
logrus.WithError(err).Fatal("Bridge exited with error")
|
||||||
// not dependent on anything else. If that fails, it tries to create crash
|
}
|
||||||
// report which will not be possible if no folder can be created. That's the
|
|
||||||
// only problem we will not be notified about in any way.
|
|
||||||
panicHandler := &cmd.PanicHandler{
|
|
||||||
AppName: "ProtonMail Bridge",
|
|
||||||
Config: cfg,
|
|
||||||
Err: &contextError,
|
|
||||||
}
|
|
||||||
defer panicHandler.HandlePanic()
|
|
||||||
|
|
||||||
// First we need config and create necessary folder; it's dependency for everything.
|
|
||||||
if err := cfg.CreateDirs(); err != nil {
|
|
||||||
log.Fatal("Cannot create necessary folders: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup of logs should be as soon as possible to ensure we record every wanted report in the log.
|
|
||||||
logLevel := context.GlobalString("log-level")
|
|
||||||
debugClient, debugServer := config.SetupLog(cfg, logLevel)
|
|
||||||
|
|
||||||
// Doesn't make sense to continue when Bridge was invoked with wrong arguments.
|
|
||||||
// We should tell that to the user before we do anything else.
|
|
||||||
if context.Args().First() != "" {
|
|
||||||
_ = cli.ShowAppHelp(context)
|
|
||||||
return cli.NewExitError("Unknown argument", 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's safe to get version JSON file even when other instance is running.
|
|
||||||
// (thus we put it before check of presence of other Bridge instance).
|
|
||||||
updates := updates.NewBridge(cfg.GetUpdateDir())
|
|
||||||
|
|
||||||
if dir := context.GlobalString("version-json"); dir != "" {
|
|
||||||
cmd.GenerateVersionFiles(updates, dir)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should be called after logs are configured but before preferences are created.
|
|
||||||
migratePreferencesFromC10(cfg)
|
|
||||||
|
|
||||||
// ClearOldData before starting new bridge to do a proper setup.
|
|
||||||
//
|
|
||||||
// IMPORTANT: If you the change position of this you will need to wait
|
|
||||||
// until force-update to be applied on all currently used bridge
|
|
||||||
// versions
|
|
||||||
if err := cfg.ClearOldData(); err != nil {
|
|
||||||
log.Error("Cannot clear old data: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTLSConfig is needed for IMAP, SMTL and local bridge API (to check second instance).
|
|
||||||
//
|
|
||||||
// This should be called after ClearOldData, in order to re-create the
|
|
||||||
// certificates if clean data will remove them (accidentally or on purpose).
|
|
||||||
tls, err := config.GetTLSConfig(cfg)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Fatal("Cannot get TLS certificate")
|
|
||||||
}
|
|
||||||
|
|
||||||
pref := preferences.New(cfg)
|
|
||||||
|
|
||||||
// Now we can try to proceed with starting the bridge. First we need to ensure
|
|
||||||
// this is the only instance. If not, we will end and focus the existing one.
|
|
||||||
lock, err := singleinstance.CreateLockFile(cfg.GetLockPath())
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Bridge is already running")
|
|
||||||
if err := api.CheckOtherInstanceAndFocus(pref.GetInt(preferences.APIPortKey), tls); err != nil {
|
|
||||||
cmd.DisableRestart()
|
|
||||||
log.Error("Second instance: ", err)
|
|
||||||
}
|
|
||||||
return cli.NewExitError("Bridge is already running.", 3)
|
|
||||||
}
|
|
||||||
defer lock.Close() //nolint[errcheck]
|
|
||||||
|
|
||||||
// In case user wants to do CPU or memory profiles...
|
|
||||||
if doCPUProfile := context.GlobalBool("cpu-prof"); doCPUProfile {
|
|
||||||
cmd.StartCPUProfile()
|
|
||||||
defer pprof.StopCPUProfile()
|
|
||||||
}
|
|
||||||
|
|
||||||
if doMemoryProfile := context.GlobalBool("mem-prof"); doMemoryProfile {
|
|
||||||
defer cmd.MakeMemoryProfile()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we initialize all Bridge parts.
|
|
||||||
log.Debug("Initializing bridge...")
|
|
||||||
eventListener := listener.New()
|
|
||||||
events.SetupEvents(eventListener)
|
|
||||||
|
|
||||||
credentialsStore, credentialsError := credentials.NewStore(appName)
|
|
||||||
if credentialsError != nil {
|
|
||||||
log.Error("Could not get credentials store: ", credentialsError)
|
|
||||||
}
|
|
||||||
|
|
||||||
cm := pmapi.NewClientManager(cfg.GetAPIConfig())
|
|
||||||
|
|
||||||
// Different build types have different roundtrippers (e.g. we want to enable
|
|
||||||
// TLS fingerprint checks in production builds). GetRoundTripper has a different
|
|
||||||
// implementation depending on whether build flag pmapi_prod is used or not.
|
|
||||||
cm.SetRoundTripper(cfg.GetRoundTripper(cm, eventListener))
|
|
||||||
|
|
||||||
// Cookies must be persisted across restarts.
|
|
||||||
jar, err := cookies.NewCookieJar(pref)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithError(err).Warn("Could not create cookie jar")
|
|
||||||
} else {
|
|
||||||
cm.SetCookieJar(jar)
|
|
||||||
}
|
|
||||||
|
|
||||||
bridgeInstance := bridge.New(cfg, pref, panicHandler, eventListener, cm, credentialsStore)
|
|
||||||
imapBackend := imap.NewIMAPBackend(panicHandler, eventListener, cfg, bridgeInstance)
|
|
||||||
smtpBackend := smtp.NewSMTPBackend(panicHandler, eventListener, pref, bridgeInstance)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer panicHandler.HandlePanic()
|
|
||||||
apiServer := api.NewAPIServer(pref, tls, cfg.GetTLSCertPath(), cfg.GetTLSKeyPath(), eventListener)
|
|
||||||
apiServer.ListenAndServe()
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer panicHandler.HandlePanic()
|
|
||||||
imapPort := pref.GetInt(preferences.IMAPPortKey)
|
|
||||||
imapServer := imap.NewIMAPServer(debugClient, debugServer, imapPort, tls, imapBackend, eventListener)
|
|
||||||
imapServer.ListenAndServe()
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer panicHandler.HandlePanic()
|
|
||||||
smtpPort := pref.GetInt(preferences.SMTPPortKey)
|
|
||||||
useSSL := pref.GetBool(preferences.SMTPSSLKey)
|
|
||||||
smtpServer := smtp.NewSMTPServer(debugClient || debugServer, smtpPort, useSSL, tls, smtpBackend, eventListener)
|
|
||||||
smtpServer.ListenAndServe()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Decide about frontend mode before initializing rest of bridge.
|
|
||||||
var frontendMode string
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case context.GlobalBool("cli"):
|
|
||||||
frontendMode = "cli"
|
|
||||||
case context.GlobalBool("noninteractive"):
|
|
||||||
frontendMode = "noninteractive"
|
|
||||||
default:
|
|
||||||
frontendMode = "qt"
|
|
||||||
}
|
|
||||||
|
|
||||||
log.WithField("mode", frontendMode).Debug("Determined frontend mode to use")
|
|
||||||
|
|
||||||
// If we are starting bridge in noninteractive mode, simply block instead of starting a frontend.
|
|
||||||
if frontendMode == "noninteractive" {
|
|
||||||
<-(make(chan struct{}))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
showWindowOnStart := !context.GlobalBool("no-window")
|
|
||||||
frontend := frontend.New(constants.Version, constants.BuildVersion, frontendMode, showWindowOnStart, panicHandler, cfg, pref, eventListener, updates, bridgeInstance, smtpBackend)
|
|
||||||
|
|
||||||
// Last part is to start everything.
|
|
||||||
log.Debug("Starting frontend...")
|
|
||||||
if err := frontend.Loop(credentialsError); err != nil {
|
|
||||||
log.Error("Frontend failed with error: ", err)
|
|
||||||
return cli.NewExitError("Frontend error", 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
if frontend.IsAppRestarting() {
|
|
||||||
cmd.RestartApp()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// migratePreferencesFromC10 will copy preferences from c10 folder to c11.
|
|
||||||
// It will happen only when c10/prefs.json exists and c11/prefs.json not.
|
|
||||||
// No configuration changed between c10 and c11 versions.
|
|
||||||
func migratePreferencesFromC10(cfg *config.Config) {
|
|
||||||
pref10Path := config.New(appName, constants.Version, constants.Revision, "c10").GetPreferencesPath()
|
|
||||||
if _, err := os.Stat(pref10Path); os.IsNotExist(err) {
|
|
||||||
log.WithField("path", pref10Path).Trace("Old preferences does not exist, migration skipped")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pref11Path := cfg.GetPreferencesPath()
|
|
||||||
if _, err := os.Stat(pref11Path); err == nil {
|
|
||||||
log.WithField("path", pref11Path).Trace("New preferences already exists, migration skipped")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(pref10Path) //nolint[gosec]
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Problem to load old preferences")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ioutil.WriteFile(pref11Path, data, 0600)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Problem to migrate preferences")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Preferences migrated")
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,177 +0,0 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
|
||||||
//
|
|
||||||
// This file is part of ProtonMail Bridge.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime/pprof"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/cmd"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/cookies"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/events"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/frontend"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/importexport"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/preferences"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/updates"
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/users/credentials"
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/config"
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/constants"
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
|
||||||
"github.com/allan-simon/go-singleinstance"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// cacheVersion is used for cache files such as lock, or preferences.
|
|
||||||
// Different number will drop old files and create new ones.
|
|
||||||
cacheVersion = "c11"
|
|
||||||
|
|
||||||
appName = "importExport"
|
|
||||||
appNameDash = "import-export-app"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
log = logrus.WithField("pkg", "main") //nolint[gochecknoglobals]
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
cmd.Main(
|
|
||||||
"ProtonMail Import-Export",
|
|
||||||
"ProtonMail Import-Export app",
|
|
||||||
nil,
|
|
||||||
run,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// run initializes and starts everything in a precise order.
|
|
||||||
//
|
|
||||||
// IMPORTANT: ***Read the comments before CHANGING the order ***
|
|
||||||
func run(context *cli.Context) (contextError error) { // nolint[funlen]
|
|
||||||
// We need to have config instance to setup a logs, panic handler, etc ...
|
|
||||||
cfg := config.New(appName, constants.Version, constants.Revision, cacheVersion)
|
|
||||||
|
|
||||||
// We want to know about any problem. Our PanicHandler calls sentry which is
|
|
||||||
// not dependent on anything else. If that fails, it tries to create crash
|
|
||||||
// report which will not be possible if no folder can be created. That's the
|
|
||||||
// only problem we will not be notified about in any way.
|
|
||||||
panicHandler := &cmd.PanicHandler{
|
|
||||||
AppName: "ProtonMail Import-Export app",
|
|
||||||
Config: cfg,
|
|
||||||
Err: &contextError,
|
|
||||||
}
|
|
||||||
defer panicHandler.HandlePanic()
|
|
||||||
|
|
||||||
// First we need config and create necessary folder; it's dependency for everything.
|
|
||||||
if err := cfg.CreateDirs(); err != nil {
|
|
||||||
log.Fatal("Cannot create necessary folders: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup of logs should be as soon as possible to ensure we record every wanted report in the log.
|
|
||||||
logLevel := context.GlobalString("log-level")
|
|
||||||
_, _ = config.SetupLog(cfg, logLevel)
|
|
||||||
|
|
||||||
// Doesn't make sense to continue when Import-Export was invoked with wrong arguments.
|
|
||||||
// We should tell that to the user before we do anything else.
|
|
||||||
if context.Args().First() != "" {
|
|
||||||
_ = cli.ShowAppHelp(context)
|
|
||||||
return cli.NewExitError("Unknown argument", 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's safe to get version JSON file even when other instance is running.
|
|
||||||
// (thus we put it before check of presence of other Import-Export instance).
|
|
||||||
updates := updates.NewImportExport(cfg.GetUpdateDir())
|
|
||||||
|
|
||||||
if dir := context.GlobalString("version-json"); dir != "" {
|
|
||||||
cmd.GenerateVersionFiles(updates, dir)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we can try to proceed with starting the Import-Export. First we need to ensure
|
|
||||||
// this is the only instance. If not, we will end and focus the existing one.
|
|
||||||
lock, err := singleinstance.CreateLockFile(cfg.GetLockPath())
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Import-Export app is already running")
|
|
||||||
return cli.NewExitError("Import-Export app is already running.", 3)
|
|
||||||
}
|
|
||||||
defer lock.Close() //nolint[errcheck]
|
|
||||||
|
|
||||||
// In case user wants to do CPU or memory profiles...
|
|
||||||
if doCPUProfile := context.GlobalBool("cpu-prof"); doCPUProfile {
|
|
||||||
cmd.StartCPUProfile()
|
|
||||||
defer pprof.StopCPUProfile()
|
|
||||||
}
|
|
||||||
|
|
||||||
if doMemoryProfile := context.GlobalBool("mem-prof"); doMemoryProfile {
|
|
||||||
defer cmd.MakeMemoryProfile()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we initialize all Import-Export parts.
|
|
||||||
log.Debug("Initializing import-export...")
|
|
||||||
eventListener := listener.New()
|
|
||||||
events.SetupEvents(eventListener)
|
|
||||||
|
|
||||||
credentialsStore, credentialsError := credentials.NewStore(appNameDash)
|
|
||||||
if credentialsError != nil {
|
|
||||||
log.Error("Could not get credentials store: ", credentialsError)
|
|
||||||
}
|
|
||||||
|
|
||||||
cm := pmapi.NewClientManager(cfg.GetAPIConfig())
|
|
||||||
|
|
||||||
// Different build types have different roundtrippers (e.g. we want to enable
|
|
||||||
// TLS fingerprint checks in production builds). GetRoundTripper has a different
|
|
||||||
// implementation depending on whether build flag pmapi_prod is used or not.
|
|
||||||
cm.SetRoundTripper(cfg.GetRoundTripper(cm, eventListener))
|
|
||||||
|
|
||||||
pref := preferences.New(cfg)
|
|
||||||
|
|
||||||
// Cookies must be persisted across restarts.
|
|
||||||
jar, err := cookies.NewCookieJar(pref)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithError(err).Warn("Could not create cookie jar")
|
|
||||||
} else {
|
|
||||||
cm.SetCookieJar(jar)
|
|
||||||
}
|
|
||||||
|
|
||||||
importexportInstance := importexport.New(cfg, panicHandler, eventListener, cm, credentialsStore)
|
|
||||||
|
|
||||||
// Decide about frontend mode before initializing rest of import-export.
|
|
||||||
var frontendMode string
|
|
||||||
switch {
|
|
||||||
case context.GlobalBool("cli"):
|
|
||||||
frontendMode = "cli"
|
|
||||||
default:
|
|
||||||
frontendMode = "qt"
|
|
||||||
}
|
|
||||||
log.WithField("mode", frontendMode).Debug("Determined frontend mode to use")
|
|
||||||
|
|
||||||
frontend := frontend.NewImportExport(constants.Version, constants.BuildVersion, frontendMode, panicHandler, cfg, eventListener, updates, importexportInstance)
|
|
||||||
|
|
||||||
// Last part is to start everything.
|
|
||||||
log.Debug("Starting frontend...")
|
|
||||||
if err := frontend.Loop(credentialsError); err != nil {
|
|
||||||
log.Error("Frontend failed with error: ", err)
|
|
||||||
return cli.NewExitError("Frontend error", 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
if frontend.IsAppRestarting() {
|
|
||||||
cmd.RestartApp()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
294
cmd/launcher/main.go
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/useragent"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/crash"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/locations"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/logging"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/sentry"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/versioner"
|
||||||
|
"github.com/bradenaw/juniper/xslices"
|
||||||
|
"github.com/elastic/go-sysinfo"
|
||||||
|
"github.com/elastic/go-sysinfo/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sys/execabs"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
appName = "Proton Mail Launcher"
|
||||||
|
configName = "bridge"
|
||||||
|
exeName = "bridge"
|
||||||
|
guiName = "bridge-gui"
|
||||||
|
|
||||||
|
FlagCLI = "--cli"
|
||||||
|
FlagCLIShort = "-c"
|
||||||
|
FlagLauncher = "--launcher"
|
||||||
|
FlagWait = "--wait"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() { //nolint:funlen
|
||||||
|
reporter := sentry.NewReporter(appName, constants.Version, useragent.New())
|
||||||
|
|
||||||
|
crashHandler := crash.NewHandler(reporter.ReportException)
|
||||||
|
defer crashHandler.HandlePanic()
|
||||||
|
|
||||||
|
locationsProvider, err := locations.NewDefaultProvider(filepath.Join(constants.VendorName, configName))
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatal("Failed to get locations provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
locations := locations.New(locationsProvider, configName)
|
||||||
|
|
||||||
|
logsPath, err := locations.ProvideLogsPath()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatal("Failed to get logs path")
|
||||||
|
}
|
||||||
|
crashHandler.AddRecoveryAction(logging.DumpStackTrace(logsPath))
|
||||||
|
|
||||||
|
if err := logging.Init(logsPath); err != nil {
|
||||||
|
logrus.WithError(err).Fatal("Failed to setup logging")
|
||||||
|
}
|
||||||
|
|
||||||
|
logging.SetLevel(os.Getenv("VERBOSITY"))
|
||||||
|
|
||||||
|
updatesPath, err := locations.ProvideUpdatesPath()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatal("Failed to get updates path")
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := crypto.NewKeyFromArmored(updater.DefaultPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatal("Failed to create new verification key")
|
||||||
|
}
|
||||||
|
|
||||||
|
kr, err := crypto.NewKeyRing(key)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatal("Failed to create new verification keyring")
|
||||||
|
}
|
||||||
|
|
||||||
|
versioner := versioner.New(updatesPath)
|
||||||
|
|
||||||
|
exeToLaunch := guiName
|
||||||
|
args := os.Args[1:]
|
||||||
|
if inCLIMode(args) {
|
||||||
|
exeToLaunch = exeName
|
||||||
|
}
|
||||||
|
|
||||||
|
exe, err := getPathToUpdatedExecutable(exeToLaunch, versioner, kr, reporter)
|
||||||
|
if err != nil {
|
||||||
|
if exe, err = getFallbackExecutable(exeToLaunch, versioner); err != nil {
|
||||||
|
logrus.WithError(err).Fatal("Failed to find any launchable executable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
launcher, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatal("Failed to determine path to launcher")
|
||||||
|
}
|
||||||
|
|
||||||
|
args, wait, mainExe := findAndStripWait(args)
|
||||||
|
if wait {
|
||||||
|
waitForProcessToFinish(mainExe)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := execabs.Command(exe, appendLauncherPath(launcher, args)...) //nolint:gosec
|
||||||
|
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
// On windows, if you use Run(), a terminal stays open; we don't want that.
|
||||||
|
if //goland:noinspection GoBoolExpressions
|
||||||
|
runtime.GOOS == "windows" {
|
||||||
|
err = cmd.Start()
|
||||||
|
} else {
|
||||||
|
err = cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatal("Failed to launch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendLauncherPath add launcher path if missing.
|
||||||
|
func appendLauncherPath(path string, args []string) []string {
|
||||||
|
if !sliceContains(args, FlagLauncher) {
|
||||||
|
res := append([]string{}, args...)
|
||||||
|
res = append(res, FlagLauncher, path)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// inCLIMode detect if CLI mode is asked.
|
||||||
|
func inCLIMode(args []string) bool {
|
||||||
|
return sliceContains(args, FlagCLI) || sliceContains(args, FlagCLIShort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sliceContains checks if a value is present in a list.
|
||||||
|
func sliceContains[T comparable](list []T, s T) bool {
|
||||||
|
return xslices.Any(list, func(arg T) bool { return arg == s })
|
||||||
|
}
|
||||||
|
|
||||||
|
// findAndStrip check if a value is present in s list and remove all occurrences of the value from this list.
|
||||||
|
func findAndStrip[T comparable](slice []T, v T) (strippedList []T, found bool) {
|
||||||
|
strippedList = xslices.Filter(slice, func(value T) bool {
|
||||||
|
return value != v
|
||||||
|
})
|
||||||
|
return strippedList, len(strippedList) != len(slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
// findAndStripWait Check for waiter flag get its value and clean them both.
|
||||||
|
func findAndStripWait(args []string) ([]string, bool, string) {
|
||||||
|
res := append([]string{}, args...)
|
||||||
|
|
||||||
|
hasFlag := false
|
||||||
|
var value string
|
||||||
|
|
||||||
|
for k, v := range res {
|
||||||
|
if v != FlagWait {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if k+1 >= len(res) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
hasFlag = true
|
||||||
|
value = res[k+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasFlag {
|
||||||
|
res, _ = findAndStrip(res, FlagWait)
|
||||||
|
res, _ = findAndStrip(res, value)
|
||||||
|
}
|
||||||
|
return res, hasFlag, value
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPathToUpdatedExecutable(
|
||||||
|
name string,
|
||||||
|
versioner *versioner.Versioner,
|
||||||
|
kr *crypto.KeyRing,
|
||||||
|
reporter *sentry.Reporter,
|
||||||
|
) (string, error) {
|
||||||
|
versions, err := versioner.ListVersions()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to list available versions")
|
||||||
|
}
|
||||||
|
|
||||||
|
currentVersion, err := semver.StrictNewVersion(constants.Version)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithField("version", constants.Version).WithError(err).Error("Failed to parse current version")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, version := range versions {
|
||||||
|
vlog := logrus.WithField("version", version)
|
||||||
|
|
||||||
|
if err := version.VerifyFiles(kr); err != nil {
|
||||||
|
vlog.WithError(err).Error("Files failed verification and will be removed")
|
||||||
|
|
||||||
|
if err := reporter.ReportMessage(fmt.Sprintf("version %v failed verification: %v", version, err)); err != nil {
|
||||||
|
vlog.WithError(err).Error("Failed to report corrupt update files")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := version.Remove(); err != nil {
|
||||||
|
vlog.WithError(err).Error("Failed to remove files")
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip versions that are less or equal to launcher version.
|
||||||
|
if currentVersion != nil && !version.SemVer().GreaterThan(currentVersion) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
exe, err := version.GetExecutable(name)
|
||||||
|
if err != nil {
|
||||||
|
vlog.WithError(err).Error("Failed to get executable")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return exe, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("no available newer versions")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFallbackExecutable(name string, versioner *versioner.Versioner) (string, error) {
|
||||||
|
logrus.Info("Searching for fallback executable")
|
||||||
|
|
||||||
|
launcher, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "failed to determine path to launcher")
|
||||||
|
}
|
||||||
|
|
||||||
|
return versioner.GetExecutableInDirectory(name, filepath.Dir(launcher))
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForProcessToFinish waits until the process with the given path is finished.
|
||||||
|
func waitForProcessToFinish(exePath string) {
|
||||||
|
for {
|
||||||
|
processes, err := sysinfo.Processes()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Error("Could not determine running processes")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
exeInfo, err := os.Stat(exePath)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).WithField("file", exeInfo).Error("Could not retrieve file info")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if xslices.Any(processes, func(process types.Process) bool {
|
||||||
|
info, err := process.Info()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Error("Could not retrieve process info")
|
||||||
|
}
|
||||||
|
|
||||||
|
return sameFile(exeInfo, info.Exe)
|
||||||
|
}) {
|
||||||
|
logrus.Infof("Waiting for %v to finish.", exeInfo.Name())
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sameFile(info os.FileInfo, path string) bool {
|
||||||
|
pathInfo, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).WithField("file", path).Error("Could not retrieve file info")
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.SameFile(pathInfo, info)
|
||||||
|
}
|
||||||
58
cmd/launcher/main_test.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/bradenaw/juniper/xslices"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSliceContains(t *testing.T) {
|
||||||
|
assert.True(t, sliceContains([]string{"a", "b", "c"}, "a"))
|
||||||
|
assert.True(t, sliceContains([]int{1, 2, 3}, 2))
|
||||||
|
assert.False(t, sliceContains([]string{"a", "b", "c"}, "A"))
|
||||||
|
assert.False(t, sliceContains([]int{1, 2, 3}, 4))
|
||||||
|
assert.False(t, sliceContains([]string{}, "a"))
|
||||||
|
assert.True(t, sliceContains([]string{"a", "a"}, "a"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindAndStrip(t *testing.T) {
|
||||||
|
list := []string{"a", "b", "c", "c", "b", "c"}
|
||||||
|
|
||||||
|
result, found := findAndStrip(list, "a")
|
||||||
|
assert.True(t, found)
|
||||||
|
assert.True(t, xslices.Equal(result, []string{"b", "c", "c", "b", "c"}))
|
||||||
|
|
||||||
|
result, found = findAndStrip(list, "c")
|
||||||
|
assert.True(t, found)
|
||||||
|
assert.True(t, xslices.Equal(result, []string{"a", "b", "b"}))
|
||||||
|
|
||||||
|
result, found = findAndStrip([]string{"c", "c", "c"}, "c")
|
||||||
|
assert.True(t, found)
|
||||||
|
assert.True(t, xslices.Equal(result, []string{}))
|
||||||
|
|
||||||
|
result, found = findAndStrip(list, "A")
|
||||||
|
assert.False(t, found)
|
||||||
|
assert.True(t, xslices.Equal(result, list))
|
||||||
|
|
||||||
|
result, found = findAndStrip([]string{}, "a")
|
||||||
|
assert.False(t, found)
|
||||||
|
assert.True(t, xslices.Equal(result, []string{}))
|
||||||
|
}
|
||||||
BIN
dist/Bridge.icns
vendored
Normal file
BIN
dist/bridge.ico
vendored
Normal file
|
After Width: | Height: | Size: 124 KiB |
32
dist/bridge.svg
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_9588_57903)">
|
||||||
|
<path d="M127.416 -0.0898438C63.1423 -0.0898438 11.0449 51.9864 11.0449 116.234V233.331C11.0449 245.779 21.1405 255.871 33.5942 255.871H223.647C234.767 255.871 243.788 246.853 243.788 235.738V116.234C243.788 51.9948 191.691 -0.0898438 127.416 -0.0898438ZM194.401 115.589L143.537 158.421C134.357 166.155 120.929 166.155 111.749 158.421L60.8849 115.589C60.8849 79.2409 90.3659 49.7718 126.728 49.7718H128.558C164.92 49.7718 194.401 79.2409 194.401 115.589Z" fill="#6D4AFF"/>
|
||||||
|
<path d="M127.416 -0.0898438C63.1423 -0.0898438 11.0449 51.9864 11.0449 116.234V233.331C11.0449 245.779 21.1405 255.871 33.5942 255.871H223.647C234.767 255.871 243.788 246.853 243.788 235.738V116.234C243.788 51.9948 191.691 -0.0898438 127.416 -0.0898438ZM194.401 115.589L143.537 158.421C134.357 166.155 120.929 166.155 111.749 158.421L60.8849 115.589C60.8849 79.2409 90.3659 49.7718 126.728 49.7718H128.558C164.92 49.7718 194.401 79.2409 194.401 115.589Z" fill="url(#paint0_linear_9588_57903)"/>
|
||||||
|
<g filter="url(#filter0_i_9588_57903)">
|
||||||
|
<path d="M143.572 158.939C138.271 163.23 124.489 169.238 111.766 158.939C99.0439 148.64 72.6401 125.871 61.0285 115.774H61.0868L60.8676 115.59C60.8676 79.2418 90.3367 49.7728 126.684 49.7728H128.513C164.861 49.7728 194.33 79.2418 194.33 115.59L194.111 115.774H194.31V255.872H223.564C234.679 255.872 243.697 246.854 243.697 235.739V116.235C243.697 51.9958 191.62 -0.0888672 127.372 -0.0888672C63.1241 -0.0888672 11.0479 51.9874 11.0479 116.235V123.587L82.9896 185.444C88.2906 190.492 102.224 197.56 115.553 185.444C128.881 173.327 139.786 162.726 143.572 158.939Z" fill="url(#paint1_radial_9588_57903)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_i_9588_57903" x="7.29545" y="-0.0888672" width="236.401" height="266.43" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dx="-3.7524" dy="10.4692"/>
|
||||||
|
<feGaussianBlur stdDeviation="28.143"/>
|
||||||
|
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0.462745 0 0 0 0 0.337255 0 0 0 0 1 0 0 0 0.24 0"/>
|
||||||
|
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_9588_57903"/>
|
||||||
|
</filter>
|
||||||
|
<linearGradient id="paint0_linear_9588_57903" x1="19.3784" y1="285.405" x2="54.2022" y2="186.949" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#28B0E8"/>
|
||||||
|
<stop offset="1" stop-color="#C5B7FF" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient id="paint1_radial_9588_57903" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(229.979 277.075) rotate(-138.034) scale(294.445 240.743)">
|
||||||
|
<stop stop-color="#E2DBFF"/>
|
||||||
|
<stop offset="1" stop-color="#6D4AFF"/>
|
||||||
|
</radialGradient>
|
||||||
|
<clipPath id="clip0_9588_57903">
|
||||||
|
<rect width="256" height="256" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
36
dist/info.rc
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#define STRINGIZE_(x) #x
|
||||||
|
#define STRINGIZE(x) STRINGIZE_(x)
|
||||||
|
|
||||||
|
IDI_ICON1 ICON DISCARDABLE STRINGIZE(ICO_FILE)
|
||||||
|
|
||||||
|
#define FILE_COMMENTS "The Bridge is an application that runs on your computer in the background and seamlessly encrypts and decrypts your mail as it enters and leaves your computer."
|
||||||
|
#define FILE_DESCRIPTION "Proton Mail Bridge"
|
||||||
|
#define INTERNAL_NAME STRINGIZE(EXE_NAME)
|
||||||
|
#define PRODUCT_NAME "Proton Mail Bridge for Windows"
|
||||||
|
|
||||||
|
#define LEGAL_COPYRIGHT "(C) " STRINGIZE(YEAR) " Proton AG"
|
||||||
|
|
||||||
|
1 VERSIONINFO
|
||||||
|
FILEVERSION FILE_VERSION_COMMA,0
|
||||||
|
PRODUCTVERSION FILE_VERSION_COMMA,0
|
||||||
|
BEGIN
|
||||||
|
BLOCK "StringFileInfo"
|
||||||
|
BEGIN
|
||||||
|
BLOCK "040904b0"
|
||||||
|
BEGIN
|
||||||
|
VALUE "Comments", FILE_COMMENTS
|
||||||
|
VALUE "CompanyName", "Proton AG"
|
||||||
|
VALUE "FileDescription", FILE_DESCRIPTION
|
||||||
|
VALUE "FileVersion", STRINGIZE(FILE_VERSION)
|
||||||
|
VALUE "InternalName", INTERNAL_NAME
|
||||||
|
VALUE "LegalCopyright", LEGAL_COPYRIGHT
|
||||||
|
VALUE "OriginalFilename", STRINGIZE(ORIGINAL_FILE_NAME)
|
||||||
|
VALUE "ProductName", PRODUCT_NAME
|
||||||
|
VALUE "ProductVersion", STRINGIZE(PRODUCT_VERSION)
|
||||||
|
END
|
||||||
|
END
|
||||||
|
BLOCK "VarFileInfo"
|
||||||
|
BEGIN
|
||||||
|
VALUE "Translation", 0x0409, 0x04B0
|
||||||
|
END
|
||||||
|
END
|
||||||
11
dist/proton-bridge.desktop
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Version=1.1
|
||||||
|
Name=Proton Mail Bridge
|
||||||
|
GenericName=Proton Mail Bridge for Linux
|
||||||
|
Comment=The Bridge is an application that runs on your computer in the background and seamlessly encrypts and decrypts your mail as it enters and leaves your computer.
|
||||||
|
Icon=protonmail-bridge
|
||||||
|
Exec=protonmail-bridge
|
||||||
|
Terminal=false
|
||||||
|
Categories=Office;Email;Network
|
||||||
|
StartupWMClass=Proton Mail Bridge
|
||||||
BIN
dist/raw/mac_icon_128x128.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
dist/raw/mac_icon_128x128@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
dist/raw/mac_icon_16x16.png
vendored
Normal file
|
After Width: | Height: | Size: 715 B |
BIN
dist/raw/mac_icon_16x16@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
dist/raw/mac_icon_256x256.png
vendored
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
dist/raw/mac_icon_256x256@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
dist/raw/mac_icon_32x32.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
dist/raw/mac_icon_32x32@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
dist/raw/mac_icon_512x512.png
vendored
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
dist/raw/mac_icon_512x512@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 494 KiB |
32
dist/raw/win+lin_icon_16x16.svg
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_9588_57915)">
|
||||||
|
<path d="M7.9607 -0.00488281C3.9452 -0.00488281 0.69043 3.24988 0.69043 7.26539V14.5839C0.69043 15.3619 1.32115 15.9926 2.09919 15.9926H13.9727C14.6674 15.9926 15.231 15.429 15.231 14.7344V7.26539C15.231 3.25041 11.9762 -0.00488281 7.9607 -0.00488281ZM12.1456 7.22502L8.96786 9.90202C8.39429 10.3854 7.55543 10.3854 6.98186 9.90202L3.80416 7.22502C3.80416 4.95329 5.64598 3.11147 7.91771 3.11147H8.032C10.3037 3.11147 12.1456 4.95329 12.1456 7.22502Z" fill="#6D4AFF"/>
|
||||||
|
<path d="M7.9607 -0.00488281C3.9452 -0.00488281 0.69043 3.24988 0.69043 7.26539V14.5839C0.69043 15.3619 1.32115 15.9926 2.09919 15.9926H13.9727C14.6674 15.9926 15.231 15.429 15.231 14.7344V7.26539C15.231 3.25041 11.9762 -0.00488281 7.9607 -0.00488281ZM12.1456 7.22502L8.96786 9.90202C8.39429 10.3854 7.55543 10.3854 6.98186 9.90202L3.80416 7.22502C3.80416 4.95329 5.64598 3.11147 7.91771 3.11147H8.032C10.3037 3.11147 12.1456 4.95329 12.1456 7.22502Z" fill="url(#paint0_linear_9588_57915)"/>
|
||||||
|
<g filter="url(#filter0_i_9588_57915)">
|
||||||
|
<path d="M8.97323 9.93257C8.64192 10.2008 7.78051 10.5763 6.98537 9.93257C6.19022 9.28888 4.53998 7.86583 3.81426 7.23476H3.81805L3.80416 7.22306C3.80416 4.95133 5.64598 3.10952 7.91771 3.10952H8.032C10.3037 3.10952 12.1456 4.95133 12.1456 7.22306L12.1317 7.23476H12.1443V15.9907H13.9727C14.6674 15.9907 15.231 15.4271 15.231 14.7324V7.26343C15.231 3.24845 11.9762 -0.00683594 7.9607 -0.00683594C3.9452 -0.00683594 0.69043 3.24793 0.69043 7.26343V7.72306L5.18683 11.5891C5.51814 11.9047 6.38901 12.3464 7.22202 11.5891C8.05502 10.8318 8.73658 10.1692 8.97323 9.93257Z" fill="url(#paint1_radial_9588_57915)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_i_9588_57915" x="0.455905" y="-0.00683594" width="14.7755" height="16.6514" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dx="-0.234525" dy="0.654324"/>
|
||||||
|
<feGaussianBlur stdDeviation="1.75894"/>
|
||||||
|
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0.462745 0 0 0 0 0.337255 0 0 0 0 1 0 0 0 0.24 0"/>
|
||||||
|
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_9588_57915"/>
|
||||||
|
</filter>
|
||||||
|
<linearGradient id="paint0_linear_9588_57915" x1="1.21106" y1="17.8385" x2="3.38824" y2="11.6856" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#28B0E8"/>
|
||||||
|
<stop offset="1" stop-color="#C5B7FF" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient id="paint1_radial_9588_57915" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(14.3736 17.3159) rotate(-138.034) scale(18.4028 15.0465)">
|
||||||
|
<stop stop-color="#E2DBFF"/>
|
||||||
|
<stop offset="1" stop-color="#6D4AFF"/>
|
||||||
|
</radialGradient>
|
||||||
|
<clipPath id="clip0_9588_57915">
|
||||||
|
<rect width="16" height="16" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
32
dist/raw/win+lin_icon_24x24.svg
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_9588_57912)">
|
||||||
|
<path d="M11.8651 -0.0078125C6.09279 -0.0078125 1.41406 4.67091 1.41406 10.4432V20.9636C1.41406 22.082 2.32072 22.9886 3.43915 22.9886H20.5073C21.5059 22.9886 22.3161 22.1785 22.3161 21.1799V10.4432C22.3161 4.67167 17.6374 -0.0078125 11.8651 -0.0078125ZM17.8808 10.3852L13.3129 14.2334C12.4884 14.9282 11.2825 14.9282 10.458 14.2334L5.89005 10.3852C5.89005 7.11956 8.53767 4.47195 11.8033 4.47195H11.9676C15.2332 4.47195 17.8808 7.11956 17.8808 10.3852Z" fill="#6D4AFF"/>
|
||||||
|
<path d="M11.8651 -0.0078125C6.09279 -0.0078125 1.41406 4.67091 1.41406 10.4432V20.9636C1.41406 22.082 2.32072 22.9886 3.43915 22.9886H20.5073C21.5059 22.9886 22.3161 22.1785 22.3161 21.1799V10.4432C22.3161 4.67167 17.6374 -0.0078125 11.8651 -0.0078125ZM17.8808 10.3852L13.3129 14.2334C12.4884 14.9282 11.2825 14.9282 10.458 14.2334L5.89005 10.3852C5.89005 7.11956 8.53767 4.47195 11.8033 4.47195H11.9676C15.2332 4.47195 17.8808 7.11956 17.8808 10.3852Z" fill="url(#paint0_linear_9588_57912)"/>
|
||||||
|
<g filter="url(#filter0_i_9588_57912)">
|
||||||
|
<path d="M13.3213 14.28C12.8451 14.6656 11.6068 15.2053 10.4638 14.28C9.32078 13.3547 6.94856 11.3091 5.90533 10.4019H5.91095L5.89103 10.3852C5.89103 7.11956 8.53864 4.47195 11.8043 4.47195H11.9686C15.2342 4.47195 17.8818 7.11956 17.8818 10.3852L17.8619 10.4019H17.8798V22.9886H20.5083C21.5069 22.9886 22.3171 22.1785 22.3171 21.1799V10.4432C22.3171 4.67167 17.6383 -0.0078125 11.8661 -0.0078125C6.09377 -0.0078125 1.41504 4.67091 1.41504 10.4432V11.1041L7.8784 16.6613C8.35466 17.1149 9.60653 17.7499 10.804 16.6613C12.0014 15.5727 12.9812 14.6202 13.3213 14.28Z" fill="url(#paint1_radial_9588_57912)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_i_9588_57912" x="1.07791" y="-0.0078125" width="21.2395" height="23.9367" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dx="-0.337129" dy="0.940591"/>
|
||||||
|
<feGaussianBlur stdDeviation="2.52847"/>
|
||||||
|
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0.462745 0 0 0 0 0.337255 0 0 0 0 1 0 0 0 0.24 0"/>
|
||||||
|
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_9588_57912"/>
|
||||||
|
</filter>
|
||||||
|
<linearGradient id="paint0_linear_9588_57912" x1="2.16247" y1="25.6421" x2="5.29216" y2="16.7973" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#28B0E8"/>
|
||||||
|
<stop offset="1" stop-color="#C5B7FF" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient id="paint1_radial_9588_57912" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(21.0847 24.8937) rotate(-138.034) scale(26.454 21.6293)">
|
||||||
|
<stop stop-color="#E2DBFF"/>
|
||||||
|
<stop offset="1" stop-color="#6D4AFF"/>
|
||||||
|
</radialGradient>
|
||||||
|
<clipPath id="clip0_9588_57912">
|
||||||
|
<rect width="24" height="24" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
BIN
dist/raw/win+lin_icon_256x256.png
vendored
Normal file
|
After Width: | Height: | Size: 27 KiB |
32
dist/raw/win+lin_icon_256x256.svg
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_9588_57903)">
|
||||||
|
<path d="M127.416 -0.0898438C63.1423 -0.0898438 11.0449 51.9864 11.0449 116.234V233.331C11.0449 245.779 21.1405 255.871 33.5942 255.871H223.647C234.767 255.871 243.788 246.853 243.788 235.738V116.234C243.788 51.9948 191.691 -0.0898438 127.416 -0.0898438ZM194.401 115.589L143.537 158.421C134.357 166.155 120.929 166.155 111.749 158.421L60.8849 115.589C60.8849 79.2409 90.3659 49.7718 126.728 49.7718H128.558C164.92 49.7718 194.401 79.2409 194.401 115.589Z" fill="#6D4AFF"/>
|
||||||
|
<path d="M127.416 -0.0898438C63.1423 -0.0898438 11.0449 51.9864 11.0449 116.234V233.331C11.0449 245.779 21.1405 255.871 33.5942 255.871H223.647C234.767 255.871 243.788 246.853 243.788 235.738V116.234C243.788 51.9948 191.691 -0.0898438 127.416 -0.0898438ZM194.401 115.589L143.537 158.421C134.357 166.155 120.929 166.155 111.749 158.421L60.8849 115.589C60.8849 79.2409 90.3659 49.7718 126.728 49.7718H128.558C164.92 49.7718 194.401 79.2409 194.401 115.589Z" fill="url(#paint0_linear_9588_57903)"/>
|
||||||
|
<g filter="url(#filter0_i_9588_57903)">
|
||||||
|
<path d="M143.572 158.939C138.271 163.23 124.489 169.238 111.766 158.939C99.0439 148.64 72.6401 125.871 61.0285 115.774H61.0868L60.8676 115.59C60.8676 79.2418 90.3367 49.7728 126.684 49.7728H128.513C164.861 49.7728 194.33 79.2418 194.33 115.59L194.111 115.774H194.31V255.872H223.564C234.679 255.872 243.697 246.854 243.697 235.739V116.235C243.697 51.9958 191.62 -0.0888672 127.372 -0.0888672C63.1241 -0.0888672 11.0479 51.9874 11.0479 116.235V123.587L82.9896 185.444C88.2906 190.492 102.224 197.56 115.553 185.444C128.881 173.327 139.786 162.726 143.572 158.939Z" fill="url(#paint1_radial_9588_57903)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_i_9588_57903" x="7.29545" y="-0.0888672" width="236.401" height="266.43" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dx="-3.7524" dy="10.4692"/>
|
||||||
|
<feGaussianBlur stdDeviation="28.143"/>
|
||||||
|
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0.462745 0 0 0 0 0.337255 0 0 0 0 1 0 0 0 0.24 0"/>
|
||||||
|
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_9588_57903"/>
|
||||||
|
</filter>
|
||||||
|
<linearGradient id="paint0_linear_9588_57903" x1="19.3784" y1="285.405" x2="54.2022" y2="186.949" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#28B0E8"/>
|
||||||
|
<stop offset="1" stop-color="#C5B7FF" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient id="paint1_radial_9588_57903" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(229.979 277.075) rotate(-138.034) scale(294.445 240.743)">
|
||||||
|
<stop stop-color="#E2DBFF"/>
|
||||||
|
<stop offset="1" stop-color="#6D4AFF"/>
|
||||||
|
</radialGradient>
|
||||||
|
<clipPath id="clip0_9588_57903">
|
||||||
|
<rect width="256" height="256" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
32
dist/raw/win+lin_icon_32x32.svg
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_9588_57909)">
|
||||||
|
<path d="M15.9709 -0.0107422C8.19088 -0.0107422 1.88477 6.29537 1.88477 14.0754V28.255C1.88477 29.7625 3.10678 30.9845 4.61423 30.9845H27.6191C28.9651 30.9845 30.0571 29.8925 30.0571 28.5465V14.0754C30.0571 6.29638 23.751 -0.0107422 15.9709 -0.0107422ZM24.0791 13.9972L17.9223 19.1839C16.811 20.1205 15.1857 20.1205 14.0744 19.1839L7.91762 13.9972C7.91762 9.59571 11.4861 6.02719 15.8876 6.02719H16.1091C20.5105 6.02719 24.0791 9.59571 24.0791 13.9972Z" fill="#6D4AFF"/>
|
||||||
|
<path d="M15.9709 -0.0107422C8.19088 -0.0107422 1.88477 6.29537 1.88477 14.0754V28.255C1.88477 29.7625 3.10678 30.9845 4.61423 30.9845H27.6191C28.9651 30.9845 30.0571 29.8925 30.0571 28.5465V14.0754C30.0571 6.29638 23.751 -0.0107422 15.9709 -0.0107422ZM24.0791 13.9972L17.9223 19.1839C16.811 20.1205 15.1857 20.1205 14.0744 19.1839L7.91762 13.9972C7.91762 9.59571 11.4861 6.02719 15.8876 6.02719H16.1091C20.5105 6.02719 24.0791 9.59571 24.0791 13.9972Z" fill="url(#paint0_linear_9588_57909)"/>
|
||||||
|
<g filter="url(#filter0_i_9588_57909)">
|
||||||
|
<path d="M17.9322 19.2467C17.2903 19.7663 15.6213 20.4938 14.0807 19.2467C12.5401 17.9996 9.34279 15.2424 7.9367 14.0197H7.94435L7.91762 13.9972C7.91762 9.59571 11.4861 6.02719 15.8876 6.02719H16.1091C20.5105 6.02719 24.0791 9.59571 24.0791 13.9972L24.0523 14.0197H24.0762V30.9845H27.6191C28.9651 30.9845 30.0571 29.8925 30.0571 28.5465V14.0754C30.0571 6.29638 23.751 -0.0107422 15.9709 -0.0107422C8.19088 -0.0107422 1.88477 6.29537 1.88477 14.0754V14.9662L10.596 22.4563C11.238 23.0676 12.9253 23.9235 14.5392 22.4563C16.1532 20.989 17.4737 19.7052 17.9322 19.2467Z" fill="url(#paint1_radial_9588_57909)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_i_9588_57909" x="1.43037" y="-0.0107422" width="28.6263" height="32.2629" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dx="-0.454392" dy="1.26775"/>
|
||||||
|
<feGaussianBlur stdDeviation="3.40794"/>
|
||||||
|
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0.462745 0 0 0 0 0.337255 0 0 0 0 1 0 0 0 0.24 0"/>
|
||||||
|
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_9588_57909"/>
|
||||||
|
</filter>
|
||||||
|
<linearGradient id="paint0_linear_9588_57909" x1="2.89348" y1="34.5608" x2="7.11177" y2="22.6396" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#28B0E8"/>
|
||||||
|
<stop offset="1" stop-color="#C5B7FF" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient id="paint1_radial_9588_57909" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(28.396 33.5521) rotate(-138.034) scale(35.6554 29.1525)">
|
||||||
|
<stop stop-color="#E2DBFF"/>
|
||||||
|
<stop offset="1" stop-color="#6D4AFF"/>
|
||||||
|
</radialGradient>
|
||||||
|
<clipPath id="clip0_9588_57909">
|
||||||
|
<rect width="32" height="32" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
32
dist/raw/win+lin_icon_48x48.svg
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_9588_57906)">
|
||||||
|
<path d="M23.8921 -0.0166016C11.9792 -0.0166016 2.32324 9.63938 2.32324 21.5523V43.2642C2.32324 45.5724 4.1944 47.4436 6.50263 47.4436H41.728C43.7889 47.4436 45.461 45.7715 45.461 43.7106V21.5523C45.461 9.64093 35.805 -0.0166016 23.8921 -0.0166016ZM36.3074 21.4325L26.8801 29.3744C25.1784 30.8085 22.6898 30.8085 20.9882 29.3744L11.5608 21.4325C11.5608 14.6929 17.025 9.22875 23.7646 9.22875H24.1036C30.8432 9.22875 36.3074 14.6929 36.3074 21.4325Z" fill="#6D4AFF"/>
|
||||||
|
<path d="M23.8921 -0.0166016C11.9792 -0.0166016 2.32324 9.63938 2.32324 21.5523V43.2642C2.32324 45.5724 4.1944 47.4436 6.50263 47.4436H41.728C43.7889 47.4436 45.461 45.7715 45.461 43.7106V21.5523C45.461 9.64093 35.805 -0.0166016 23.8921 -0.0166016ZM36.3074 21.4325L26.8801 29.3744C25.1784 30.8085 22.6898 30.8085 20.9882 29.3744L11.5608 21.4325C11.5608 14.6929 17.025 9.22875 23.7646 9.22875H24.1036C30.8432 9.22875 36.3074 14.6929 36.3074 21.4325Z" fill="url(#paint0_linear_9588_57906)"/>
|
||||||
|
<g filter="url(#filter0_i_9588_57906)">
|
||||||
|
<path d="M26.8954 29.4706C25.9125 30.2663 23.3569 31.3803 20.998 29.4706C18.639 27.561 13.7432 23.3392 11.5902 21.467H11.6017L11.5608 21.4325C11.5608 14.6929 17.025 9.22875 23.7646 9.22875H24.1036C30.8432 9.22875 36.3074 14.6929 36.3074 21.4325L36.2665 21.467H36.3032V47.4436H41.728C43.7889 47.4436 45.461 45.7715 45.461 43.7106V21.5523C45.461 9.64093 35.805 -0.0166016 23.8921 -0.0166016C11.9792 -0.0166016 2.32324 9.63938 2.32324 21.5523V22.9161L15.6622 34.3851C16.6451 35.3212 19.2287 36.6318 21.7 34.3851C24.1713 32.1385 26.1933 30.1727 26.8954 29.4706Z" fill="url(#paint1_radial_9588_57906)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_i_9588_57906" x="1.62747" y="-0.0166016" width="43.8335" height="49.4012" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dx="-0.69577" dy="1.9412"/>
|
||||||
|
<feGaussianBlur stdDeviation="5.21827"/>
|
||||||
|
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0.462745 0 0 0 0 0.337255 0 0 0 0 1 0 0 0 0.24 0"/>
|
||||||
|
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_9588_57906"/>
|
||||||
|
</filter>
|
||||||
|
<linearGradient id="paint0_linear_9588_57906" x1="3.8678" y1="52.9198" x2="10.3269" y2="34.6659" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#28B0E8"/>
|
||||||
|
<stop offset="1" stop-color="#C5B7FF" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient id="paint1_radial_9588_57906" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(42.9175 51.3752) rotate(-138.034) scale(54.5959 44.6386)">
|
||||||
|
<stop stop-color="#E2DBFF"/>
|
||||||
|
<stop offset="1" stop-color="#6D4AFF"/>
|
||||||
|
</radialGradient>
|
||||||
|
<clipPath id="clip0_9588_57906">
|
||||||
|
<rect width="48" height="48" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.0 KiB |
@ -51,7 +51,7 @@ PMAPI directly.
|
|||||||
graph TD
|
graph TD
|
||||||
|
|
||||||
C["Client (e.g. Thunderbird)"]
|
C["Client (e.g. Thunderbird)"]
|
||||||
PM[ProtonMail Server]
|
PM[Proton Mail Server]
|
||||||
|
|
||||||
subgraph "Bridge app"
|
subgraph "Bridge app"
|
||||||
subgraph "Bridge core"
|
subgraph "Bridge core"
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
# Encryption
|
# Encryption
|
||||||
|
|
||||||
Encryption is done in PMAPI, bridge utils and bridge itself. The best would be to keep encryption
|
Encryption is done in PMAPI, bridge utils and bridge itself. The best would be to keep encryption
|
||||||
in PMAPI and bridge utils (in pacakge such as messages). All packages are using our high-level
|
in PMAPI and bridge utils (in package such as messages). All packages are using our high-level
|
||||||
GopenPGP library on top of openpgp.
|
GopenPGP library on top of OpenPGP.
|
||||||
|
|
||||||
## `gopenpgp.KeyRing`
|
## `gopenpgp.KeyRing`
|
||||||
|
|
||||||
We use one `KeyRing` per address. Our usage then contains all keys for specific address. Primary
|
We use one `KeyRing` per address. Our usage then contains all keys for specific address. Primary
|
||||||
key is always on the first position, then there old ones to be able to decrypt last e-mail.
|
key is always on the first position, then there old ones to be able to decrypt last e-mail.
|
||||||
Openpgp encrypts given message with all available keys, so we need to first get first (primary)
|
OpenPGP encrypts given message with all available keys, so we need to first get first (primary)
|
||||||
key for encryption to have message encrypted only once with primary key.
|
key for encryption to have message encrypted only once with primary key.
|
||||||
|
|||||||
@ -1,135 +0,0 @@
|
|||||||
# Import-Export app
|
|
||||||
|
|
||||||
## Main blocks
|
|
||||||
|
|
||||||
This is basic overview of the main Import-Export blocks.
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
graph LR
|
|
||||||
S[ProtonMail server]
|
|
||||||
U[User]
|
|
||||||
|
|
||||||
subgraph "Import-Export app"
|
|
||||||
Users
|
|
||||||
Frontend["Qt / CLI"]
|
|
||||||
ImportExport
|
|
||||||
Transfer
|
|
||||||
|
|
||||||
Frontend --> ImportExport
|
|
||||||
Frontend --> Transfer
|
|
||||||
ImportExport --> Users
|
|
||||||
ImportExport --> Transfer
|
|
||||||
end
|
|
||||||
|
|
||||||
EML --> Transfer
|
|
||||||
MBOX --> Transfer
|
|
||||||
IMAP --> Transfer
|
|
||||||
S --> Transfer
|
|
||||||
|
|
||||||
Transfer --> EML
|
|
||||||
Transfer --> MBOX
|
|
||||||
Transfer --> S
|
|
||||||
|
|
||||||
U --> Frontend
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code structure
|
|
||||||
|
|
||||||
More detailed graph of main types used in Import-Export app and connection between them.
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
graph TD
|
|
||||||
PM[ProtonMail Server]
|
|
||||||
EML[EML]
|
|
||||||
MBOX[MBOX]
|
|
||||||
IMAP[IMAP]
|
|
||||||
|
|
||||||
subgraph "Import-Export app"
|
|
||||||
subgraph "pkg users"
|
|
||||||
subgraph "pkg credentials"
|
|
||||||
CredStore[Store]
|
|
||||||
Creds[Credentials]
|
|
||||||
|
|
||||||
CredStore --> Creds
|
|
||||||
end
|
|
||||||
|
|
||||||
US[Users]
|
|
||||||
U[User]
|
|
||||||
|
|
||||||
US --> U
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph "pkg frontend"
|
|
||||||
CLI
|
|
||||||
Qt
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph "pkg importExport"
|
|
||||||
IE[ImportExport]
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph "pkg transfer"
|
|
||||||
Transfer
|
|
||||||
Rules
|
|
||||||
Progress
|
|
||||||
|
|
||||||
Provider
|
|
||||||
LocalProvider
|
|
||||||
EMLProvider
|
|
||||||
MBOXProvider
|
|
||||||
IMAPProvider
|
|
||||||
PMAPIProvider
|
|
||||||
|
|
||||||
Mailbox
|
|
||||||
Message
|
|
||||||
|
|
||||||
Transfer --> |source|Provider
|
|
||||||
Transfer --> |target|Provider
|
|
||||||
Transfer --> Rules
|
|
||||||
Transfer --> Progress
|
|
||||||
|
|
||||||
Provider --> LocalProvider
|
|
||||||
Provider --> EMLProvider
|
|
||||||
Provider --> MBOXProvider
|
|
||||||
Provider --> IMAPProvider
|
|
||||||
Provider --> PMAPIProvider
|
|
||||||
|
|
||||||
LocalProvider --> EMLProvider
|
|
||||||
LocalProvider --> MBOXProvider
|
|
||||||
|
|
||||||
Provider --> Mailbox
|
|
||||||
Provider --> Message
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph PMAPI
|
|
||||||
APIM[ClientManager]
|
|
||||||
APIC[Client]
|
|
||||||
|
|
||||||
APIM --> APIC
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
CLI --> IE
|
|
||||||
CLI --> Transfer
|
|
||||||
CLI --> Progress
|
|
||||||
Qt --> IE
|
|
||||||
Qt --> Transfer
|
|
||||||
Qt --> Progress
|
|
||||||
|
|
||||||
U --> CredStore
|
|
||||||
U --> Creds
|
|
||||||
|
|
||||||
US --> APIM
|
|
||||||
U --> APIM
|
|
||||||
|
|
||||||
PMAPIProvider --> APIM
|
|
||||||
EMLProvider --> EML
|
|
||||||
MBOXProvider --> MBOX
|
|
||||||
IMAPProvider --> IMAP
|
|
||||||
|
|
||||||
IE --> US
|
|
||||||
IE --> Transfer
|
|
||||||
|
|
||||||
APIC --> PM
|
|
||||||
```
|
|
||||||
@ -1,14 +1,9 @@
|
|||||||
# Documentation
|
# Bridge Documentation
|
||||||
|
|
||||||
Documentation pages in order to read for a novice:
|
Documentation pages in order to read for a novice:
|
||||||
|
|
||||||
## Bridge
|
|
||||||
|
|
||||||
* [Bridge code](bridge.md)
|
* [Bridge code](bridge.md)
|
||||||
* [Internal Bridge database](database.md)
|
* [Internal Bridge database](database.md)
|
||||||
* [Communication between Bridge, Client and Server](communication.md)
|
* [Communication between Bridge, Client and Server](communication.md)
|
||||||
* [Encryption](encryption.md)
|
* [Encryption](encryption.md)
|
||||||
|
|
||||||
## Import-Export app
|
|
||||||
|
|
||||||
* [Import-Export code](importexport.md)
|
|
||||||
|
|||||||
103
doc/updates.md
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
# Update mechanism of Bridge
|
||||||
|
|
||||||
|
There are mulitple options how to change version of application:
|
||||||
|
* Automatic in-app update
|
||||||
|
* Manual in-app update
|
||||||
|
* Manual install
|
||||||
|
|
||||||
|
In-app update ends with restarting bridge into new version. Automatic in-app
|
||||||
|
update is downloading, verifying and installing the new version immediatelly
|
||||||
|
without user confirmation. For manual in-app update user needs to confirm first.
|
||||||
|
Update is done from special update file published on website.
|
||||||
|
|
||||||
|
The manual installation requires user to download, verify and install manually
|
||||||
|
using installer for given OS.
|
||||||
|
|
||||||
|
The bridge is installed and executed differently for given OS:
|
||||||
|
|
||||||
|
* Windows and Linux apps are using launcher mechanism:
|
||||||
|
* There is system protected installation path which is created on first
|
||||||
|
install. It contains bridge exe and launcher exe. When users starts
|
||||||
|
bridge the launcher is executed first. It will check update path compare
|
||||||
|
version with installed one. The newer version then is then executed.
|
||||||
|
* Update mechanism means to replace files in update folder which is located
|
||||||
|
in user space.
|
||||||
|
|
||||||
|
* macOS app does not use launcher
|
||||||
|
* No launcher, only one executable
|
||||||
|
* In-App udpate replaces the bridge files in installation path directly
|
||||||
|
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
subgraph Frontend
|
||||||
|
U[User requests<br>version check]
|
||||||
|
ManIns((Notify user about<br>manual install<br>is needed))
|
||||||
|
R((Notify user<br>about restart))
|
||||||
|
ManUp((Notify user about<br>manual update))
|
||||||
|
NF((Notify user about<br>force update))
|
||||||
|
|
||||||
|
ManUp -->|Install| InstFront[Install]
|
||||||
|
InstFront -->|Ok| R
|
||||||
|
InstFront -->|Error| ManIns
|
||||||
|
|
||||||
|
U --> CheckFront[Check online]
|
||||||
|
CheckFront -->|Ok| IAFront{Is new version<br>and applicable?}
|
||||||
|
CheckFront -->|Error| ManIns
|
||||||
|
|
||||||
|
IAFront -->|No| Latest((Notify user<br>has latest version))
|
||||||
|
IAFront -->|Yes| CanInstall{Can update?}
|
||||||
|
CanInstall -->|No| ManIns
|
||||||
|
CanInstall -->|Yes| NotifOrInstall{Is automatic<br>update enabled?}
|
||||||
|
NotifOrInstall -->|Manual| ManUp
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
subgraph Backend
|
||||||
|
W[Wait for next check]
|
||||||
|
|
||||||
|
W --> Check[Check online]
|
||||||
|
|
||||||
|
Check --> NV{Has new<br>version?}
|
||||||
|
Check -->|Error| W
|
||||||
|
NV -->|No new version| W
|
||||||
|
IA{Is install<br>applicable?}
|
||||||
|
NV -->|New version<br>available| IA
|
||||||
|
IA -->|Local rollout<br>not enough| W
|
||||||
|
IA -->|Yes| AU{Is automatic\nupdate enabled?}
|
||||||
|
|
||||||
|
AU -->|Yes| CanUp{Can update?}
|
||||||
|
CanUp -->|No| ManIns
|
||||||
|
|
||||||
|
CanUp -->|Yes| Ins[Install]
|
||||||
|
Ins -->|Error| ManIns
|
||||||
|
Ins -->|Ok| R
|
||||||
|
|
||||||
|
AU -->|No| ManUp
|
||||||
|
ManUp -->|Ignore| W
|
||||||
|
|
||||||
|
|
||||||
|
F[Force update]
|
||||||
|
F --> NF
|
||||||
|
end
|
||||||
|
|
||||||
|
ManIns --> Web[Open web page]
|
||||||
|
NF --> Web
|
||||||
|
ManUp --> Web
|
||||||
|
R --> Re[Restart]
|
||||||
|
NF --> Q[Quit bridge]
|
||||||
|
NotifOrInstall -->|Automatic| W
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
The non-trivial is to combine the update with setting change:
|
||||||
|
* turn off/on automatic in-app updates
|
||||||
|
* change from stable to beta or back
|
||||||
|
|
||||||
|
_TODO fill flow chart details_
|
||||||
|
|
||||||
|
|
||||||
|
We are not support downgrade functionality. Only some circumstances can lead to
|
||||||
|
downgrading the app version.
|
||||||
|
|
||||||
|
_TODO fill flow chart details_
|
||||||
1
extern/vcpkg
vendored
Submodule
162
go.mod
@ -1,82 +1,108 @@
|
|||||||
module github.com/ProtonMail/proton-bridge
|
module github.com/ProtonMail/proton-bridge/v2
|
||||||
|
|
||||||
go 1.13
|
go 1.18
|
||||||
|
|
||||||
// These dependencies are `replace`d below, so the version numbers should be ignored.
|
|
||||||
// They are in a separate require block to highlight this.
|
|
||||||
require (
|
require (
|
||||||
github.com/docker/docker-credential-helpers v0.6.3
|
github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557
|
||||||
github.com/emersion/go-smtp v0.0.0-20180712174835-db5eec195e67
|
github.com/Masterminds/semver/v3 v3.1.1
|
||||||
github.com/jameskeane/bcrypt v0.0.0-20170924085257-7509ea014998
|
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895
|
||||||
|
github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde
|
||||||
|
github.com/ProtonMail/go-rfc5322 v0.11.0
|
||||||
|
github.com/ProtonMail/go-srp v0.0.5
|
||||||
|
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5
|
||||||
|
github.com/ProtonMail/gopenpgp/v2 v2.4.10
|
||||||
|
github.com/PuerkitoBio/goquery v1.8.0
|
||||||
|
github.com/abiosoft/ishell v2.0.0+incompatible
|
||||||
|
github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37
|
||||||
|
github.com/bradenaw/juniper v0.8.0
|
||||||
|
github.com/cucumber/godog v0.12.5
|
||||||
|
github.com/cucumber/messages-go/v16 v16.0.1
|
||||||
|
github.com/docker/docker-credential-helpers v0.6.4
|
||||||
|
github.com/elastic/go-sysinfo v1.8.1
|
||||||
|
github.com/emersion/go-imap v1.2.1
|
||||||
|
github.com/emersion/go-imap-appendlimit v0.0.0-20210907172056-e3baed77bbe4
|
||||||
|
github.com/emersion/go-imap-move v0.0.0-20210907172020-fe4558f9c872
|
||||||
|
github.com/emersion/go-imap-quota v0.0.0-20210203125329-619074823f3c
|
||||||
|
github.com/emersion/go-imap-unselect v0.0.0-20210907172115-4c2c4843bf69
|
||||||
|
github.com/emersion/go-message v0.16.0
|
||||||
|
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead
|
||||||
|
github.com/emersion/go-smtp v0.15.0
|
||||||
|
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594
|
||||||
|
github.com/fatih/color v1.13.0
|
||||||
|
github.com/getsentry/sentry-go v0.13.0
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0
|
||||||
|
github.com/godbus/dbus v4.1.0+incompatible
|
||||||
|
github.com/golang/mock v1.6.0
|
||||||
|
github.com/google/go-cmp v0.5.9
|
||||||
|
github.com/google/uuid v1.3.0
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1
|
||||||
|
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba
|
||||||
|
github.com/keybase/go-keychain v0.0.0-20220610143837-c2ce06069005
|
||||||
|
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||||
|
github.com/miekg/dns v1.1.50
|
||||||
|
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/ricochet2200/go-disk-usage/du v0.0.0-20210707232629-ac9918953285
|
||||||
|
github.com/sirupsen/logrus v1.9.0
|
||||||
|
github.com/stretchr/testify v1.8.0
|
||||||
|
github.com/urfave/cli/v2 v2.16.3
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5
|
||||||
|
go.etcd.io/bbolt v1.3.6
|
||||||
|
golang.org/x/exp v0.0.0-20220921164117-439092de6870
|
||||||
|
golang.org/x/net v0.1.0
|
||||||
|
golang.org/x/sys v0.1.0
|
||||||
|
golang.org/x/text v0.4.0
|
||||||
|
google.golang.org/grpc v1.49.0
|
||||||
|
google.golang.org/protobuf v1.28.1
|
||||||
|
howett.net/plist v1.0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/0xAX/notificator v0.0.0-20191016112426-3962a5ea8da1
|
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf // indirect
|
||||||
github.com/Masterminds/semver/v3 v3.1.0
|
github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f // indirect
|
||||||
github.com/ProtonMail/go-appdir v1.1.0
|
|
||||||
github.com/ProtonMail/go-apple-mobileconfig v0.0.0-20160701194735-7ea9927a11f6
|
|
||||||
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a
|
|
||||||
github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde
|
|
||||||
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5
|
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.0.1
|
|
||||||
github.com/PuerkitoBio/goquery v1.5.1
|
|
||||||
github.com/abiosoft/ishell v2.0.0+incompatible
|
|
||||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
|
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
|
||||||
github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc
|
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||||
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 // indirect
|
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
|
||||||
github.com/chzyer/logex v1.1.10 // indirect
|
github.com/chzyer/test v1.0.0 // indirect
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
|
github.com/cloudflare/circl v1.2.0 // indirect
|
||||||
github.com/cucumber/godog v0.8.1
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
github.com/emersion/go-imap v1.0.6-0.20200708083111-011063d6c9df
|
github.com/cronokirby/saferith v0.33.0 // indirect
|
||||||
github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a
|
github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect
|
||||||
github.com/emersion/go-imap-idle v0.0.0-20200601154248-f05f54664cc4
|
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||||
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/emersion/go-imap-quota v0.0.0-20200423100218-dcfd1b7d2b41
|
github.com/elastic/go-windows v1.0.1 // indirect
|
||||||
github.com/emersion/go-imap-specialuse v0.0.0-20200722111535-598ff00e4075
|
github.com/emersion/go-vcard v0.0.0-20220507122617-d4056df0ec4a // indirect
|
||||||
github.com/emersion/go-imap-unselect v0.0.0-20171113212723-b985794e5f26
|
|
||||||
github.com/emersion/go-mbox v1.0.0
|
|
||||||
github.com/emersion/go-message v0.12.1-0.20200903165315-e1abe21f389a
|
|
||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
|
|
||||||
github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe
|
|
||||||
github.com/emersion/go-vcard v0.0.0-20190105225839-8856043f13c5 // indirect
|
|
||||||
github.com/fatih/color v1.9.0
|
|
||||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
|
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
|
||||||
github.com/getsentry/raven-go v0.2.0
|
github.com/gofrs/uuid v4.3.0+incompatible // indirect
|
||||||
github.com/go-resty/resty/v2 v2.3.0
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/golang/mock v1.4.4
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/google/go-cmp v0.5.1
|
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||||
github.com/google/uuid v1.1.1
|
github.com/hashicorp/go-memdb v1.3.3 // indirect
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect
|
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.0
|
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect
|
||||||
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
github.com/keybase/go-keychain v0.0.0-20200502122510-cda31fe0c86d
|
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/miekg/dns v1.1.30
|
github.com/prometheus/procfs v0.8.0 // indirect
|
||||||
github.com/myesui/uuid v1.0.0 // indirect
|
github.com/rivo/uniseg v0.4.2 // indirect
|
||||||
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/olekukonko/tablewriter v0.0.4 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
|
||||||
github.com/sirupsen/logrus v1.6.0
|
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||||
github.com/stretchr/testify v1.6.1
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
github.com/twinj/uuid v1.0.0 // indirect
|
golang.org/x/crypto v0.1.0 // indirect
|
||||||
github.com/urfave/cli v1.22.4
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||||
go.etcd.io/bbolt v1.3.5
|
golang.org/x/tools v0.1.12 // indirect
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381
|
google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 // indirect
|
||||||
golang.org/x/text v0.3.3
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gopkg.in/stretchr/testify.v1 v1.2.2 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
replace (
|
replace (
|
||||||
github.com/docker/docker-credential-helpers => github.com/ProtonMail/docker-credential-helpers v1.1.0
|
github.com/docker/docker-credential-helpers => github.com/ProtonMail/docker-credential-helpers v1.1.0
|
||||||
github.com/emersion/go-imap => github.com/ProtonMail/go-imap v0.0.0-20200828124548-d04b0dc1f399
|
github.com/emersion/go-imap => github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac
|
||||||
github.com/emersion/go-mbox => github.com/ProtonMail/mbox v0.0.0-20200918064939-909a18c9af45
|
github.com/emersion/go-message => github.com/ProtonMail/go-message v0.0.0-20210611055058-fabeff2ec753
|
||||||
github.com/emersion/go-smtp => github.com/ProtonMail/go-smtp v0.0.0-20181206232543-8261df20d309
|
github.com/keybase/go-keychain => github.com/cuthix/go-keychain v0.0.0-20220405075754-31e7cee908fe
|
||||||
github.com/jameskeane/bcrypt => github.com/ProtonMail/bcrypt v0.0.0-20170924085257-7509ea014998
|
|
||||||
golang.org/x/crypto => github.com/ProtonMail/crypto v0.0.0-20200416114516-1fa7f403fb9c
|
|
||||||
)
|
)
|
||||||
|
|||||||
656
go.sum
@ -1,221 +1,561 @@
|
|||||||
github.com/0xAX/notificator v0.0.0-20191016112426-3962a5ea8da1 h1:j9HaafapDbPbGRDku6e/HRs6KBMcKHiWcm1/9Sbxnl4=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/0xAX/notificator v0.0.0-20191016112426-3962a5ea8da1/go.mod h1:NtXa9WwQsukMHZpjNakTTz0LArxvGYdPA9CjIcUSZ6s=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557 h1:l6surSnJ3RP4qA1qmKJ+hQn3UjytosdoG27WGjrDlVs=
|
||||||
|
github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557/go.mod h1:sTrmvD/TxuypdOERsDOS7SndZg0rzzcCi1b6wQMXUYM=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk=
|
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||||
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
github.com/ProtonMail/bcrypt v0.0.0-20170924085257-7509ea014998 h1:YT2uVwQiRQZxCaaahwfcgTq2j3j66w00n/27gb/zubs=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/ProtonMail/bcrypt v0.0.0-20170924085257-7509ea014998/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I=
|
github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I=
|
||||||
github.com/ProtonMail/crypto v0.0.0-20200416114516-1fa7f403fb9c h1:DAvlgde2Stu18slmjwikiMPs/CKPV35wSvmJS34z0FU=
|
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs69zUkSzubzjBbL+cmOXgnmt9Fyd9ug=
|
||||||
github.com/ProtonMail/crypto v0.0.0-20200416114516-1fa7f403fb9c/go.mod h1:Pxr7w4gA2ikI4sWyYwEffm+oew1WAJHzG1SiDpQMkrI=
|
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo=
|
||||||
github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk=
|
github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk=
|
||||||
github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g=
|
github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g=
|
||||||
github.com/ProtonMail/go-appdir v1.1.0 h1:9hdNDlU9kTqRKVNzmoqah8qqrj5QZyLByQdwQNlFWig=
|
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4=
|
||||||
github.com/ProtonMail/go-appdir v1.1.0/go.mod h1:3d8Y9F5mbEUjrYbcJ3rcDxcWbqbttF+011nVZmdRdzc=
|
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4=
|
||||||
github.com/ProtonMail/go-apple-mobileconfig v0.0.0-20160701194735-7ea9927a11f6 h1:YsSJ/mvZFYydQm/hRrt8R8UtgETixN2y3LK98f5LT60=
|
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||||
github.com/ProtonMail/go-apple-mobileconfig v0.0.0-20160701194735-7ea9927a11f6/go.mod h1:EtDfBMIDWmVe4viZCuBTEfe3OIIo0ghbpOaAZVO+hVg=
|
github.com/ProtonMail/go-crypto v0.0.0-20220822140716-1678d6eb0cbe/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
|
||||||
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a h1:fXK2KsfnkBV9Nh+9SKzHchYjuE9s0vI20JG1mbtEAcc=
|
github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 h1:NsReiLpErIPzRrnogAXYwSoU7txA977LjDGrbkewJbg=
|
||||||
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4=
|
github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
|
||||||
github.com/ProtonMail/go-imap v0.0.0-20200828124548-d04b0dc1f399 h1:wBo/Xgb/Dn2loU47D+PJaOoIZ67i3AqYp51gLn8YE5U=
|
github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac h1:2xU3QncAiS/W3UlWZTkbNKW5WkLzk6Egl1T0xX+sbjs=
|
||||||
github.com/ProtonMail/go-imap v0.0.0-20200828124548-d04b0dc1f399/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU=
|
github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU=
|
||||||
github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde h1:5koQozTDELymYOyFbQ/VSubexAEXzDR8qGM5mO8GRdw=
|
github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde h1:5koQozTDELymYOyFbQ/VSubexAEXzDR8qGM5mO8GRdw=
|
||||||
github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde/go.mod h1:795VPXcRUIQ9JyMNHP4el582VokQfippgjkQP3Gk0r0=
|
github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde/go.mod h1:795VPXcRUIQ9JyMNHP4el582VokQfippgjkQP3Gk0r0=
|
||||||
github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a h1:W6RrgN/sTxg1msqzFFb+G80MFmpjMw61IU+slm+wln4=
|
github.com/ProtonMail/go-message v0.0.0-20210611055058-fabeff2ec753 h1:I8IsYA297x0QLU80G5I6aLYUu3JYNSpo8j5fkXtFDW0=
|
||||||
github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4=
|
github.com/ProtonMail/go-message v0.0.0-20210611055058-fabeff2ec753/go.mod h1:NBAn21zgCJ/52WLDyed18YvYFm5tEoeDauubFqLokM4=
|
||||||
github.com/ProtonMail/go-smtp v0.0.0-20181206232543-8261df20d309 h1:2pzfKjhBjSnw3BgmfTYRFQr1rFGxhfhUY0KKkg+RYxE=
|
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4=
|
||||||
github.com/ProtonMail/go-smtp v0.0.0-20181206232543-8261df20d309/go.mod h1:6UoBvDAMA/cTBwS3Y7tGpKnY5RH1F1uYHschT6eqAkI=
|
github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f h1:4IWzKjHzZxdrW9k4zl/qCwenOVHDbVDADPPHFLjs0Oc=
|
||||||
|
github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM=
|
||||||
|
github.com/ProtonMail/go-rfc5322 v0.11.0 h1:o5Obrm4DpmQEffvgsVqG6S4BKwC1Wat+hYwjIp2YcCY=
|
||||||
|
github.com/ProtonMail/go-rfc5322 v0.11.0/go.mod h1:6oOKr0jXvpoE6pwTx/HukigQpX2J9WUf6h0auplrFTw=
|
||||||
|
github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgiGg=
|
||||||
|
github.com/ProtonMail/go-srp v0.0.5/go.mod h1:06iYHtLXW8vjLtccWj++x3MKy65sIT8yZd7nrJF49rs=
|
||||||
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5 h1:Uga1DHFN4GUxuDQr0F71tpi8I9HqPIlZodZAI1lR6VQ=
|
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5 h1:Uga1DHFN4GUxuDQr0F71tpi8I9HqPIlZodZAI1lR6VQ=
|
||||||
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5/go.mod h1:oeP9CMN+ajWp5jKp1kue5daJNwMMxLF+ujPaUIoJWlA=
|
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5/go.mod h1:oeP9CMN+ajWp5jKp1kue5daJNwMMxLF+ujPaUIoJWlA=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.0.1 h1:x0uvDhry5WzoHeJO4J3dgMLhG4Z9PeBJ2O+sDOY0LcU=
|
github.com/ProtonMail/gopenpgp/v2 v2.4.10 h1:EYgkxzwmQvsa6kxxkgP1AwzkFqKHscF2UINxaSn6rdI=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.0.1/go.mod h1:wQQCJo7DURO6S9VwH+kSDEYs/B63yZnAEfGlOg8YNBY=
|
github.com/ProtonMail/gopenpgp/v2 v2.4.10/go.mod h1:CTRA7/toc/4DxDy5Du4hPDnIZnJvXSeQ8LsRTOUJoyc=
|
||||||
github.com/ProtonMail/mbox v0.0.0-20200918064939-909a18c9af45 h1:GDh55hDI2sNiirDqEWV8b6EB729u78Qxu3nKF970n6g=
|
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
|
||||||
github.com/ProtonMail/mbox v0.0.0-20200918064939-909a18c9af45/go.mod h1:Yp9IVuuOYLEuMv4yjgDHvhb5mHOcYH6x92Oas3QqEZI=
|
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
|
||||||
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
|
||||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
|
||||||
github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw=
|
github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw=
|
||||||
github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg=
|
github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg=
|
||||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db h1:CjPUSXOiYptLbTdr1RceuZgSFDQ7U15ITERUGrUORx8=
|
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db h1:CjPUSXOiYptLbTdr1RceuZgSFDQ7U15ITERUGrUORx8=
|
||||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530=
|
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530=
|
||||||
github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc h1:mZca0/HZ/XWXP9txkfdl2GH6mUzBqAlyJz3u5Lg8fuA=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc/go.mod h1:qqsTQiwdyqxU05iDCsi0oN3P4nrVxAmn8xCtODDSf/U=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
|
github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37 h1:28uU3TtuvQ6KRndxg9TrC868jBWmSKgh0GTXkACCXmA=
|
||||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37/go.mod h1:6AXRstqK+32jeFmw89QGL2748+dj34Av4xc/I9oo9BY=
|
||||||
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 h1:JLaf/iINcLyjwbtTsCJjc6rtlASgHeIJPrB6QmwURnA=
|
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
|
||||||
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
|
||||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220816024939-bc8df83d7b9d/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/cucumber/godog v0.8.1 h1:lVb+X41I4YDreE+ibZ50bdXmySxgRviYFgKY6Aw4XE8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/cucumber/godog v0.8.1/go.mod h1:vSh3r/lM+psC1BPXvdkSEuNjmXfpVqrMGYAElF6hxnA=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/danieljoos/wincred v1.1.0 h1:3RNcEpBg4IhIChZdFRSdlQt1QjCp1sMAPIrOnm7Yf8g=
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
|
github.com/bradenaw/juniper v0.8.0 h1:sdanLNdJbLjcLj993VYIwUHlUVkLzvgiD/x9O7cvvxk=
|
||||||
|
github.com/bradenaw/juniper v0.8.0/go.mod h1:Z2B7aJlQ7xbfWsnMLROj5t/5FQ94/MkIdKC30J4WvzI=
|
||||||
|
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||||
|
github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||||
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
|
||||||
|
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||||
|
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
||||||
|
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
||||||
|
github.com/cloudflare/circl v1.2.0 h1:NheeISPSUcYftKlfrLuOo4T62FkmD4t4jviLfFFYaec=
|
||||||
|
github.com/cloudflare/circl v1.2.0/go.mod h1:Ch2UgYr6ti2KTtlejELlROl0YIYj7SLjAC8M+INXlMk=
|
||||||
|
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
|
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
|
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/cronokirby/saferith v0.33.0 h1:TgoQlfsD4LIwx71+ChfRcIpjkw+RPOapDEVxa+LhwLo=
|
||||||
|
github.com/cronokirby/saferith v0.33.0/go.mod h1:QKJhjoqUtBsXCAVEjw38mFqoi7DebT7kthcD7UzbnoA=
|
||||||
|
github.com/cucumber/gherkin-go/v19 v19.0.3 h1:mMSKu1077ffLbTJULUfM5HPokgeBcIGboyeNUof1MdE=
|
||||||
|
github.com/cucumber/gherkin-go/v19 v19.0.3/go.mod h1:jY/NP6jUtRSArQQJ5h1FXOUgk5fZK24qtE7vKi776Vw=
|
||||||
|
github.com/cucumber/godog v0.12.5 h1:FZIy6VCfMbmGHts9qd6UjBMT9abctws/pQYO/ZcwOVs=
|
||||||
|
github.com/cucumber/godog v0.12.5/go.mod h1:u6SD7IXC49dLpPN35kal0oYEjsXZWee4pW6Tm9t5pIc=
|
||||||
|
github.com/cucumber/messages-go/v16 v16.0.0/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
|
||||||
|
github.com/cucumber/messages-go/v16 v16.0.1 h1:fvkpwsLgnIm0qugftrw2YwNlio+ABe2Iu94Ap8GMYIY=
|
||||||
|
github.com/cucumber/messages-go/v16 v16.0.1/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
|
||||||
|
github.com/cuthix/go-keychain v0.0.0-20220405075754-31e7cee908fe h1:KRj3wdvA9yE92prNmOjS7x5DOqoyjxqdE30qnrmTasc=
|
||||||
|
github.com/cuthix/go-keychain v0.0.0-20220405075754-31e7cee908fe/go.mod h1:ZoZU1fnBy3mOLWr3Pg+Y2+nTKtu6ypDte2kZg9HvSwY=
|
||||||
github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
|
github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
|
||||||
|
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
|
||||||
|
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a h1:bMdSPm6sssuOFpIaveu3XGAijMS3Tq2S3EqFZmZxidc=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a/go.mod h1:ikgISoP7pRAolqsVP64yMteJa2FIpS6ju88eBT6K1yQ=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
github.com/emersion/go-imap-idle v0.0.0-20200601154248-f05f54664cc4 h1:/JIALzmCduf5o8TWJSiOBzTb9+R0SChwElUrJLlp2po=
|
github.com/elastic/go-sysinfo v1.8.1 h1:4Yhj+HdV6WjbCRgGdZpPJ8lZQlXZLKDAeIkmQ/VRvi4=
|
||||||
github.com/emersion/go-imap-idle v0.0.0-20200601154248-f05f54664cc4/go.mod h1:o14zPKCmEH5WC1vU5SdPoZGgNvQx7zzKSnxPQlobo78=
|
github.com/elastic/go-sysinfo v1.8.1/go.mod h1:JfllUnzoQV/JRYymbH3dO1yggI3mV2oTKSXsDHM+uIM=
|
||||||
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342 h1:5p1t3e1PomYgLWwEwhwEU5kVBwcyAcVrOpexv8AeZx0=
|
github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0=
|
||||||
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342/go.mod h1:QuMaZcKFDVI0yCrnAbPLfbwllz1wtOrZH8/vZ5yzp4w=
|
github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss=
|
||||||
github.com/emersion/go-imap-quota v0.0.0-20200423100218-dcfd1b7d2b41 h1:z5lDGnSURauBEDdNLj3o0+HogVYKQCGeY3Anl/xyRfU=
|
github.com/emersion/go-imap-appendlimit v0.0.0-20210907172056-e3baed77bbe4 h1:U6LL6F1dYqXpVTwEbXhcfU8hgpNvmjB9xeOAiHN695o=
|
||||||
github.com/emersion/go-imap-quota v0.0.0-20200423100218-dcfd1b7d2b41/go.mod h1:iApyhIQBiU4XFyr+3kdJyyGqle82TbQyuP2o+OZHrV0=
|
github.com/emersion/go-imap-appendlimit v0.0.0-20210907172056-e3baed77bbe4/go.mod h1:ikgISoP7pRAolqsVP64yMteJa2FIpS6ju88eBT6K1yQ=
|
||||||
github.com/emersion/go-imap-specialuse v0.0.0-20200722111535-598ff00e4075 h1:z8TiDE4yqtzNeA1yb6ZRcktd+BHlXQbKGugvmDuc488=
|
github.com/emersion/go-imap-move v0.0.0-20210907172020-fe4558f9c872 h1:HGBfonz0q/zq7y3ew+4oy4emHSvk6bkmV0mdDG3E77M=
|
||||||
github.com/emersion/go-imap-specialuse v0.0.0-20200722111535-598ff00e4075/go.mod h1:/nybxhI8kXom8Tw6BrHMl42usALvka6meORflnnYwe4=
|
github.com/emersion/go-imap-move v0.0.0-20210907172020-fe4558f9c872/go.mod h1:QuMaZcKFDVI0yCrnAbPLfbwllz1wtOrZH8/vZ5yzp4w=
|
||||||
github.com/emersion/go-imap-unselect v0.0.0-20171113212723-b985794e5f26 h1:FiSb8+XBQQSkcX3ubr+1tAtlRJBYaFmRZqOAweZ9Wy8=
|
github.com/emersion/go-imap-quota v0.0.0-20210203125329-619074823f3c h1:khcEdu1yFiZjBgi7gGnQiLhpSgghJ0YTnKD0l4EUqqc=
|
||||||
github.com/emersion/go-imap-unselect v0.0.0-20171113212723-b985794e5f26/go.mod h1:+gnnZx3Mg3MnCzZrv0eZdp5puxXQUgGT/6N6L7ShKfM=
|
github.com/emersion/go-imap-quota v0.0.0-20210203125329-619074823f3c/go.mod h1:iApyhIQBiU4XFyr+3kdJyyGqle82TbQyuP2o+OZHrV0=
|
||||||
github.com/emersion/go-mbox v1.0.0 h1:HN6aKbyqmgIfK9fS/gen+NRr2wXLSxZXWfdAIAnzQPc=
|
github.com/emersion/go-imap-unselect v0.0.0-20210907172115-4c2c4843bf69 h1:ltTnRlPdSMMb0a/pg7S31T3g+syYeSS5UVJtiR7ez1Y=
|
||||||
github.com/emersion/go-mbox v1.0.0/go.mod h1:Yp9IVuuOYLEuMv4yjgDHvhb5mHOcYH6x92Oas3QqEZI=
|
github.com/emersion/go-imap-unselect v0.0.0-20210907172115-4c2c4843bf69/go.mod h1:+gnnZx3Mg3MnCzZrv0eZdp5puxXQUgGT/6N6L7ShKfM=
|
||||||
github.com/emersion/go-message v0.11.1/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY=
|
|
||||||
github.com/emersion/go-message v0.12.1-0.20200903165315-e1abe21f389a h1:3C6qIGgPr1qAT0ikRD5NbyKpME/iHCDeXhpv/JJsFsE=
|
|
||||||
github.com/emersion/go-message v0.12.1-0.20200903165315-e1abe21f389a/go.mod h1:kYIioST9GDHte9/BRWgi93rpqbDuFftMjKSMaXS8ABo=
|
|
||||||
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
|
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
|
||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
|
|
||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe h1:40SWqY0zE3qCi6ZrtTf5OUdNm5lDnGnjRSq9GgmeTrg=
|
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead h1:fI1Jck0vUrXT8bnphprS1EoVRe2Q5CKCX8iDlpqjQ/Y=
|
||||||
github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
github.com/emersion/go-vcard v0.0.0-20190105225839-8856043f13c5 h1:n9qx98xiS5V4x2WIpPC2rr9mUM5ri9r/YhCEKbhCHro=
|
github.com/emersion/go-smtp v0.15.0 h1:3+hMGMGrqP/lqd7qoxZc1hTU8LY8gHV9RFGWlqSDmP8=
|
||||||
github.com/emersion/go-vcard v0.0.0-20190105225839-8856043f13c5/go.mod h1:WIi9g8OKJQHXtQbx7GExlo6UAFaui9WDMYabJ+Be4WI=
|
github.com/emersion/go-smtp v0.15.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
||||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY=
|
||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
||||||
|
github.com/emersion/go-vcard v0.0.0-20220507122617-d4056df0ec4a h1:cltZpe6s0SJtqK5c/5y2VrIYi8BAtDM6qjmiGYqfTik=
|
||||||
|
github.com/emersion/go-vcard v0.0.0-20220507122617-d4056df0ec4a/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI=
|
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI=
|
||||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
|
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
|
||||||
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo=
|
||||||
github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So=
|
github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0=
|
||||||
github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
|
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
|
||||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc=
|
||||||
|
github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||||
|
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/jameshoulahan/go-imap v0.0.0-20200728140727-d57327f48843 h1:suxlO4AC4E4bjueAsL0m+qp8kmkxRWMGj+5bBU/KJ8g=
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
github.com/jameshoulahan/go-imap v0.0.0-20200728140727-d57327f48843/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU=
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:g0fAGBisHaEQ0TRq1iBvemFRf+8AEWEmBESSiWB3Vsc=
|
github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
|
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
github.com/hashicorp/go-memdb v1.3.0/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g=
|
||||||
github.com/keybase/go-keychain v0.0.0-20200502122510-cda31fe0c86d h1:gVjhBCfVGl32RIBooOANzfw+0UqX8HU+yPlMv8vypcg=
|
github.com/hashicorp/go-memdb v1.3.3 h1:oGfEWrFuxtIUF3W2q/Jzt6G85TrMk9ey6XfYLvVe1Wo=
|
||||||
github.com/keybase/go-keychain v0.0.0-20200502122510-cda31fe0c86d/go.mod h1:W6EbaYmb4RldPn0N3gvVHjY1wmU59kbymhW9NATWhwY=
|
github.com/hashicorp/go-memdb v1.3.3/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg=
|
||||||
github.com/keybase/go.dbus v0.0.0-20200324223359-a94be52c0b03/go.mod h1:a8clEhrrGV/d76/f9r2I41BwANMihfZYV9C223vaxqE=
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba h1:QFQpJdgbON7I0jr2hYW7Bs+XV0qjc3d5tZoDnRFnqTg=
|
||||||
|
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||||
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
|
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4=
|
||||||
|
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
github.com/martinlindhe/base36 v1.0.0 h1:eYsumTah144C0A8P1T/AVSUk5ZoLnhfYFM3OGQxB52A=
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo=
|
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||||
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/myesui/uuid v1.0.0 h1:xCBmH4l5KuvLYc5L7AS7SZg9/jKdIFubM7OVoLqaQUI=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/myesui/uuid v1.0.0/go.mod h1:2CDfNgU0LR8mIdO8vdWd8i9gWWxLlcoIGGpSNgafq84=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||||
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs=
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE=
|
||||||
|
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8=
|
||||||
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
|
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||||
|
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||||
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/ricochet2200/go-disk-usage/du v0.0.0-20210707232629-ac9918953285 h1:d54EL9l+XteliUfUCGsEwwuk65dmmxX85VXF+9T6+50=
|
||||||
|
github.com/ricochet2200/go-disk-usage/du v0.0.0-20210707232629-ac9918953285/go.mod h1:fxIDly1xtudczrZeOOlfaUvd2OPb2qZAPuWdU2BsBTk=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
|
||||||
|
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e h1:G0DQ/TRQyrEZjtLlLwevFjaRiG8eeCMlq9WXQ2OO2bk=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/twinj/uuid v1.0.0 h1:fzz7COZnDrXGTAOHGuUGYd6sG+JMq+AoE7+Jlu0przk=
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
github.com/twinj/uuid v1.0.0/go.mod h1:mMgcE1RHFUFqe5AfiwlINXisXfDGro23fWdPUfOMjRY=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
github.com/urfave/cli/v2 v2.16.3 h1:gHoFIwpPjoyIMbJp/VFd+/vuD0dAgFK4B6DpEMFJfQk=
|
||||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
github.com/urfave/cli/v2 v2.16.3/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
|
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||||
|
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||||
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||||
|
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
|
golang.org/x/exp v0.0.0-20220921164117-439092de6870 h1:j8b6j9gzSigH28O5SjSpQSSh9lFd6f5D/q0aHjNTulc=
|
||||||
|
golang.org/x/exp v0.0.0-20220921164117-439092de6870/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
||||||
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
||||||
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||||
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 h1:K1zaaMdYBXRyX+cwFnxj7M6zwDyumLQMZ5xqwGvjreQ=
|
||||||
|
google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737/go.mod h1:2r/26NEF3bFmT3eC3aZreahSal0C3Shl8Gi6vyDYqOQ=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
|
||||||
|
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||||
|
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/stretchr/testify.v1 v1.2.2 h1:yhQC6Uy5CqibAIlk1wlusa/MJ3iAN49/BsR/dCCKz3M=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
|
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
|
||||||
|
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||||
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 945 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 25 KiB |
@ -1,61 +1,52 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
// Copyright (c) 2022 Proton AG
|
||||||
//
|
//
|
||||||
// This file is part of ProtonMail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// Package api provides HTTP API of the Bridge.
|
// Package api provides HTTP API of the Bridge.
|
||||||
//
|
//
|
||||||
// API endpoints:
|
// API endpoints:
|
||||||
// * /focus, see focusHandler
|
// - /focus, see focusHandler
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/events"
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/preferences"
|
"github.com/ProtonMail/proton-bridge/v2/internal/events"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/config"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/ports"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/ports"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var log = logrus.WithField("pkg", "api") //nolint:gochecknoglobals
|
||||||
log = logrus.WithField("pkg", "api") //nolint[gochecknoglobals]
|
|
||||||
)
|
|
||||||
|
|
||||||
type apiServer struct {
|
type apiServer struct {
|
||||||
host string
|
host string
|
||||||
pref *config.Preferences
|
settings *settings.Settings
|
||||||
tls *tls.Config
|
|
||||||
certPath string
|
|
||||||
keyPath string
|
|
||||||
eventListener listener.Listener
|
eventListener listener.Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAPIServer returns prepared API server struct.
|
// NewAPIServer returns prepared API server struct.
|
||||||
func NewAPIServer(pref *config.Preferences, tls *tls.Config, certPath, keyPath string, eventListener listener.Listener) *apiServer { //nolint[golint]
|
func NewAPIServer(settings *settings.Settings, eventListener listener.Listener) *apiServer { //nolint:revive
|
||||||
return &apiServer{
|
return &apiServer{
|
||||||
host: bridge.Host,
|
host: bridge.Host,
|
||||||
pref: pref,
|
settings: settings,
|
||||||
tls: tls,
|
|
||||||
certPath: certPath,
|
|
||||||
keyPath: keyPath,
|
|
||||||
eventListener: eventListener,
|
eventListener: eventListener,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,25 +58,24 @@ func (api *apiServer) ListenAndServe() {
|
|||||||
|
|
||||||
addr := api.getAddress()
|
addr := api.getAddress()
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: mux,
|
Handler: mux,
|
||||||
TLSConfig: api.tls,
|
ReadHeaderTimeout: 5 * time.Second, // fix gosec G112 (vulnerability to [Slowloris](https://www.cloudflare.com/en-gb/learning/ddos/ddos-attack-tools/slowloris/) attack).
|
||||||
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("API listening at ", addr)
|
log.Info("API listening at ", addr)
|
||||||
if err := server.ListenAndServeTLS(api.certPath, api.keyPath); err != nil {
|
if err := server.ListenAndServe(); err != nil {
|
||||||
api.eventListener.Emit(events.ErrorEvent, "API failed: "+err.Error())
|
api.eventListener.Emit(events.ErrorEvent, "API failed: "+err.Error())
|
||||||
log.Error("API failed: ", err)
|
log.Error("API failed: ", err)
|
||||||
}
|
}
|
||||||
defer server.Close() //nolint[errcheck]
|
defer server.Close() //nolint:errcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *apiServer) getAddress() string {
|
func (api *apiServer) getAddress() string {
|
||||||
port := api.pref.GetInt(preferences.APIPortKey)
|
port := api.settings.GetInt(settings.APIPortKey)
|
||||||
newPort := ports.FindFreePortFrom(port)
|
newPort := ports.FindFreePortFrom(port)
|
||||||
if newPort != port {
|
if newPort != port {
|
||||||
api.pref.SetInt(preferences.APIPortKey, newPort)
|
api.settings.SetInt(settings.APIPortKey, newPort)
|
||||||
}
|
}
|
||||||
return getAPIAddress(api.host, newPort)
|
return getAPIAddress(api.host, newPort)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,26 +1,26 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
// Copyright (c) 2022 Proton AG
|
||||||
//
|
//
|
||||||
// This file is part of ProtonMail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
||||||
)
|
)
|
||||||
|
|
||||||
// httpHandler with Go's Response and Request.
|
// httpHandler with Go's Response and Request.
|
||||||
|
|||||||
@ -1,29 +1,28 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
// Copyright (c) 2022 Proton AG
|
||||||
//
|
//
|
||||||
// This file is part of ProtonMail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/bridge"
|
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/events"
|
"github.com/ProtonMail/proton-bridge/v2/internal/events"
|
||||||
)
|
)
|
||||||
|
|
||||||
// focusHandler should be called from other instances (attempt to start bridge
|
// focusHandler should be called from other instances (attempt to start bridge
|
||||||
@ -37,16 +36,13 @@ func focusHandler(ctx handlerContext) error {
|
|||||||
|
|
||||||
// CheckOtherInstanceAndFocus is helper for new instances to check if there is
|
// CheckOtherInstanceAndFocus is helper for new instances to check if there is
|
||||||
// already a running instance and get it's focus.
|
// already a running instance and get it's focus.
|
||||||
func CheckOtherInstanceAndFocus(port int, tls *tls.Config) error {
|
func CheckOtherInstanceAndFocus(port int) error {
|
||||||
transport := &http.Transport{TLSClientConfig: tls}
|
|
||||||
client := &http.Client{Transport: transport}
|
|
||||||
|
|
||||||
addr := getAPIAddress(bridge.Host, port)
|
addr := getAPIAddress(bridge.Host, port)
|
||||||
resp, err := client.Get("https://" + addr + "/focus")
|
resp, err := (&http.Client{}).Get("http://" + addr + "/focus")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close() //nolint[errcheck]
|
defer resp.Body.Close() //nolint:errcheck
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
log.Error("Focus error: ", resp.StatusCode)
|
log.Error("Focus error: ", resp.StatusCode)
|
||||||
|
|||||||
@ -1,35 +1,35 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
// Copyright (c) 2022 Proton AG
|
||||||
//
|
//
|
||||||
// This file is part of ProtonMail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package cmd
|
package base
|
||||||
|
|
||||||
import (
|
import "strings"
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// filterProcessSerialNumberFromArgs removes additional flag from MacOS. More info ProcessSerialNumber
|
// StripProcessSerialNumber removes additional flag from macOS.
|
||||||
|
// More info:
|
||||||
// http://mirror.informatimago.com/next/developer.apple.com/documentation/Carbon/Reference/Process_Manager/prmref_main/data_type_5.html#//apple_ref/doc/uid/TP30000208/C001951
|
// http://mirror.informatimago.com/next/developer.apple.com/documentation/Carbon/Reference/Process_Manager/prmref_main/data_type_5.html#//apple_ref/doc/uid/TP30000208/C001951
|
||||||
func filterProcessSerialNumberFromArgs() {
|
func StripProcessSerialNumber(args []string) []string {
|
||||||
tmp := os.Args[:0]
|
res := args[:0]
|
||||||
for _, arg := range os.Args {
|
|
||||||
|
for _, arg := range args {
|
||||||
if !strings.Contains(arg, "-psn_") {
|
if !strings.Contains(arg, "-psn_") {
|
||||||
tmp = append(tmp, arg)
|
res = append(res, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
os.Args = tmp
|
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
430
internal/app/base/base.go
Normal file
@ -0,0 +1,430 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package base implements a common application base currently shared by bridge and IE.
|
||||||
|
// The base includes the following:
|
||||||
|
// - access to standard filesystem locations like config, cache, logging dirs
|
||||||
|
// - an extensible crash handler
|
||||||
|
// - versioned cache directory
|
||||||
|
// - persistent settings
|
||||||
|
// - event listener
|
||||||
|
// - credentials store
|
||||||
|
// - pmapi Manager
|
||||||
|
//
|
||||||
|
// In addition, the base initialises logging and reacts to command line arguments
|
||||||
|
// which control the log verbosity and enable cpu/memory profiling.
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"runtime/pprof"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/ProtonMail/go-autostart"
|
||||||
|
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/api"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/cache"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/tls"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/useragent"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/cookies"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/crash"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/events"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/locations"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/logging"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/sentry"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/users/credentials"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/versioner"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagCPUProfile = "cpu-prof"
|
||||||
|
flagCPUProfileShort = "p"
|
||||||
|
flagMemProfile = "mem-prof"
|
||||||
|
flagMemProfileShort = "m"
|
||||||
|
flagLogLevel = "log-level"
|
||||||
|
flagLogLevelShort = "l"
|
||||||
|
FlagGRPC = "grpc" // FlagGRPC starts the gRPC frontend
|
||||||
|
FlagGRPCShort = "g"
|
||||||
|
FlagCLI = "cli" // FlagCLI indicate to start with command line interface.
|
||||||
|
flagCLIShort = "c"
|
||||||
|
flagRestart = "restart"
|
||||||
|
FlagLauncher = "launcher"
|
||||||
|
FlagNoWindow = "no-window"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Base struct {
|
||||||
|
SentryReporter *sentry.Reporter
|
||||||
|
CrashHandler *crash.Handler
|
||||||
|
Locations *locations.Locations
|
||||||
|
Settings *settings.Settings
|
||||||
|
Lock *os.File
|
||||||
|
Cache *cache.Cache
|
||||||
|
Listener listener.Listener
|
||||||
|
Creds *credentials.Store
|
||||||
|
CM pmapi.Manager
|
||||||
|
CookieJar *cookies.Jar
|
||||||
|
UserAgent *useragent.UserAgent
|
||||||
|
Updater *updater.Updater
|
||||||
|
Versioner *versioner.Versioner
|
||||||
|
TLS *tls.TLS
|
||||||
|
Autostart *autostart.App
|
||||||
|
|
||||||
|
Name string // the app's name
|
||||||
|
usage string // the app's usage description
|
||||||
|
command string // the command used to launch the app (either the exe path or the launcher path)
|
||||||
|
restart bool // whether the app is currently set to restart
|
||||||
|
launcher string // launcher to be used if not set in args
|
||||||
|
mainExecutable string // mainExecutable the main executable process.
|
||||||
|
|
||||||
|
teardown []func() error // actions to perform when app is exiting
|
||||||
|
}
|
||||||
|
|
||||||
|
func New( //nolint:funlen
|
||||||
|
appName,
|
||||||
|
appUsage,
|
||||||
|
configName,
|
||||||
|
updateURLName,
|
||||||
|
keychainName,
|
||||||
|
cacheVersion string,
|
||||||
|
) (*Base, error) {
|
||||||
|
userAgent := useragent.New()
|
||||||
|
|
||||||
|
sentryReporter := sentry.NewReporter(appName, constants.Version, userAgent)
|
||||||
|
|
||||||
|
crashHandler := crash.NewHandler(
|
||||||
|
sentryReporter.ReportException,
|
||||||
|
crash.ShowErrorNotification(appName),
|
||||||
|
)
|
||||||
|
defer crashHandler.HandlePanic()
|
||||||
|
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
os.Args = StripProcessSerialNumber(os.Args)
|
||||||
|
|
||||||
|
locationsProvider, err := locations.NewDefaultProvider(filepath.Join(constants.VendorName, configName))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
locations := locations.New(locationsProvider, configName)
|
||||||
|
|
||||||
|
logsPath, err := locations.ProvideLogsPath()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := logging.Init(logsPath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
crashHandler.AddRecoveryAction(logging.DumpStackTrace(logsPath))
|
||||||
|
|
||||||
|
if err := migrateFiles(configName); err != nil {
|
||||||
|
logrus.WithError(err).Warn("Old config files could not be migrated")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := locations.Clean(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsPath, err := locations.ProvideSettingsPath()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
settingsObj := settings.New(settingsPath)
|
||||||
|
|
||||||
|
lock, err := checkSingleInstance(locations.GetLockFile(), settingsObj)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Warnf("%v is already running", appName)
|
||||||
|
return nil, api.CheckOtherInstanceAndFocus(settingsObj.GetInt(settings.APIPortKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := migrateRebranding(settingsObj, keychainName); err != nil {
|
||||||
|
logrus.WithError(err).Warn("Rebranding migration failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
cachePath, err := locations.ProvideCachePath()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cache, err := cache.New(cachePath, cacheVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := cache.RemoveOldVersions(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
listener := listener.New()
|
||||||
|
events.SetupEvents(listener)
|
||||||
|
|
||||||
|
// If we can't load the keychain for whatever reason,
|
||||||
|
// we signal to frontend and supply a dummy keychain that always returns errors.
|
||||||
|
kc, err := keychain.NewKeychain(settingsObj, keychainName)
|
||||||
|
if err != nil {
|
||||||
|
listener.Emit(events.CredentialsErrorEvent, err.Error())
|
||||||
|
kc = keychain.NewMissingKeychain()
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := pmapi.NewConfig(configName, constants.Version)
|
||||||
|
cfg.GetUserAgent = userAgent.String
|
||||||
|
cfg.UpgradeApplicationHandler = func() { listener.Emit(events.UpgradeApplicationEvent, "") }
|
||||||
|
cfg.TLSIssueHandler = func() { listener.Emit(events.TLSCertIssue, "") }
|
||||||
|
|
||||||
|
cm := pmapi.New(cfg)
|
||||||
|
|
||||||
|
sentryReporter.SetClientFromManager(cm)
|
||||||
|
|
||||||
|
cm.AddConnectionObserver(pmapi.NewConnectionObserver(
|
||||||
|
func() { listener.Emit(events.InternetConnChangedEvent, events.InternetOff) },
|
||||||
|
func() { listener.Emit(events.InternetConnChangedEvent, events.InternetOn) },
|
||||||
|
))
|
||||||
|
|
||||||
|
jar, err := cookies.NewCookieJar(settingsObj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cm.SetCookieJar(jar)
|
||||||
|
|
||||||
|
key, err := crypto.NewKeyFromArmored(updater.DefaultPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
kr, err := crypto.NewKeyRing(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
updatesDir, err := locations.ProvideUpdatesPath()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
versioner := versioner.New(updatesDir)
|
||||||
|
installer := updater.NewInstaller(versioner)
|
||||||
|
updater := updater.New(
|
||||||
|
cm,
|
||||||
|
installer,
|
||||||
|
settingsObj,
|
||||||
|
kr,
|
||||||
|
semver.MustParse(constants.Version),
|
||||||
|
updateURLName,
|
||||||
|
runtime.GOOS,
|
||||||
|
)
|
||||||
|
|
||||||
|
exe, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
autostart := &autostart.App{
|
||||||
|
Name: startupNameForRebranding(appName),
|
||||||
|
DisplayName: appName,
|
||||||
|
Exec: []string{exe, "--" + FlagNoWindow},
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Base{
|
||||||
|
SentryReporter: sentryReporter,
|
||||||
|
CrashHandler: crashHandler,
|
||||||
|
Locations: locations,
|
||||||
|
Settings: settingsObj,
|
||||||
|
Lock: lock,
|
||||||
|
Cache: cache,
|
||||||
|
Listener: listener,
|
||||||
|
Creds: credentials.NewStore(kc),
|
||||||
|
CM: cm,
|
||||||
|
CookieJar: jar,
|
||||||
|
UserAgent: userAgent,
|
||||||
|
Updater: updater,
|
||||||
|
Versioner: versioner,
|
||||||
|
TLS: tls.New(settingsPath),
|
||||||
|
Autostart: autostart,
|
||||||
|
|
||||||
|
Name: appName,
|
||||||
|
usage: appUsage,
|
||||||
|
|
||||||
|
// By default, the command is the app's executable.
|
||||||
|
// This can be changed at runtime by using the "--launcher" flag.
|
||||||
|
command: exe,
|
||||||
|
// By default, the command is the app's executable.
|
||||||
|
// This can be changed at runtime by summoning the SetMainExecutable gRPC call.
|
||||||
|
mainExecutable: exe,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) NewApp(mainLoop func(*Base, *cli.Context) error) *cli.App {
|
||||||
|
app := cli.NewApp()
|
||||||
|
|
||||||
|
app.Name = b.Name
|
||||||
|
app.Usage = b.usage
|
||||||
|
app.Version = constants.Version
|
||||||
|
app.Action = b.wrapMainLoop(mainLoop)
|
||||||
|
app.Flags = []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: flagCPUProfile,
|
||||||
|
Aliases: []string{flagCPUProfileShort},
|
||||||
|
Usage: "Generate CPU profile",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: flagMemProfile,
|
||||||
|
Aliases: []string{flagMemProfileShort},
|
||||||
|
Usage: "Generate memory profile",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: flagLogLevel,
|
||||||
|
Aliases: []string{flagLogLevelShort},
|
||||||
|
Usage: "Set the log level (one of panic, fatal, error, warn, info, debug)",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: FlagGRPC,
|
||||||
|
Aliases: []string{FlagGRPCShort},
|
||||||
|
Usage: "Start the gRPC service",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: FlagCLI,
|
||||||
|
Aliases: []string{flagCLIShort},
|
||||||
|
Usage: "Use command line interface",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: FlagNoWindow,
|
||||||
|
Usage: "Don't show window after start",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: flagRestart,
|
||||||
|
Usage: "The number of times the application has already restarted",
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: FlagLauncher,
|
||||||
|
Usage: "The launcher to use to restart the application",
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetToRestart sets the app to restart the next time it is closed.
|
||||||
|
func (b *Base) SetToRestart() {
|
||||||
|
b.restart = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) ForceLauncher(launcher string) {
|
||||||
|
b.launcher = launcher
|
||||||
|
b.setupLauncher(launcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) SetMainExecutable(exe string) {
|
||||||
|
logrus.Info("Main Executable set to ", exe)
|
||||||
|
b.mainExecutable = exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTeardownAction adds an action to perform during app teardown.
|
||||||
|
func (b *Base) AddTeardownAction(fn func() error) {
|
||||||
|
b.teardown = append(b.teardown, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) wrapMainLoop(appMainLoop func(*Base, *cli.Context) error) cli.ActionFunc { //nolint:funlen
|
||||||
|
return func(c *cli.Context) error {
|
||||||
|
defer b.CrashHandler.HandlePanic()
|
||||||
|
defer func() { _ = b.Lock.Close() }()
|
||||||
|
|
||||||
|
// If launcher was used to start the app, use that for restart
|
||||||
|
// and autostart.
|
||||||
|
if launcher := c.String(FlagLauncher); launcher != "" {
|
||||||
|
b.setupLauncher(launcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Bool(flagCPUProfile) {
|
||||||
|
startCPUProfile()
|
||||||
|
defer pprof.StopCPUProfile()
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Bool(flagMemProfile) {
|
||||||
|
defer makeMemoryProfile()
|
||||||
|
}
|
||||||
|
|
||||||
|
logging.SetLevel(c.String(flagLogLevel))
|
||||||
|
b.CM.SetLogging(logrus.WithField("pkg", "pmapi"), logrus.GetLevel() == logrus.TraceLevel)
|
||||||
|
|
||||||
|
logrus.
|
||||||
|
WithField("appName", b.Name).
|
||||||
|
WithField("version", constants.Version).
|
||||||
|
WithField("revision", constants.Revision).
|
||||||
|
WithField("build", constants.BuildTime).
|
||||||
|
WithField("runtime", runtime.GOOS).
|
||||||
|
WithField("args", os.Args).
|
||||||
|
Info("Run app")
|
||||||
|
|
||||||
|
b.CrashHandler.AddRecoveryAction(func(interface{}) error {
|
||||||
|
sentry.Flush(2 * time.Second)
|
||||||
|
|
||||||
|
if c.Int(flagRestart) > maxAllowedRestarts {
|
||||||
|
logrus.
|
||||||
|
WithField("restart", c.Int("restart")).
|
||||||
|
Warn("Not restarting, already restarted too many times")
|
||||||
|
os.Exit(1)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.restartApp(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := appMainLoop(b, c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.doTeardown(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.restart {
|
||||||
|
return b.restartApp(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) doTeardown() error {
|
||||||
|
for _, action := range b.teardown {
|
||||||
|
if err := action(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) setupLauncher(launcher string) {
|
||||||
|
b.command = launcher
|
||||||
|
// Bridge supports no-window option which we should use
|
||||||
|
// for autostart.
|
||||||
|
b.Autostart.Exec = []string{launcher, "--" + FlagNoWindow}
|
||||||
|
}
|
||||||
131
internal/app/base/migration.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/locations"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// migrateFiles migrates files from their old (pre-refactor) locations to their new locations.
|
||||||
|
// We can remove this eventually.
|
||||||
|
//
|
||||||
|
// | entity | old location | new location |
|
||||||
|
// |-----------|-------------------------------------------|----------------------------------------|
|
||||||
|
// | prefs | ~/.cache/protonmail/<app>/c11/prefs.json | ~/.config/protonmail/<app>/prefs.json |
|
||||||
|
// | c11 1.5.x | ~/.cache/protonmail/<app>/c11 | ~/.cache/protonmail/<app>/cache/c11 |
|
||||||
|
// | c11 1.6.x | ~/.cache/protonmail/<app>/cache/c11 | ~/.config/protonmail/<app>/cache/c11 |
|
||||||
|
// | updates | ~/.cache/protonmail/<app>/updates | ~/.config/protonmail/<app>/updates |.
|
||||||
|
func migrateFiles(configName string) error {
|
||||||
|
locationsProvider, err := locations.NewDefaultProvider(filepath.Join(constants.VendorName, configName))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
locations := locations.New(locationsProvider, configName)
|
||||||
|
userCacheDir := locationsProvider.UserCache()
|
||||||
|
|
||||||
|
if err := migratePrefsFrom15x(locations, userCacheDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := migrateCacheFromBoth15xAnd16x(locations, userCacheDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := migrateUpdatesFrom16x(configName, locations); err != nil { //nolint:revive It is more clear to structure this way
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func migratePrefsFrom15x(locations *locations.Locations, userCacheDir string) error {
|
||||||
|
newSettingsDir, err := locations.ProvideSettingsPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return moveIfExists(
|
||||||
|
filepath.Join(userCacheDir, "c11", "prefs.json"),
|
||||||
|
filepath.Join(newSettingsDir, "prefs.json"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func migrateCacheFromBoth15xAnd16x(locations *locations.Locations, userCacheDir string) error {
|
||||||
|
olderCacheDir := userCacheDir
|
||||||
|
newerCacheDir := locations.GetOldCachePath()
|
||||||
|
latestCacheDir, err := locations.ProvideCachePath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migration for versions before 1.6.x.
|
||||||
|
if err := moveIfExists(
|
||||||
|
filepath.Join(olderCacheDir, "c11"),
|
||||||
|
filepath.Join(latestCacheDir, "c11"),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migration for versions 1.6.x.
|
||||||
|
return moveIfExists(
|
||||||
|
filepath.Join(newerCacheDir, "c11"),
|
||||||
|
filepath.Join(latestCacheDir, "c11"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func migrateUpdatesFrom16x(configName string, locations *locations.Locations) error {
|
||||||
|
// In order to properly update Bridge 1.6.X and higher we need to
|
||||||
|
// change the launcher first. Since this is not part of automatic
|
||||||
|
// updates the migration must wait until manual update. Until that
|
||||||
|
// we need to keep old path.
|
||||||
|
if configName == "bridge" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
oldUpdatesPath := locations.GetOldUpdatesPath()
|
||||||
|
// Do not use ProvideUpdatesPath, that creates dir right away.
|
||||||
|
newUpdatesPath := locations.GetUpdatesPath()
|
||||||
|
|
||||||
|
return moveIfExists(oldUpdatesPath, newUpdatesPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func moveIfExists(source, destination string) error {
|
||||||
|
l := logrus.WithField("source", source).WithField("destination", destination)
|
||||||
|
|
||||||
|
if _, err := os.Stat(source); os.IsNotExist(err) {
|
||||||
|
l.Info("No need to migrate file, source doesn't exist")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(destination); !os.IsNotExist(err) {
|
||||||
|
// Once migrated, files should not stay in source anymore. Therefore
|
||||||
|
// if some files are still in source location but target already exist,
|
||||||
|
// it's suspicious. Could happen by installing new version, then the
|
||||||
|
// old one because of some reason, and then the new one again.
|
||||||
|
// Good to see as warning because it could be a reason why Bridge is
|
||||||
|
// behaving weirdly, like wrong configuration, or db re-sync and so on.
|
||||||
|
l.Warn("No need to migrate file, target already exists")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Info("Migrating files")
|
||||||
|
return os.Rename(source, destination)
|
||||||
|
}
|
||||||
197
internal/app/base/migration_rebranding.go
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const darwin = "darwin"
|
||||||
|
|
||||||
|
func migrateRebranding(settingsObj *settings.Settings, keychainName string) (result error) {
|
||||||
|
if err := migrateStartupBeforeRebranding(); err != nil {
|
||||||
|
result = multierror.Append(result, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lastUsedVersion := settingsObj.Get(settings.LastVersionKey)
|
||||||
|
|
||||||
|
// Skipping migration: it is first bridge start or cache was cleared.
|
||||||
|
if lastUsedVersion == "" {
|
||||||
|
settingsObj.SetBool(settings.RebrandingMigrationKey, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skipping rest of migration: already done
|
||||||
|
if settingsObj.GetBool(settings.RebrandingMigrationKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows", "linux":
|
||||||
|
// GODT-1260 we would need admin rights to changes desktop files
|
||||||
|
// and start menu items.
|
||||||
|
settingsObj.SetBool(settings.RebrandingMigrationKey, true)
|
||||||
|
case darwin:
|
||||||
|
if shouldContinue, err := isMacBeforeRebranding(); !shouldContinue || err != nil {
|
||||||
|
if err != nil {
|
||||||
|
result = multierror.Append(result, err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := migrateMacKeychainBeforeRebranding(settingsObj, keychainName); err != nil {
|
||||||
|
result = multierror.Append(result, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsObj.SetBool(settings.RebrandingMigrationKey, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// migrateMacKeychainBeforeRebranding deals with write access restriction to
|
||||||
|
// mac keychain passwords which are caused by application renaming. The old
|
||||||
|
// passwords are copied under new name in order to have write access afer
|
||||||
|
// renaming.
|
||||||
|
func migrateMacKeychainBeforeRebranding(settingsObj *settings.Settings, keychainName string) error {
|
||||||
|
l := logrus.WithField("pkg", "app/base/migration")
|
||||||
|
l.Warn("Migrating mac keychain")
|
||||||
|
|
||||||
|
helperConstructor, ok := keychain.Helpers["macos-keychain"]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("cannot find macos-keychain helper")
|
||||||
|
}
|
||||||
|
|
||||||
|
oldKC, err := helperConstructor("ProtonMailBridgeService")
|
||||||
|
if err != nil {
|
||||||
|
l.WithError(err).Error("Keychain constructor failed")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
idByURL, err := oldKC.List()
|
||||||
|
if err != nil {
|
||||||
|
l.WithError(err).Error("List old keychain failed")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newKC, err := keychain.NewKeychain(settingsObj, keychainName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for url, id := range idByURL {
|
||||||
|
li := l.WithField("id", id).WithField("url", url)
|
||||||
|
userID, secret, err := oldKC.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
li.WithField("userID", userID).
|
||||||
|
WithField("err", err).
|
||||||
|
Error("Faild to get old item")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, _, err := newKC.Get(userID); err == nil {
|
||||||
|
li.Warn("Skipping migration, item already exists.")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := newKC.Put(userID, secret); err != nil {
|
||||||
|
li.WithError(err).Error("Failed to migrate user")
|
||||||
|
}
|
||||||
|
|
||||||
|
li.Info("Item migrated")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// migrateStartupBeforeRebranding removes old startup links. The creation of new links is
|
||||||
|
// handled by bridge initialisation.
|
||||||
|
func migrateStartupBeforeRebranding() error {
|
||||||
|
path, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
path = filepath.Join(path, `AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\ProtonMail Bridge.lnk`)
|
||||||
|
case "linux":
|
||||||
|
path = filepath.Join(path, `.config/autostart/ProtonMail Bridge.desktop`)
|
||||||
|
case darwin:
|
||||||
|
path = filepath.Join(path, `Library/LaunchAgents/ProtonMail Bridge.plist`)
|
||||||
|
default:
|
||||||
|
return errors.New("unknown GOOS")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.WithField("pkg", "app/base/migration").Warn("Migrating autostartup links")
|
||||||
|
return os.Remove(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// startupNameForRebranding returns the name for autostart launcher based on
|
||||||
|
// type of rebranded instance i.e. update or manual.
|
||||||
|
//
|
||||||
|
// This only affects darwin when udpate re-writes the old startup and then
|
||||||
|
// manual installed it would not run proper exe. Therefore we return "old" name
|
||||||
|
// for updates and "new" name for manual which would be properly migrated.
|
||||||
|
//
|
||||||
|
// For orther (linux and windows) the link is always pointing to launcher which
|
||||||
|
// path didn't changed.
|
||||||
|
func startupNameForRebranding(origin string) string {
|
||||||
|
if runtime.GOOS == darwin {
|
||||||
|
if path, err := os.Executable(); err == nil && strings.Contains(path, "ProtonMail Bridge") {
|
||||||
|
return "ProtonMail Bridge"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to solve for other OS. See comment above.
|
||||||
|
return origin
|
||||||
|
}
|
||||||
|
|
||||||
|
// isBeforeRebranding decide if last used version was older than 2.2.0. If
|
||||||
|
// cannot decide it returns false with error.
|
||||||
|
func isMacBeforeRebranding() (bool, error) {
|
||||||
|
// previous version | update | do mac migration |
|
||||||
|
// | first | false |
|
||||||
|
// cleared-cache | manual | false |
|
||||||
|
// cleared-cache | in-app | false |
|
||||||
|
// old | in-app | false |
|
||||||
|
// old in-app | in-app | false |
|
||||||
|
// old | manual | true |
|
||||||
|
// old in-app | manual | true |
|
||||||
|
// manual | in-app | false |
|
||||||
|
|
||||||
|
// Skip if it was in-app update and not manual
|
||||||
|
if path, err := os.Executable(); err != nil || strings.Contains(path, "ProtonMail Bridge") {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
@ -1,54 +1,56 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
// Copyright (c) 2022 Proton AG
|
||||||
//
|
//
|
||||||
// This file is part of ProtonMail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package cmd
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StartCPUProfile starts CPU pprof.
|
// startCPUProfile starts CPU pprof.
|
||||||
func StartCPUProfile() {
|
func startCPUProfile() {
|
||||||
f, err := os.Create("./cpu.pprof")
|
f, err := os.Create("./cpu.pprof")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Could not create CPU profile: ", err)
|
logrus.Fatal("Could not create CPU profile: ", err)
|
||||||
}
|
}
|
||||||
if err := pprof.StartCPUProfile(f); err != nil {
|
if err := pprof.StartCPUProfile(f); err != nil {
|
||||||
log.Fatal("Could not start CPU profile: ", err)
|
logrus.Fatal("Could not start CPU profile: ", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeMemoryProfile generates memory pprof.
|
// makeMemoryProfile generates memory pprof.
|
||||||
func MakeMemoryProfile() {
|
func makeMemoryProfile() {
|
||||||
name := "./mem.pprof"
|
name := "./mem.pprof"
|
||||||
f, err := os.Create(name)
|
f, err := os.Create(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Could not create memory profile: ", err)
|
logrus.Fatal("Could not create memory profile: ", err)
|
||||||
}
|
}
|
||||||
if abs, err := filepath.Abs(name); err == nil {
|
if abs, err := filepath.Abs(name); err == nil {
|
||||||
name = abs
|
name = abs
|
||||||
}
|
}
|
||||||
log.Info("Writing memory profile to ", name)
|
logrus.Info("Writing memory profile to ", name)
|
||||||
runtime.GC() // get up-to-date statistics
|
runtime.GC() // get up-to-date statistics
|
||||||
if err := pprof.WriteHeapProfile(f); err != nil {
|
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||||
log.Fatal("Could not write memory profile: ", err)
|
logrus.Fatal("Could not write memory profile: ", err)
|
||||||
}
|
}
|
||||||
_ = f.Close()
|
_ = f.Close()
|
||||||
}
|
}
|
||||||
112
internal/app/base/restart.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sys/execabs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// maxAllowedRestarts controls after how many crashes the app will give up restarting.
|
||||||
|
const maxAllowedRestarts = 10
|
||||||
|
|
||||||
|
func (b *Base) restartApp(crash bool) error {
|
||||||
|
var args []string
|
||||||
|
|
||||||
|
if crash {
|
||||||
|
args = incrementRestartFlag(os.Args)[1:]
|
||||||
|
defer func() { os.Exit(1) }()
|
||||||
|
} else {
|
||||||
|
args = os.Args[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.launcher != "" {
|
||||||
|
args = forceLauncherFlag(args, b.launcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, "--wait", b.mainExecutable)
|
||||||
|
|
||||||
|
logrus.
|
||||||
|
WithField("command", b.command).
|
||||||
|
WithField("args", args).
|
||||||
|
Warn("Restarting")
|
||||||
|
|
||||||
|
return execabs.Command(b.command, args...).Start() //nolint:gosec
|
||||||
|
}
|
||||||
|
|
||||||
|
// incrementRestartFlag increments the value of the restart flag.
|
||||||
|
// If no such flag is present, it is added with initial value 1.
|
||||||
|
func incrementRestartFlag(args []string) []string {
|
||||||
|
res := append([]string{}, args...)
|
||||||
|
|
||||||
|
hasFlag := false
|
||||||
|
|
||||||
|
for k, v := range res {
|
||||||
|
if v != "--restart" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFlag = true
|
||||||
|
|
||||||
|
if k+1 >= len(res) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := strconv.Atoi(res[k+1])
|
||||||
|
if err != nil {
|
||||||
|
res[k+1] = "1"
|
||||||
|
} else {
|
||||||
|
res[k+1] = strconv.Itoa(n + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasFlag {
|
||||||
|
res = append(res, "--restart", "1")
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// forceLauncherFlag replace or add the launcher args with the one set in the app.
|
||||||
|
func forceLauncherFlag(args []string, launcher string) []string {
|
||||||
|
res := append([]string{}, args...)
|
||||||
|
|
||||||
|
hasFlag := false
|
||||||
|
|
||||||
|
for k, v := range res {
|
||||||
|
if v != "--launcher" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if k+1 >= len(res) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFlag = true
|
||||||
|
res[k+1] = launcher
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasFlag {
|
||||||
|
res = append(res, "--launcher", launcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
63
internal/app/base/restart_test.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIncrementRestartFlag(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in []string
|
||||||
|
out []string
|
||||||
|
}{
|
||||||
|
{[]string{"./bridge", "--restart", "1"}, []string{"./bridge", "--restart", "2"}},
|
||||||
|
{[]string{"./bridge", "--restart", "2"}, []string{"./bridge", "--restart", "3"}},
|
||||||
|
{[]string{"./bridge", "--other", "--restart", "2"}, []string{"./bridge", "--other", "--restart", "3"}},
|
||||||
|
{[]string{"./bridge", "--restart", "2", "--other"}, []string{"./bridge", "--restart", "3", "--other"}},
|
||||||
|
{[]string{"./bridge", "--restart", "2", "--other", "2"}, []string{"./bridge", "--restart", "3", "--other", "2"}},
|
||||||
|
{[]string{"./bridge"}, []string{"./bridge", "--restart", "1"}},
|
||||||
|
{[]string{"./bridge", "--something"}, []string{"./bridge", "--something", "--restart", "1"}},
|
||||||
|
{[]string{"./bridge", "--something", "--else"}, []string{"./bridge", "--something", "--else", "--restart", "1"}},
|
||||||
|
{[]string{"./bridge", "--restart", "bad"}, []string{"./bridge", "--restart", "1"}},
|
||||||
|
{[]string{"./bridge", "--restart", "bad", "--other"}, []string{"./bridge", "--restart", "1", "--other"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(strings.Join(tt.in, " "), func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.out, incrementRestartFlag(tt.in))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionLessThan(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
|
||||||
|
old := semver.MustParse("1.1.0")
|
||||||
|
current := semver.MustParse("1.1.1")
|
||||||
|
newer := semver.MustParse("1.1.2")
|
||||||
|
|
||||||
|
r.True(old.LessThan(current))
|
||||||
|
r.False(current.LessThan(current))
|
||||||
|
r.False(newer.LessThan(current))
|
||||||
|
}
|
||||||
101
internal/app/base/singleinstance_unix.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
||||||
|
"github.com/allan-simon/go-singleinstance"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkSingleInstance returns error if a bridge instance is already running
|
||||||
|
// This instance should be stop and window of running window should be brought
|
||||||
|
// to focus.
|
||||||
|
//
|
||||||
|
// For macOS and Linux when already running version is older than this instance
|
||||||
|
// it will kill old and continue with this new bridge (i.e. no error returned).
|
||||||
|
func checkSingleInstance(lockFilePath string, settingsObj *settings.Settings) (*os.File, error) {
|
||||||
|
if lock, err := singleinstance.CreateLockFile(lockFilePath); err == nil {
|
||||||
|
// Bridge is not runnig, continue normally
|
||||||
|
return lock, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := runningVersionIsOlder(settingsObj); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pid, err := getPID(lockFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := unix.Kill(pid, unix.SIGTERM); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to wait some time to release file lock
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
return singleinstance.CreateLockFile(lockFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPID(lockFilePath string) (int, error) {
|
||||||
|
file, err := os.Open(filepath.Clean(lockFilePath))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer func() { _ = file.Close() }()
|
||||||
|
|
||||||
|
rawPID := make([]byte, 10) // PID is probably up to 7 digits long, 10 should be enough
|
||||||
|
n, err := file.Read(rawPID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return strconv.Atoi(strings.TrimSpace(string(rawPID[:n])))
|
||||||
|
}
|
||||||
|
|
||||||
|
func runningVersionIsOlder(settingsObj *settings.Settings) error {
|
||||||
|
currentVer, err := semver.StrictNewVersion(constants.Version)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
runningVer, err := semver.StrictNewVersion(settingsObj.Get(settings.LastVersionKey))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !runningVer.LessThan(currentVer) {
|
||||||
|
return errors.New("running version is not older")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
32
internal/app/base/singleinstance_windows.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
|
"github.com/allan-simon/go-singleinstance"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkSingleInstance(lockFilePath string, _ *settings.Settings) (*os.File, error) {
|
||||||
|
return singleinstance.CreateLockFile(lockFilePath)
|
||||||
|
}
|
||||||
281
internal/app/bridge/bridge.go
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package bridge implements the bridge CLI application.
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/api"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/app/base"
|
||||||
|
pkgBridge "github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/frontend"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/imap"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/smtp"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/store"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/store/cache"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/message"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagLogIMAP = "log-imap"
|
||||||
|
flagLogSMTP = "log-smtp"
|
||||||
|
flagNonInteractive = "noninteractive"
|
||||||
|
flagNonInteractiveShort = "n"
|
||||||
|
// Memory cache was estimated by empirical usage in the past, and it was set to 100MB.
|
||||||
|
// NOTE: This value must not be less than maximal size of one email (~30MB).
|
||||||
|
inMemoryCacheLimit = 100 * (1 << 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(base *base.Base) *cli.App {
|
||||||
|
app := base.NewApp(main)
|
||||||
|
|
||||||
|
app.Flags = append(app.Flags, []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: flagLogIMAP,
|
||||||
|
Usage: "Enable logging of IMAP communications (all|client|server) (may contain decrypted data!)",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: flagLogSMTP,
|
||||||
|
Usage: "Enable logging of SMTP communications (may contain decrypted data!)",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: flagNonInteractive,
|
||||||
|
Aliases: []string{flagNonInteractiveShort},
|
||||||
|
Usage: "Start Bridge entirely non-interactively",
|
||||||
|
},
|
||||||
|
}...)
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func main(b *base.Base, c *cli.Context) error { //nolint:funlen
|
||||||
|
frontendType := getFrontendTypeFromCLIParams(c)
|
||||||
|
if frontendType == frontend.Unknown {
|
||||||
|
_ = cli.ShowAppHelp(c)
|
||||||
|
return errors.New("no frontend was specified. Use --grpc, --cli or --noninteractive")
|
||||||
|
}
|
||||||
|
|
||||||
|
f := frontend.New(
|
||||||
|
frontendType,
|
||||||
|
!c.Bool(base.FlagNoWindow),
|
||||||
|
b.CrashHandler,
|
||||||
|
b.Listener,
|
||||||
|
b.Updater,
|
||||||
|
b,
|
||||||
|
b.Locations,
|
||||||
|
)
|
||||||
|
|
||||||
|
cache, cacheErr := loadMessageCache(b)
|
||||||
|
if cacheErr != nil {
|
||||||
|
logrus.WithError(cacheErr).Error("Could not load local cache.")
|
||||||
|
}
|
||||||
|
|
||||||
|
builder := message.NewBuilder(
|
||||||
|
b.Settings.GetInt(settings.FetchWorkers),
|
||||||
|
b.Settings.GetInt(settings.AttachmentWorkers),
|
||||||
|
)
|
||||||
|
|
||||||
|
bridge := pkgBridge.New(
|
||||||
|
b.Locations,
|
||||||
|
b.Cache,
|
||||||
|
b.Settings,
|
||||||
|
b.SentryReporter,
|
||||||
|
b.CrashHandler,
|
||||||
|
b.Listener,
|
||||||
|
b.TLS,
|
||||||
|
b.UserAgent,
|
||||||
|
cache,
|
||||||
|
builder,
|
||||||
|
b.CM,
|
||||||
|
b.Creds,
|
||||||
|
b.Updater,
|
||||||
|
b.Versioner,
|
||||||
|
b.Autostart,
|
||||||
|
)
|
||||||
|
imapBackend := imap.NewIMAPBackend(b.CrashHandler, b.Listener, b.Cache, b.Settings, bridge)
|
||||||
|
smtpBackend := smtp.NewSMTPBackend(b.CrashHandler, b.Listener, b.Settings, bridge)
|
||||||
|
|
||||||
|
tlsConfig, err := bridge.GetTLSConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cacheErr != nil {
|
||||||
|
bridge.AddError(pkgBridge.ErrLocalCacheUnavailable)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer b.CrashHandler.HandlePanic()
|
||||||
|
api.NewAPIServer(b.Settings, b.Listener).ListenAndServe()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer b.CrashHandler.HandlePanic()
|
||||||
|
imapPort := b.Settings.GetInt(settings.IMAPPortKey)
|
||||||
|
imap.NewIMAPServer(
|
||||||
|
b.CrashHandler,
|
||||||
|
c.String(flagLogIMAP) == "client" || c.String(flagLogIMAP) == "all",
|
||||||
|
c.String(flagLogIMAP) == "server" || c.String(flagLogIMAP) == "all",
|
||||||
|
imapPort, tlsConfig, imapBackend, b.UserAgent, b.Listener).ListenAndServe()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer b.CrashHandler.HandlePanic()
|
||||||
|
smtpPort := b.Settings.GetInt(settings.SMTPPortKey)
|
||||||
|
useSSL := b.Settings.GetBool(settings.SMTPSSLKey)
|
||||||
|
smtp.NewSMTPServer(
|
||||||
|
b.CrashHandler,
|
||||||
|
c.Bool(flagLogSMTP),
|
||||||
|
smtpPort, useSSL, tlsConfig, smtpBackend, b.Listener).ListenAndServe()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// We want to remove old versions if the app exits successfully.
|
||||||
|
b.AddTeardownAction(b.Versioner.RemoveOldVersions)
|
||||||
|
|
||||||
|
// We want cookies to be saved to disk so they are loaded the next time.
|
||||||
|
b.AddTeardownAction(b.CookieJar.PersistCookies)
|
||||||
|
|
||||||
|
if frontendType == frontend.NonInteractive {
|
||||||
|
return <-(make(chan error))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch for updates routine
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(constants.UpdateCheckInterval)
|
||||||
|
|
||||||
|
for {
|
||||||
|
checkAndHandleUpdate(b.Updater, f, b.Settings.GetBool(settings.AutoUpdateKey))
|
||||||
|
<-ticker.C
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return f.Loop(bridge)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFrontendTypeFromCLIParams(c *cli.Context) frontend.Type {
|
||||||
|
switch {
|
||||||
|
case c.Bool(base.FlagGRPC):
|
||||||
|
return frontend.GRPC
|
||||||
|
case c.Bool(base.FlagCLI):
|
||||||
|
return frontend.CLI
|
||||||
|
case c.Bool(flagNonInteractive):
|
||||||
|
return frontend.NonInteractive
|
||||||
|
default:
|
||||||
|
return frontend.Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkAndHandleUpdate(u types.Updater, f frontend.Frontend, autoUpdate bool) {
|
||||||
|
log := logrus.WithField("pkg", "app/bridge")
|
||||||
|
version, err := u.Check()
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("An error occurred while checking for updates")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.WaitUntilFrontendIsReady()
|
||||||
|
|
||||||
|
// Update links in UI
|
||||||
|
f.SetVersion(version)
|
||||||
|
|
||||||
|
if !u.IsUpdateApplicable(version) {
|
||||||
|
log.Info("No need to update")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithField("version", version.Version).Info("An update is available")
|
||||||
|
|
||||||
|
if !autoUpdate {
|
||||||
|
f.NotifyManualUpdate(version, u.CanInstall(version))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !u.CanInstall(version) {
|
||||||
|
log.Info("A manual update is required")
|
||||||
|
f.NotifySilentUpdateError(updater.ErrManualUpdateRequired)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := u.InstallUpdate(version); err != nil {
|
||||||
|
if errors.Cause(err) == updater.ErrDownloadVerify {
|
||||||
|
log.WithError(err).Warning("Skipping update installation due to temporary error")
|
||||||
|
} else {
|
||||||
|
log.WithError(err).Error("The update couldn't be installed")
|
||||||
|
f.NotifySilentUpdateError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.NotifySilentUpdateInstalled()
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadMessageCache loads local cache in case it is enabled in settings and available.
|
||||||
|
// In any other case it is returning in-memory cache. Could also return an error in case
|
||||||
|
// local cache is enabled but unavailable (in-memory cache will be returned nevertheless).
|
||||||
|
func loadMessageCache(b *base.Base) (cache.Cache, error) {
|
||||||
|
if !b.Settings.GetBool(settings.CacheEnabledKey) {
|
||||||
|
return cache.NewInMemoryCache(inMemoryCacheLimit), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var compressor cache.Compressor
|
||||||
|
|
||||||
|
// NOTE(GODT-1158): Changing compression is not an option currently
|
||||||
|
// available for user but, if user changes compression setting we have
|
||||||
|
// to nuke the cache.
|
||||||
|
if b.Settings.GetBool(settings.CacheCompressionKey) {
|
||||||
|
compressor = &cache.GZipCompressor{}
|
||||||
|
} else {
|
||||||
|
compressor = &cache.NoopCompressor{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var path string
|
||||||
|
|
||||||
|
if customPath := b.Settings.Get(settings.CacheLocationKey); customPath != "" {
|
||||||
|
path = customPath
|
||||||
|
} else {
|
||||||
|
path = b.Cache.GetDefaultMessageCacheDir()
|
||||||
|
// Store path so it will always persist if default location
|
||||||
|
// will be changed in new version.
|
||||||
|
b.Settings.Set(settings.CacheLocationKey, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// To prevent memory peaks we set maximal write concurrency for store
|
||||||
|
// build jobs.
|
||||||
|
store.SetBuildAndCacheJobLimit(b.Settings.GetInt(settings.CacheConcurrencyWrite))
|
||||||
|
|
||||||
|
messageCache, err := cache.NewOnDiskCache(path, compressor, cache.Options{
|
||||||
|
MinFreeAbs: uint64(b.Settings.GetInt(settings.CacheMinFreeAbsKey)),
|
||||||
|
MinFreeRat: b.Settings.GetFloat64(settings.CacheMinFreeRatKey),
|
||||||
|
ConcurrentRead: b.Settings.GetInt(settings.CacheConcurrencyRead),
|
||||||
|
ConcurrentWrite: b.Settings.GetInt(settings.CacheConcurrencyWrite),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return cache.NewInMemoryCache(inMemoryCacheLimit), err
|
||||||
|
}
|
||||||
|
|
||||||
|
return messageCache, nil
|
||||||
|
}
|
||||||
38
internal/bridge/autostart.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package bridge provides core functionality of Bridge app.
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import "github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
|
|
||||||
|
// IsAutostartEnabled checks if link file exits.
|
||||||
|
func (b *Bridge) IsAutostartEnabled() bool {
|
||||||
|
return b.autostart.IsEnabled()
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableAutostart creates link and sets the preferences.
|
||||||
|
func (b *Bridge) EnableAutostart() error {
|
||||||
|
b.settings.SetBool(settings.AutostartKey, true)
|
||||||
|
return b.autostart.Enable()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableAutostart removes link and sets the preferences.
|
||||||
|
func (b *Bridge) DisableAutostart() error {
|
||||||
|
b.settings.SetBool(settings.AutostartKey, false)
|
||||||
|
return b.autostart.Disable()
|
||||||
|
}
|
||||||
@ -1,79 +1,130 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
// Copyright (c) 2022 Proton AG
|
||||||
//
|
//
|
||||||
// This file is part of ProtonMail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// Package bridge provides core functionality of Bridge app.
|
// Package bridge provides core functionality of Bridge app.
|
||||||
package bridge
|
package bridge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/metrics"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/preferences"
|
"github.com/ProtonMail/go-autostart"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/users"
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/tls"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/useragent"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/metrics"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/sentry"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/store/cache"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/users"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/message"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
||||||
logrus "github.com/sirupsen/logrus"
|
logrus "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var log = logrus.WithField("pkg", "bridge") //nolint:gochecknoglobals
|
||||||
log = logrus.WithField("pkg", "bridge") //nolint[gochecknoglobals]
|
|
||||||
)
|
var ErrLocalCacheUnavailable = errors.New("local cache is unavailable")
|
||||||
|
|
||||||
type Bridge struct {
|
type Bridge struct {
|
||||||
*users.Users
|
*users.Users
|
||||||
|
|
||||||
pref PreferenceProvider
|
locations Locator
|
||||||
clientManager users.ClientManager
|
settings SettingsProvider
|
||||||
|
clientManager pmapi.Manager
|
||||||
|
updater Updater
|
||||||
|
versioner Versioner
|
||||||
|
tls *tls.TLS
|
||||||
|
userAgent *useragent.UserAgent
|
||||||
|
cacheProvider CacheProvider
|
||||||
|
autostart *autostart.App
|
||||||
|
// Bridge's global errors list.
|
||||||
|
errors []error
|
||||||
|
|
||||||
userAgentClientName string
|
isAllMailVisible bool
|
||||||
userAgentClientVersion string
|
isFirstStart bool
|
||||||
userAgentOS string
|
lastVersion string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(
|
func New( //nolint:funlen
|
||||||
config Configer,
|
locations Locator,
|
||||||
pref PreferenceProvider,
|
cacheProvider CacheProvider,
|
||||||
|
setting SettingsProvider,
|
||||||
|
sentryReporter *sentry.Reporter,
|
||||||
panicHandler users.PanicHandler,
|
panicHandler users.PanicHandler,
|
||||||
eventListener listener.Listener,
|
eventListener listener.Listener,
|
||||||
clientManager users.ClientManager,
|
tls *tls.TLS,
|
||||||
|
userAgent *useragent.UserAgent,
|
||||||
|
cache cache.Cache,
|
||||||
|
builder *message.Builder,
|
||||||
|
clientManager pmapi.Manager,
|
||||||
credStorer users.CredentialsStorer,
|
credStorer users.CredentialsStorer,
|
||||||
|
updater Updater,
|
||||||
|
versioner Versioner,
|
||||||
|
autostart *autostart.App,
|
||||||
) *Bridge {
|
) *Bridge {
|
||||||
// Allow DoH before starting the app if the user has previously set this setting.
|
// Allow DoH before starting the app if the user has previously set this setting.
|
||||||
// This allows us to start even if protonmail is blocked.
|
// This allows us to start even if protonmail is blocked.
|
||||||
if pref.GetBool(preferences.AllowProxyKey) {
|
if setting.GetBool(settings.AllowProxyKey) {
|
||||||
clientManager.AllowProxy()
|
clientManager.AllowProxy()
|
||||||
}
|
}
|
||||||
|
|
||||||
storeFactory := newStoreFactory(config, panicHandler, clientManager, eventListener)
|
u := users.New(
|
||||||
u := users.New(config, panicHandler, eventListener, clientManager, credStorer, storeFactory, true)
|
locations,
|
||||||
|
panicHandler,
|
||||||
|
eventListener,
|
||||||
|
clientManager,
|
||||||
|
credStorer,
|
||||||
|
newStoreFactory(cacheProvider, sentryReporter, panicHandler, eventListener, cache, builder),
|
||||||
|
)
|
||||||
|
|
||||||
b := &Bridge{
|
b := &Bridge{
|
||||||
Users: u,
|
Users: u,
|
||||||
|
locations: locations,
|
||||||
pref: pref,
|
settings: setting,
|
||||||
clientManager: clientManager,
|
clientManager: clientManager,
|
||||||
|
updater: updater,
|
||||||
|
versioner: versioner,
|
||||||
|
tls: tls,
|
||||||
|
userAgent: userAgent,
|
||||||
|
cacheProvider: cacheProvider,
|
||||||
|
autostart: autostart,
|
||||||
|
isFirstStart: false,
|
||||||
|
isAllMailVisible: setting.GetBool(settings.IsAllMailVisible),
|
||||||
}
|
}
|
||||||
|
|
||||||
if pref.GetBool(preferences.FirstStartKey) {
|
if setting.GetBool(settings.FirstStartKey) {
|
||||||
b.SendMetric(metrics.New(metrics.Setup, metrics.FirstStart, metrics.Label(config.GetVersion())))
|
b.isFirstStart = true
|
||||||
pref.SetBool(preferences.FirstStartKey, false)
|
if err := b.SendMetric(metrics.New(metrics.Setup, metrics.FirstStart, metrics.Label(constants.Version))); err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to send metric")
|
||||||
|
}
|
||||||
|
setting.SetBool(settings.FirstStartKey, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep in bridge and update in settings the last used version.
|
||||||
|
b.lastVersion = b.settings.Get(settings.LastVersionKey)
|
||||||
|
b.settings.Set(settings.LastVersionKey, constants.Version)
|
||||||
|
|
||||||
go b.heartbeat()
|
go b.heartbeat()
|
||||||
|
|
||||||
return b
|
return b
|
||||||
@ -81,72 +132,194 @@ func New(
|
|||||||
|
|
||||||
// heartbeat sends a heartbeat signal once a day.
|
// heartbeat sends a heartbeat signal once a day.
|
||||||
func (b *Bridge) heartbeat() {
|
func (b *Bridge) heartbeat() {
|
||||||
ticker := time.NewTicker(1 * time.Minute)
|
for range time.Tick(time.Minute) {
|
||||||
|
lastHeartbeatDay, err := strconv.ParseInt(b.settings.Get(settings.LastHeartbeatKey), 10, 64)
|
||||||
for range ticker.C {
|
|
||||||
next, err := strconv.ParseInt(b.pref.Get(preferences.NextHeartbeatKey), 10, 64)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
nextTime := time.Unix(next, 0)
|
|
||||||
if time.Now().After(nextTime) {
|
// If we're still on the same day, don't send a heartbeat.
|
||||||
b.SendMetric(metrics.New(metrics.Heartbeat, metrics.Daily, metrics.NoLabel))
|
if time.Now().YearDay() == int(lastHeartbeatDay) {
|
||||||
nextTime = nextTime.Add(24 * time.Hour)
|
continue
|
||||||
b.pref.Set(preferences.NextHeartbeatKey, strconv.FormatInt(nextTime.Unix(), 10))
|
}
|
||||||
|
|
||||||
|
// We're on the next (or a different) day, so send a heartbeat.
|
||||||
|
if err := b.SendMetric(metrics.New(metrics.Heartbeat, metrics.Daily, metrics.NoLabel)); err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to send heartbeat")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heartbeat was sent successfully so update the last heartbeat day.
|
||||||
|
b.settings.Set(settings.LastHeartbeatKey, fmt.Sprintf("%v", time.Now().YearDay()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUpdateChannel returns currently set update channel.
|
||||||
|
func (b *Bridge) GetUpdateChannel() updater.UpdateChannel {
|
||||||
|
return updater.UpdateChannel(b.settings.Get(settings.UpdateChannelKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUpdateChannel switches update channel.
|
||||||
|
func (b *Bridge) SetUpdateChannel(channel updater.UpdateChannel) {
|
||||||
|
b.settings.Set(settings.UpdateChannelKey, string(channel))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) resetToLatestStable() error {
|
||||||
|
version, err := b.updater.Check()
|
||||||
|
if err != nil {
|
||||||
|
// If we can not check for updates - just remove all local updates and reset to base installer version.
|
||||||
|
// Not using `b.locations.ClearUpdates()` because `versioner.RemoveOtherVersions` can also handle
|
||||||
|
// case when it is needed to remove currently running verion.
|
||||||
|
if err := b.versioner.RemoveOtherVersions(semver.MustParse("0.0.0")); err != nil {
|
||||||
|
log.WithError(err).Error("Failed to clear updates while downgrading channel")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If current version is same as upstream stable version - do nothing.
|
||||||
|
if version.Version.Equal(semver.MustParse(constants.Version)) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.updater.InstallUpdate(version); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.versioner.RemoveOtherVersions(version.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FactoryReset will remove all local cache and settings.
|
||||||
|
// It will also downgrade to latest stable version if user is on early version.
|
||||||
|
func (b *Bridge) FactoryReset() {
|
||||||
|
wasEarly := b.GetUpdateChannel() == updater.EarlyChannel
|
||||||
|
|
||||||
|
b.settings.Set(settings.UpdateChannelKey, string(updater.StableChannel))
|
||||||
|
|
||||||
|
if wasEarly {
|
||||||
|
if err := b.resetToLatestStable(); err != nil {
|
||||||
|
log.WithError(err).Error("Failed to reset to latest stable version")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.Users.ClearData(); err != nil {
|
||||||
|
log.WithError(err).Error("Failed to remove bridge data")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.Users.ClearUsers(); err != nil {
|
||||||
|
log.WithError(err).Error("Failed to remove bridge users")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKeychainApp returns current keychain helper.
|
||||||
|
func (b *Bridge) GetKeychainApp() string {
|
||||||
|
return b.settings.Get(settings.PreferredKeychainKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKeychainApp sets current keychain helper.
|
||||||
|
func (b *Bridge) SetKeychainApp(helper string) {
|
||||||
|
b.settings.Set(settings.PreferredKeychainKey, helper)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) EnableCache() error {
|
||||||
|
if err := b.Users.EnableCache(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.settings.SetBool(settings.CacheEnabledKey, true)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) DisableCache() error {
|
||||||
|
if err := b.Users.DisableCache(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.settings.SetBool(settings.CacheEnabledKey, false)
|
||||||
|
// Reset back to the default location when disabling.
|
||||||
|
b.settings.Set(settings.CacheLocationKey, b.cacheProvider.GetDefaultMessageCacheDir())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) MigrateCache(from, to string) error {
|
||||||
|
if err := b.Users.MigrateCache(from, to); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.settings.Set(settings.CacheLocationKey, to)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetProxyAllowed instructs the app whether to use DoH to access an API proxy if necessary.
|
||||||
|
// It also needs to work before the app is initialised (because we may need to use the proxy at startup).
|
||||||
|
func (b *Bridge) SetProxyAllowed(proxyAllowed bool) {
|
||||||
|
b.settings.SetBool(settings.AllowProxyKey, proxyAllowed)
|
||||||
|
if proxyAllowed {
|
||||||
|
b.clientManager.AllowProxy()
|
||||||
|
} else {
|
||||||
|
b.clientManager.DisallowProxy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProxyAllowed returns whether use of DoH is enabled to access an API proxy if necessary.
|
||||||
|
func (b *Bridge) GetProxyAllowed() bool {
|
||||||
|
return b.settings.GetBool(settings.AllowProxyKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddError add an error to a global error list if it does not contain it yet. Adding nil is noop.
|
||||||
|
func (b *Bridge) AddError(err error) {
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b.HasError(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.errors = append(b.errors, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelError removes an error from global error list.
|
||||||
|
func (b *Bridge) DelError(err error) {
|
||||||
|
for idx, val := range b.errors {
|
||||||
|
if val == err {
|
||||||
|
b.errors = append(b.errors[:idx], b.errors[idx+1:]...)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentClient returns currently connected client (e.g. Thunderbird).
|
// HasError returnes true if global error list contains an err.
|
||||||
func (b *Bridge) GetCurrentClient() string {
|
func (b *Bridge) HasError(err error) bool {
|
||||||
res := b.userAgentClientName
|
for _, val := range b.errors {
|
||||||
if b.userAgentClientVersion != "" {
|
if val == err {
|
||||||
res = res + " " + b.userAgentClientVersion
|
return true
|
||||||
}
|
}
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCurrentClient updates client info (e.g. Thunderbird) and sets the user agent
|
|
||||||
// on pmapi. By default no client is used, IMAP has to detect it on first login.
|
|
||||||
func (b *Bridge) SetCurrentClient(clientName, clientVersion string) {
|
|
||||||
b.userAgentClientName = clientName
|
|
||||||
b.userAgentClientVersion = clientVersion
|
|
||||||
b.updateUserAgent()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCurrentOS updates OS and sets the user agent on pmapi. By default we use
|
|
||||||
// `runtime.GOOS`, but this can be overridden in case of better detection.
|
|
||||||
func (b *Bridge) SetCurrentOS(os string) {
|
|
||||||
b.userAgentOS = os
|
|
||||||
b.updateUserAgent()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bridge) updateUserAgent() {
|
|
||||||
b.clientManager.SetUserAgent(b.userAgentClientName, b.userAgentClientVersion, b.userAgentOS)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReportBug reports a new bug from the user.
|
|
||||||
func (b *Bridge) ReportBug(osType, osVersion, description, accountName, address, emailClient string) error {
|
|
||||||
c := b.clientManager.GetAnonymousClient()
|
|
||||||
defer c.Logout()
|
|
||||||
|
|
||||||
title := "[Bridge] Bug"
|
|
||||||
report := pmapi.ReportReq{
|
|
||||||
OS: osType,
|
|
||||||
OSVersion: osVersion,
|
|
||||||
Browser: emailClient,
|
|
||||||
Title: title,
|
|
||||||
Description: description,
|
|
||||||
Username: accountName,
|
|
||||||
Email: address,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.Report(report); err != nil {
|
return false
|
||||||
log.Error("Reporting bug failed: ", err)
|
}
|
||||||
return err
|
|
||||||
}
|
// GetLastVersion returns the version which was used in previous execution of
|
||||||
|
// Bridge.
|
||||||
log.Info("Bug successfully reported")
|
func (b *Bridge) GetLastVersion() string {
|
||||||
|
return b.lastVersion
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
// IsFirstStart returns true when Bridge is running for first time or after
|
||||||
|
// factory reset.
|
||||||
|
func (b *Bridge) IsFirstStart() bool {
|
||||||
|
return b.isFirstStart
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAllMailVisible can be called extensively by IMAP. Therefore, it is better
|
||||||
|
// to cache the value instead of reading from settings file.
|
||||||
|
func (b *Bridge) IsAllMailVisible() bool {
|
||||||
|
return b.isAllMailVisible
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) SetIsAllMailVisible(isVisible bool) {
|
||||||
|
b.settings.SetBool(settings.IsAllMailVisible, isVisible)
|
||||||
|
b.isAllMailVisible = isVisible
|
||||||
}
|
}
|
||||||
|
|||||||
206
internal/bridge/bug_report.go
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/logging"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MaxAttachmentSize = 7 * 1024 * 1024 // MaxAttachmentSize 7 MB total limit
|
||||||
|
MaxCompressedFilesCount = 6
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrSizeTooLarge = errors.New("file is too big")
|
||||||
|
|
||||||
|
// ReportBug reports a new bug from the user.
|
||||||
|
func (b *Bridge) ReportBug(osType, osVersion, description, accountName, address, emailClient string, attachLogs bool) error {
|
||||||
|
if user, err := b.GetUser(address); err == nil {
|
||||||
|
accountName = user.Username()
|
||||||
|
} else if users := b.GetUsers(); len(users) > 0 {
|
||||||
|
accountName = users[0].Username()
|
||||||
|
}
|
||||||
|
|
||||||
|
report := pmapi.ReportBugReq{
|
||||||
|
OS: osType,
|
||||||
|
OSVersion: osVersion,
|
||||||
|
Browser: emailClient,
|
||||||
|
Title: "[Bridge] Bug",
|
||||||
|
Description: description,
|
||||||
|
Username: accountName,
|
||||||
|
Email: address,
|
||||||
|
}
|
||||||
|
|
||||||
|
if attachLogs {
|
||||||
|
logs, err := b.getMatchingLogs(
|
||||||
|
func(filename string) bool {
|
||||||
|
return logging.MatchLogName(filename) && !logging.MatchStackTraceName(filename)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("Can't get log files list")
|
||||||
|
}
|
||||||
|
crashes, err := b.getMatchingLogs(
|
||||||
|
func(filename string) bool {
|
||||||
|
return logging.MatchLogName(filename) && logging.MatchStackTraceName(filename)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("Can't get crash files list")
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchFiles []string
|
||||||
|
|
||||||
|
matchFiles = append(matchFiles, logs[max(0, len(logs)-(MaxCompressedFilesCount/2)):]...)
|
||||||
|
matchFiles = append(matchFiles, crashes[max(0, len(crashes)-(MaxCompressedFilesCount/2)):]...)
|
||||||
|
|
||||||
|
archive, err := zipFiles(matchFiles)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("Can't zip logs and crashes")
|
||||||
|
}
|
||||||
|
|
||||||
|
if archive != nil {
|
||||||
|
report.AddAttachment("logs.zip", "application/zip", archive)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.clientManager.ReportBug(context.Background(), report)
|
||||||
|
}
|
||||||
|
|
||||||
|
func max(a, b int) int {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) getMatchingLogs(filenameMatchFunc func(string) bool) (filenames []string, err error) {
|
||||||
|
logsPath, err := b.locations.ProvideLogsPath()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := os.ReadDir(logsPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchFiles []string
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
if filenameMatchFunc(file.Name()) {
|
||||||
|
matchFiles = append(matchFiles, filepath.Join(logsPath, file.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(matchFiles) // Sorted by timestamp: oldest first.
|
||||||
|
|
||||||
|
return matchFiles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type LimitedBuffer struct {
|
||||||
|
capacity int
|
||||||
|
buf *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLimitedBuffer(capacity int) *LimitedBuffer {
|
||||||
|
return &LimitedBuffer{
|
||||||
|
capacity: capacity,
|
||||||
|
buf: bytes.NewBuffer(make([]byte, 0, capacity)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *LimitedBuffer) Write(p []byte) (n int, err error) {
|
||||||
|
if len(p)+b.buf.Len() > b.capacity {
|
||||||
|
return 0, ErrSizeTooLarge
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.buf.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *LimitedBuffer) Read(p []byte) (n int, err error) {
|
||||||
|
return b.buf.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func zipFiles(filenames []string) (io.Reader, error) {
|
||||||
|
if len(filenames) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := NewLimitedBuffer(MaxAttachmentSize)
|
||||||
|
|
||||||
|
w := zip.NewWriter(buf)
|
||||||
|
defer w.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
for _, file := range filenames {
|
||||||
|
err := addFileToZip(file, w)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFileToZip(filename string, writer *zip.Writer) error {
|
||||||
|
fileReader, err := os.Open(filepath.Clean(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fileReader.Close() //nolint:errcheck,gosec
|
||||||
|
|
||||||
|
fileInfo, err := fileReader.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
header, err := zip.FileInfoHeader(fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
header.Method = zip.Deflate
|
||||||
|
header.Name = filepath.Base(filename)
|
||||||
|
|
||||||
|
fileWriter, err := writer.CreateHeader(header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(fileWriter, fileReader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fileReader.Close()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
70
internal/bridge/configure.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/clientconfig"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/useragent"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *Bridge) ConfigureAppleMail(userID, address string) (bool, error) {
|
||||||
|
user, err := b.GetUser(userID)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if address == "" {
|
||||||
|
address = user.GetPrimaryAddress()
|
||||||
|
}
|
||||||
|
|
||||||
|
username := address
|
||||||
|
addresses := address
|
||||||
|
|
||||||
|
if user.IsCombinedAddressMode() {
|
||||||
|
username = user.GetPrimaryAddress()
|
||||||
|
addresses = strings.Join(user.GetAddresses(), ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
restart = false
|
||||||
|
smtpSSL = b.settings.GetBool(settings.SMTPSSLKey)
|
||||||
|
)
|
||||||
|
|
||||||
|
// If configuring apple mail for Catalina or newer, users should use SSL.
|
||||||
|
if useragent.IsCatalinaOrNewer() && !smtpSSL {
|
||||||
|
smtpSSL = true
|
||||||
|
restart = true
|
||||||
|
b.settings.SetBool(settings.SMTPSSLKey, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := (&clientconfig.AppleMail{}).Configure(
|
||||||
|
Host,
|
||||||
|
b.settings.GetInt(settings.IMAPPortKey),
|
||||||
|
b.settings.GetInt(settings.SMTPPortKey),
|
||||||
|
false, smtpSSL,
|
||||||
|
username, addresses,
|
||||||
|
user.GetBridgePassword(),
|
||||||
|
); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return restart, nil
|
||||||
|
}
|
||||||
@ -1,19 +1,19 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
// Copyright (c) 2022 Proton AG
|
||||||
//
|
//
|
||||||
// This file is part of ProtonMail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package bridge
|
package bridge
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
|
||||||
//
|
|
||||||
// This file is part of ProtonMail Bridge.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// Code generated by ./credits.sh at Tue Sep 29 14:56:25 CEST 2020. DO NOT EDIT.
|
|
||||||
|
|
||||||
package bridge
|
|
||||||
|
|
||||||
const Credits = "github.com/0xAX/notificator;github.com/Masterminds/semver/v3;github.com/ProtonMail/bcrypt;github.com/ProtonMail/crypto;github.com/ProtonMail/docker-credential-helpers;github.com/ProtonMail/go-appdir;github.com/ProtonMail/go-apple-mobileconfig;github.com/ProtonMail/go-autostart;github.com/ProtonMail/go-imap;github.com/ProtonMail/go-imap-id;github.com/ProtonMail/go-smtp;github.com/ProtonMail/go-vcard;github.com/ProtonMail/gopenpgp/v2;github.com/ProtonMail/mbox;github.com/PuerkitoBio/goquery;github.com/abiosoft/ishell;github.com/abiosoft/readline;github.com/allan-simon/go-singleinstance;github.com/certifi/gocertifi;github.com/chzyer/logex;github.com/chzyer/test;github.com/cucumber/godog;github.com/docker/docker-credential-helpers;github.com/emersion/go-imap;github.com/emersion/go-imap-appendlimit;github.com/emersion/go-imap-idle;github.com/emersion/go-imap-move;github.com/emersion/go-imap-quota;github.com/emersion/go-imap-specialuse;github.com/emersion/go-imap-unselect;github.com/emersion/go-mbox;github.com/emersion/go-message;github.com/emersion/go-sasl;github.com/emersion/go-smtp;github.com/emersion/go-textwrapper;github.com/emersion/go-vcard;github.com/fatih/color;github.com/flynn-archive/go-shlex;github.com/getsentry/raven-go;github.com/go-resty/resty/v2;github.com/golang/mock;github.com/google/go-cmp;github.com/google/uuid;github.com/gopherjs/gopherjs;github.com/hashicorp/go-multierror;github.com/jameskeane/bcrypt;github.com/jaytaylor/html2text;github.com/kardianos/osext;github.com/keybase/go-keychain;github.com/logrusorgru/aurora;github.com/mattn/go-runewidth;github.com/miekg/dns;github.com/myesui/uuid;github.com/nsf/jsondiff;github.com/olekukonko/tablewriter;github.com/pkg/errors;github.com/sirupsen/logrus;github.com/skratchdot/open-golang;github.com/ssor/bom;github.com/stretchr/testify;github.com/therecipe/qt;github.com/twinj/uuid;github.com/urfave/cli;go.etcd.io/bbolt;golang.org/x/crypto;golang.org/x/net;golang.org/x/text;gopkg.in/stretchr/testify.v1;;Font Awesome 4.7.0;;Qt 5.13 by Qt group;"
|
|
||||||
30
internal/bridge/locations.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
func (b *Bridge) ProvideLogsPath() (string, error) {
|
||||||
|
return b.locations.ProvideLogsPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) GetLicenseFilePath() string {
|
||||||
|
return b.locations.GetLicenseFilePath()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) GetDependencyLicensesLink() string {
|
||||||
|
return b.locations.GetDependencyLicensesLink()
|
||||||
|
}
|
||||||
@ -1,35 +1,26 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
// Copyright (c) 2022 Proton AG
|
||||||
//
|
//
|
||||||
// This file is part of ProtonMail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// Code generated by ./release-notes.sh at 'Mon Sep 21 01:29:10 PM CEST 2020'. DO NOT EDIT.
|
// Code generated by ./release-notes.sh at 'Fri Jan 22 11:01:06 AM CET 2021'. DO NOT EDIT.
|
||||||
|
|
||||||
package bridge
|
package bridge
|
||||||
|
|
||||||
const ReleaseNotes = `• Bulletproofing against any potential data loss and/or duplication
|
const ReleaseNotes = `
|
||||||
• Performance improvements for handling attachments and non-standard formatting
|
|
||||||
• Better stability of the message parser
|
|
||||||
• Additional foreign encoding support for outgoing messages
|
|
||||||
• Complete refactor of the way messages are parsed to simplify code maintenance
|
|
||||||
• Improved User-Agent detection
|
|
||||||
• Added MacOS Big Sur compatibility
|
|
||||||
• Added persistent anonymous API cookies
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const ReleaseFixedBugs = `• Fixed rare mail loss when moving from Spam folder
|
const ReleaseFixedBugs = `• Fixed sending error caused by inconsistent use of upper and lower case in sender’s email address
|
||||||
• Limited log size
|
|
||||||
• Fixed Linux font issues (mouse hover).
|
|
||||||
`
|
`
|
||||||
|
|||||||
44
internal/bridge/settings.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import "github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
|
|
||||||
|
func (b *Bridge) Get(key settings.Key) string {
|
||||||
|
return b.settings.Get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) Set(key settings.Key, value string) {
|
||||||
|
b.settings.Set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) GetBool(key settings.Key) bool {
|
||||||
|
return b.settings.GetBool(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) SetBool(key settings.Key, value bool) {
|
||||||
|
b.settings.SetBool(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) GetInt(key settings.Key) int {
|
||||||
|
return b.settings.GetInt(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) SetInt(key settings.Key, value int) {
|
||||||
|
b.settings.SetInt(key, value)
|
||||||
|
}
|
||||||
@ -1,19 +1,19 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
// Copyright (c) 2022 Proton AG
|
||||||
//
|
//
|
||||||
// This file is part of ProtonMail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package bridge
|
package bridge
|
||||||
|
|
||||||
@ -21,49 +21,67 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/store"
|
"github.com/ProtonMail/proton-bridge/v2/internal/sentry"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/users"
|
"github.com/ProtonMail/proton-bridge/v2/internal/store"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/store/cache"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/listener"
|
"github.com/ProtonMail/proton-bridge/v2/internal/users"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/message"
|
||||||
)
|
)
|
||||||
|
|
||||||
type storeFactory struct {
|
type storeFactory struct {
|
||||||
config StoreFactoryConfiger
|
cacheProvider CacheProvider
|
||||||
panicHandler users.PanicHandler
|
sentryReporter *sentry.Reporter
|
||||||
clientManager users.ClientManager
|
panicHandler users.PanicHandler
|
||||||
eventListener listener.Listener
|
eventListener listener.Listener
|
||||||
storeCache *store.Cache
|
events *store.Events
|
||||||
|
cache cache.Cache
|
||||||
|
builder *message.Builder
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStoreFactory(
|
func newStoreFactory(
|
||||||
config StoreFactoryConfiger,
|
cacheProvider CacheProvider,
|
||||||
|
sentryReporter *sentry.Reporter,
|
||||||
panicHandler users.PanicHandler,
|
panicHandler users.PanicHandler,
|
||||||
clientManager users.ClientManager,
|
|
||||||
eventListener listener.Listener,
|
eventListener listener.Listener,
|
||||||
|
cache cache.Cache,
|
||||||
|
builder *message.Builder,
|
||||||
) *storeFactory {
|
) *storeFactory {
|
||||||
return &storeFactory{
|
return &storeFactory{
|
||||||
config: config,
|
cacheProvider: cacheProvider,
|
||||||
panicHandler: panicHandler,
|
sentryReporter: sentryReporter,
|
||||||
clientManager: clientManager,
|
panicHandler: panicHandler,
|
||||||
eventListener: eventListener,
|
eventListener: eventListener,
|
||||||
storeCache: store.NewCache(config.GetIMAPCachePath()),
|
events: store.NewEvents(cacheProvider.GetIMAPCachePath()),
|
||||||
|
cache: cache,
|
||||||
|
builder: builder,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates new store for given user.
|
// New creates new store for given user.
|
||||||
func (f *storeFactory) New(user store.BridgeUser) (*store.Store, error) {
|
func (f *storeFactory) New(user store.BridgeUser) (*store.Store, error) {
|
||||||
storePath := getUserStorePath(f.config.GetDBDir(), user.ID())
|
return store.New(
|
||||||
return store.New(f.panicHandler, user, f.clientManager, f.eventListener, storePath, f.storeCache)
|
f.sentryReporter,
|
||||||
|
f.panicHandler,
|
||||||
|
user,
|
||||||
|
f.eventListener,
|
||||||
|
f.cache,
|
||||||
|
f.builder,
|
||||||
|
getUserStorePath(f.cacheProvider.GetDBDir(), user.ID()),
|
||||||
|
f.events,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes all store files for given user.
|
// Remove removes all store files for given user.
|
||||||
func (f *storeFactory) Remove(userID string) error {
|
func (f *storeFactory) Remove(userID string) error {
|
||||||
storePath := getUserStorePath(f.config.GetDBDir(), userID)
|
return store.RemoveStore(
|
||||||
return store.RemoveStore(f.storeCache, storePath, userID)
|
f.events,
|
||||||
|
getUserStorePath(f.cacheProvider.GetDBDir(), userID),
|
||||||
|
userID,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getUserStorePath returns the file path of the store database for the given userID.
|
// getUserStorePath returns the file path of the store database for the given userID.
|
||||||
func getUserStorePath(storeDir string, userID string) (path string) {
|
func getUserStorePath(storeDir string, userID string) (path string) {
|
||||||
fileName := fmt.Sprintf("mailbox-%v.db", userID)
|
return filepath.Join(storeDir, fmt.Sprintf("mailbox-%v.db", userID))
|
||||||
return filepath.Join(storeDir, fileName)
|
|
||||||
}
|
}
|
||||||
|
|||||||
64
internal/bridge/tls.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
|
||||||
|
pkgTLS "github.com/ProtonMail/proton-bridge/v2/internal/config/tls"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
logrus "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *Bridge) GetTLSConfig() (*tls.Config, error) {
|
||||||
|
if !b.tls.HasCerts() {
|
||||||
|
if err := b.generateTLSCerts(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig, err := b.tls.GetConfig()
|
||||||
|
if err == nil {
|
||||||
|
return tlsConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.WithError(err).Error("Failed to load TLS config, regenerating certificates")
|
||||||
|
|
||||||
|
if err := b.generateTLSCerts(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.tls.GetConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) generateTLSCerts() error {
|
||||||
|
template, err := pkgTLS.NewTLSTemplate()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to generate TLS template")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.tls.GenerateCerts(template); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to generate TLS certs")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.tls.InstallCerts(); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to install TLS certs")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -1,38 +1,62 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
// Copyright (c) 2022 Proton AG
|
||||||
//
|
//
|
||||||
// This file is part of ProtonMail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package bridge
|
package bridge
|
||||||
|
|
||||||
import "github.com/ProtonMail/proton-bridge/internal/users"
|
import (
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
|
||||||
type Configer interface {
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
users.Configer
|
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||||
StoreFactoryConfiger
|
)
|
||||||
|
|
||||||
|
type Locator interface {
|
||||||
|
ProvideLogsPath() (string, error)
|
||||||
|
|
||||||
|
GetLicenseFilePath() string
|
||||||
|
GetDependencyLicensesLink() string
|
||||||
|
|
||||||
|
Clear() error
|
||||||
|
ClearUpdates() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type StoreFactoryConfiger interface {
|
type CacheProvider interface {
|
||||||
GetDBDir() string
|
|
||||||
GetIMAPCachePath() string
|
GetIMAPCachePath() string
|
||||||
|
GetDBDir() string
|
||||||
|
GetDefaultMessageCacheDir() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PreferenceProvider interface {
|
type SettingsProvider interface {
|
||||||
Get(key string) string
|
Get(key settings.Key) string
|
||||||
GetBool(key string) bool
|
Set(key settings.Key, value string)
|
||||||
SetBool(key string, val bool)
|
|
||||||
GetInt(key string) int
|
GetBool(key settings.Key) bool
|
||||||
Set(key string, value string)
|
SetBool(key settings.Key, val bool)
|
||||||
|
|
||||||
|
GetInt(key settings.Key) int
|
||||||
|
SetInt(key settings.Key, val int)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Updater interface {
|
||||||
|
Check() (updater.VersionInfo, error)
|
||||||
|
IsDowngrade(updater.VersionInfo) bool
|
||||||
|
InstallUpdate(updater.VersionInfo) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Versioner interface {
|
||||||
|
RemoveOtherVersions(*semver.Version) error
|
||||||
}
|
}
|
||||||
|
|||||||
26
internal/bridge/useragent.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
func (b *Bridge) GetCurrentUserAgent() string {
|
||||||
|
return b.userAgent.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) SetCurrentPlatform(platform string) {
|
||||||
|
b.userAgent.SetPlatform(platform)
|
||||||
|
}
|
||||||
116
internal/clientconfig/applemail.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package clientconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/useragent"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/mobileconfig"
|
||||||
|
"golang.org/x/sys/execabs"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
bigSurPreferencesPane = "/System/Library/PreferencePanes/Profiles.prefPane"
|
||||||
|
venturaPreferencesPane = "x-apple.systempreferences:com.apple.preferences.configurationprofiles"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AppleMail struct{}
|
||||||
|
|
||||||
|
func (c *AppleMail) Configure(
|
||||||
|
hostname string,
|
||||||
|
imapPort, smtpPort int,
|
||||||
|
imapSSL, smtpSSL bool,
|
||||||
|
username, addresses, password string,
|
||||||
|
) error {
|
||||||
|
mc := prepareMobileConfig(hostname, imapPort, smtpPort, imapSSL, smtpSSL, username, addresses, password)
|
||||||
|
|
||||||
|
confPath, err := saveConfigTemporarily(mc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if useragent.IsBigSurOrNewer() {
|
||||||
|
prefPane := bigSurPreferencesPane
|
||||||
|
|
||||||
|
if useragent.IsVenturaOrNewer() {
|
||||||
|
prefPane = venturaPreferencesPane
|
||||||
|
}
|
||||||
|
|
||||||
|
return execabs.Command("open", prefPane, confPath).Run() //nolint:gosec // G204 open command is safe, mobileconfig is generated by us
|
||||||
|
}
|
||||||
|
|
||||||
|
return execabs.Command("open", confPath).Run() //nolint:gosec // G204 open command is safe, mobileconfig is generated by us
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareMobileConfig(
|
||||||
|
hostname string,
|
||||||
|
imapPort, smtpPort int,
|
||||||
|
imapSSL, smtpSSL bool,
|
||||||
|
username, addresses, password string,
|
||||||
|
) *mobileconfig.Config {
|
||||||
|
return &mobileconfig.Config{
|
||||||
|
DisplayName: username,
|
||||||
|
EmailAddress: addresses,
|
||||||
|
Identifier: "protonmail " + username + strconv.FormatInt(time.Now().Unix(), 10),
|
||||||
|
IMAP: &mobileconfig.IMAP{
|
||||||
|
Hostname: hostname,
|
||||||
|
Port: imapPort,
|
||||||
|
TLS: imapSSL,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
SMTP: &mobileconfig.SMTP{
|
||||||
|
Hostname: hostname,
|
||||||
|
Port: smtpPort,
|
||||||
|
TLS: smtpSSL,
|
||||||
|
Username: username,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveConfigTemporarily(mc *mobileconfig.Config) (fname string, err error) {
|
||||||
|
dir, err := os.MkdirTemp("", "protonmail-autoconfig")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the temporary file is deleted.
|
||||||
|
go func() {
|
||||||
|
<-time.After(10 * time.Minute)
|
||||||
|
_ = os.RemoveAll(dir)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Make sure the file is only readable for the current user.
|
||||||
|
fname = filepath.Clean(filepath.Join(dir, "protonmail.mobileconfig"))
|
||||||
|
f, err := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, 0o600) //nolint:gosec
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = mc.WriteOut(f); err != nil {
|
||||||
|
_ = f.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = f.Close()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@ -1,86 +0,0 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
|
||||||
//
|
|
||||||
// This file is part of ProtonMail Bridge.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/constants"
|
|
||||||
"github.com/getsentry/raven-go"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
log = logrus.WithField("pkg", "cmd") //nolint[gochecknoglobals]
|
|
||||||
|
|
||||||
baseFlags = []cli.Flag{ //nolint[gochecknoglobals]
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "log-level, l",
|
|
||||||
Usage: "Set the log level (one of panic, fatal, error, warn, info, debug, debug-client, debug-server)"},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "cli, c",
|
|
||||||
Usage: "Use command line interface"},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "version-json, g",
|
|
||||||
Usage: "Generate json version file"},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "mem-prof, m",
|
|
||||||
Usage: "Generate memory profile"},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "cpu-prof, p",
|
|
||||||
Usage: "Generate CPU profile"},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Main sets up Sentry, filters out unwanted args, creates app and runs it.
|
|
||||||
func Main(appName, usage string, extraFlags []cli.Flag, run func(*cli.Context) error) {
|
|
||||||
if err := raven.SetDSN(constants.DSNSentry); err != nil {
|
|
||||||
log.WithError(err).Errorln("Can not setup sentry DSN")
|
|
||||||
}
|
|
||||||
raven.SetRelease(constants.Revision)
|
|
||||||
|
|
||||||
filterProcessSerialNumberFromArgs()
|
|
||||||
filterRestartNumberFromArgs()
|
|
||||||
|
|
||||||
app := newApp(appName, usage, extraFlags, run)
|
|
||||||
|
|
||||||
logrus.SetLevel(logrus.InfoLevel)
|
|
||||||
log.WithField("version", constants.Version).
|
|
||||||
WithField("revision", constants.Revision).
|
|
||||||
WithField("build", constants.BuildTime).
|
|
||||||
WithField("runtime", runtime.GOOS).
|
|
||||||
WithField("args", os.Args).
|
|
||||||
WithField("appName", app.Name).
|
|
||||||
Info("Run app")
|
|
||||||
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
|
||||||
log.Error("Program exited with error: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newApp(appName, usage string, extraFlags []cli.Flag, run func(*cli.Context) error) *cli.App {
|
|
||||||
app := cli.NewApp()
|
|
||||||
app.Name = appName
|
|
||||||
app.Usage = usage
|
|
||||||
app.Version = constants.BuildVersion
|
|
||||||
app.Flags = append(baseFlags, extraFlags...) //nolint[gocritic]
|
|
||||||
app.Action = run
|
|
||||||
return app
|
|
||||||
}
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
|
||||||
//
|
|
||||||
// This file is part of ProtonMail Bridge.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/frontend"
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/config"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// After how many crashes app gives up starting.
|
|
||||||
maxAllowedCrashes = 10
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// How many crashes happened so far in a row.
|
|
||||||
// It will be filled from args by `filterRestartNumberFromArgs`.
|
|
||||||
// Every call of `HandlePanic` will increase this number.
|
|
||||||
// Then it will be passed as argument to the next try by `RestartApp`.
|
|
||||||
numberOfCrashes = 0 //nolint[gochecknoglobals]
|
|
||||||
)
|
|
||||||
|
|
||||||
// filterRestartNumberFromArgs removes flag with a number how many restart we already did.
|
|
||||||
// See restartApp how that number is used.
|
|
||||||
func filterRestartNumberFromArgs() {
|
|
||||||
tmp := os.Args[:0]
|
|
||||||
for i, arg := range os.Args {
|
|
||||||
if !strings.HasPrefix(arg, "--restart_") {
|
|
||||||
tmp = append(tmp, arg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
numberOfCrashes, err = strconv.Atoi(os.Args[i][10:])
|
|
||||||
if err != nil {
|
|
||||||
numberOfCrashes = maxAllowedCrashes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
os.Args = tmp
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableRestart disables restart once `RestartApp` is called.
|
|
||||||
func DisableRestart() {
|
|
||||||
numberOfCrashes = maxAllowedCrashes
|
|
||||||
}
|
|
||||||
|
|
||||||
// RestartApp starts a new instance in background.
|
|
||||||
func RestartApp() {
|
|
||||||
if numberOfCrashes >= maxAllowedCrashes {
|
|
||||||
log.Error("Too many crashes")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if exeFile, err := os.Executable(); err == nil {
|
|
||||||
arguments := append(os.Args[1:], fmt.Sprintf("--restart_%d", numberOfCrashes))
|
|
||||||
cmd := exec.Command(exeFile, arguments...) //nolint[gosec]
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
cmd.Stdin = os.Stdin
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
log.Error("Restart failed: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PanicHandler defines HandlePanic which can be used anywhere in defer.
|
|
||||||
type PanicHandler struct {
|
|
||||||
AppName string
|
|
||||||
Config *config.Config
|
|
||||||
Err *error // Pointer to error of cli action.
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandlePanic should be called in defer to ensure restart of app after error.
|
|
||||||
func (ph *PanicHandler) HandlePanic() {
|
|
||||||
r := recover()
|
|
||||||
if r == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config.HandlePanic(ph.Config, fmt.Sprintf("Recover: %v", r))
|
|
||||||
frontend.HandlePanic(ph.AppName)
|
|
||||||
|
|
||||||
*ph.Err = cli.NewExitError("Panic and restart", 255)
|
|
||||||
numberOfCrashes++
|
|
||||||
log.Error("Restarting after panic")
|
|
||||||
RestartApp()
|
|
||||||
os.Exit(255)
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
|
||||||
//
|
|
||||||
// This file is part of ProtonMail Bridge.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import "github.com/ProtonMail/proton-bridge/internal/updates"
|
|
||||||
|
|
||||||
// GenerateVersionFiles writes a JSON file with details about current build.
|
|
||||||
// Those files are used for upgrading the app.
|
|
||||||
func GenerateVersionFiles(updates *updates.Updates, dir string) {
|
|
||||||
log.Info("Generating version files")
|
|
||||||
for _, goos := range []string{"windows", "darwin", "linux"} {
|
|
||||||
log.Debug("Generating JSON for ", goos)
|
|
||||||
if err := updates.CreateJSONAndSign(dir, goos); err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
70
internal/config/cache/cache.go
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package cache provides access to contents inside a cache directory.
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/files"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
dir, version string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(dir, version string) (*Cache, error) {
|
||||||
|
if err := os.MkdirAll(filepath.Join(dir, version), 0o700); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Cache{
|
||||||
|
dir: dir,
|
||||||
|
version: version,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDBDir returns folder for db files.
|
||||||
|
func (c *Cache) GetDBDir() string {
|
||||||
|
return c.getCurrentCacheDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultMessageCacheDir returns folder for cached messages files.
|
||||||
|
func (c *Cache) GetDefaultMessageCacheDir() string {
|
||||||
|
return filepath.Join(c.getCurrentCacheDir(), "messages")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIMAPCachePath returns path to file with IMAP status.
|
||||||
|
func (c *Cache) GetIMAPCachePath() string {
|
||||||
|
return filepath.Join(c.getCurrentCacheDir(), "user_info.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransferDir returns folder for import-export rules files.
|
||||||
|
func (c *Cache) GetTransferDir() string {
|
||||||
|
return c.getCurrentCacheDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveOldVersions removes any cache dirs that are not the current version.
|
||||||
|
func (c *Cache) RemoveOldVersions() error {
|
||||||
|
return files.Remove(c.dir).Except(c.getCurrentCacheDir()).Do()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) getCurrentCacheDir() string {
|
||||||
|
return filepath.Join(c.dir, c.version)
|
||||||
|
}
|
||||||
69
internal/config/cache/cache_test.go
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRemoveOldVersions(t *testing.T) {
|
||||||
|
dir, err := os.MkdirTemp("", "test-cache")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
cache, err := New(dir, "c4")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
createFilesInDir(t, dir,
|
||||||
|
"unexpected1.txt",
|
||||||
|
"c1/unexpected1.txt",
|
||||||
|
"c2/unexpected2.txt",
|
||||||
|
"c3/unexpected3.txt",
|
||||||
|
"something.txt",
|
||||||
|
)
|
||||||
|
|
||||||
|
require.DirExists(t, filepath.Join(dir, "c4"))
|
||||||
|
require.FileExists(t, filepath.Join(dir, "unexpected1.txt"))
|
||||||
|
require.FileExists(t, filepath.Join(dir, "c1", "unexpected1.txt"))
|
||||||
|
require.FileExists(t, filepath.Join(dir, "c2", "unexpected2.txt"))
|
||||||
|
require.FileExists(t, filepath.Join(dir, "c3", "unexpected3.txt"))
|
||||||
|
require.FileExists(t, filepath.Join(dir, "something.txt"))
|
||||||
|
|
||||||
|
assert.NoError(t, cache.RemoveOldVersions())
|
||||||
|
|
||||||
|
assert.DirExists(t, filepath.Join(dir, "c4"))
|
||||||
|
assert.NoFileExists(t, filepath.Join(dir, "unexpected1.txt"))
|
||||||
|
assert.NoFileExists(t, filepath.Join(dir, "c1", "unexpected1.txt"))
|
||||||
|
assert.NoFileExists(t, filepath.Join(dir, "c2", "unexpected2.txt"))
|
||||||
|
assert.NoFileExists(t, filepath.Join(dir, "c3", "unexpected3.txt"))
|
||||||
|
assert.NoFileExists(t, filepath.Join(dir, "something.txt"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFilesInDir(t *testing.T, dir string, files ...string) {
|
||||||
|
for _, target := range files {
|
||||||
|
require.NoError(t, os.MkdirAll(filepath.Dir(filepath.Join(dir, target)), 0o700))
|
||||||
|
|
||||||
|
f, err := os.Create(filepath.Join(dir, target))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, f.Close())
|
||||||
|
}
|
||||||
|
}
|
||||||
151
internal/config/settings/kvs.go
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type keyValueStore struct {
|
||||||
|
vals map[Key]string
|
||||||
|
path string
|
||||||
|
lock *sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// newKeyValueStore returns loaded preferences.
|
||||||
|
func newKeyValueStore(path string) *keyValueStore {
|
||||||
|
p := &keyValueStore{
|
||||||
|
path: path,
|
||||||
|
lock: &sync.RWMutex{},
|
||||||
|
}
|
||||||
|
if err := p.load(); err != nil {
|
||||||
|
logrus.WithError(err).Warn("Cannot load preferences file, creating new one")
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *keyValueStore) load() error {
|
||||||
|
if p.vals != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
|
||||||
|
p.vals = make(map[Key]string)
|
||||||
|
|
||||||
|
f, err := os.Open(p.path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close() //nolint:errcheck,gosec
|
||||||
|
|
||||||
|
return json.NewDecoder(f).Decode(&p.vals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *keyValueStore) save() error {
|
||||||
|
if p.vals == nil {
|
||||||
|
return errors.New("cannot save preferences: cache is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
|
||||||
|
b, err := json.MarshalIndent(p.vals, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.WriteFile(p.path, b, 0o600)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *keyValueStore) setDefault(key Key, value string) {
|
||||||
|
if p.Get(key) == "" {
|
||||||
|
p.Set(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *keyValueStore) Get(key Key) string {
|
||||||
|
p.lock.RLock()
|
||||||
|
defer p.lock.RUnlock()
|
||||||
|
|
||||||
|
return p.vals[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *keyValueStore) GetBool(key Key) bool {
|
||||||
|
return p.Get(key) == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *keyValueStore) GetInt(key Key) int {
|
||||||
|
if p.Get(key) == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := strconv.Atoi(p.Get(key))
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Error("Cannot parse int")
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *keyValueStore) GetFloat64(key Key) float64 {
|
||||||
|
if p.Get(key) == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := strconv.ParseFloat(p.Get(key), 64)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Error("Cannot parse float64")
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *keyValueStore) Set(key Key, value string) {
|
||||||
|
p.lock.Lock()
|
||||||
|
p.vals[key] = value
|
||||||
|
p.lock.Unlock()
|
||||||
|
|
||||||
|
if err := p.save(); err != nil {
|
||||||
|
logrus.WithError(err).Warn("Cannot save preferences")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *keyValueStore) SetBool(key Key, value bool) {
|
||||||
|
if value {
|
||||||
|
p.Set(key, "true")
|
||||||
|
} else {
|
||||||
|
p.Set(key, "false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *keyValueStore) SetInt(key Key, value int) {
|
||||||
|
p.Set(key, strconv.Itoa(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *keyValueStore) SetFloat64(key Key, value float64) {
|
||||||
|
p.Set(key, fmt.Sprintf("%v", value))
|
||||||
|
}
|
||||||
141
internal/config/settings/kvs_test.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadNoKeyValueStore(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
pref, clean := newTestEmptyKeyValueStore(r)
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
r.Equal("", pref.Get("key"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadBadKeyValueStore(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
path, clean := newTmpFile(r)
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
r.NoError(os.WriteFile(path, []byte("{\"key\":\"MISSING_QUOTES"), 0o700))
|
||||||
|
pref := newKeyValueStore(path)
|
||||||
|
r.Equal("", pref.Get("key"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyValueStor(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
pref, clean := newTestKeyValueStore(r)
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
r.Equal("value", pref.Get("str"))
|
||||||
|
r.Equal("42", pref.Get("int"))
|
||||||
|
r.Equal("true", pref.Get("bool"))
|
||||||
|
r.Equal("t", pref.Get("falseBool"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyValueStoreGetInt(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
pref, clean := newTestKeyValueStore(r)
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
r.Equal(0, pref.GetInt("str"))
|
||||||
|
r.Equal(42, pref.GetInt("int"))
|
||||||
|
r.Equal(0, pref.GetInt("bool"))
|
||||||
|
r.Equal(0, pref.GetInt("falseBool"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyValueStoreGetBool(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
pref, clean := newTestKeyValueStore(r)
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
r.Equal(false, pref.GetBool("str"))
|
||||||
|
r.Equal(false, pref.GetBool("int"))
|
||||||
|
r.Equal(true, pref.GetBool("bool"))
|
||||||
|
r.Equal(false, pref.GetBool("falseBool"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyValueStoreSetDefault(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
pref, clean := newTestEmptyKeyValueStore(r)
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
pref.setDefault("key", "value")
|
||||||
|
pref.setDefault("key", "othervalue")
|
||||||
|
r.Equal("value", pref.Get("key"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyValueStoreSet(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
pref, clean := newTestEmptyKeyValueStore(r)
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
pref.Set("str", "value")
|
||||||
|
checkSavedKeyValueStore(r, pref.path, "{\n\t\"str\": \"value\"\n}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyValueStoreSetInt(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
pref, clean := newTestEmptyKeyValueStore(r)
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
pref.SetInt("int", 42)
|
||||||
|
checkSavedKeyValueStore(r, pref.path, "{\n\t\"int\": \"42\"\n}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyValueStoreSetBool(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
pref, clean := newTestEmptyKeyValueStore(r)
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
pref.SetBool("trueBool", true)
|
||||||
|
pref.SetBool("falseBool", false)
|
||||||
|
checkSavedKeyValueStore(r, pref.path, "{\n\t\"falseBool\": \"false\",\n\t\"trueBool\": \"true\"\n}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTmpFile(r *require.Assertions) (path string, clean func()) {
|
||||||
|
tmpfile, err := os.CreateTemp("", "pref.*.json")
|
||||||
|
r.NoError(err)
|
||||||
|
defer r.NoError(tmpfile.Close())
|
||||||
|
|
||||||
|
return tmpfile.Name(), func() {
|
||||||
|
r.NoError(os.Remove(tmpfile.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestEmptyKeyValueStore(r *require.Assertions) (*keyValueStore, func()) {
|
||||||
|
path, clean := newTmpFile(r)
|
||||||
|
return newKeyValueStore(path), clean
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestKeyValueStore(r *require.Assertions) (*keyValueStore, func()) {
|
||||||
|
path, clean := newTmpFile(r)
|
||||||
|
r.NoError(os.WriteFile(path, []byte("{\"str\":\"value\",\"int\":\"42\",\"bool\":\"true\",\"falseBool\":\"t\"}"), 0o700))
|
||||||
|
return newKeyValueStore(path), clean
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSavedKeyValueStore(r *require.Assertions, path, expected string) {
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
r.NoError(err)
|
||||||
|
r.Equal(expected, string(data))
|
||||||
|
}
|
||||||
116
internal/config/settings/settings.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package settings provides access to persistent user settings.
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Key string
|
||||||
|
|
||||||
|
// Keys of preferences in JSON file.
|
||||||
|
const (
|
||||||
|
FirstStartKey Key = "first_time_start"
|
||||||
|
FirstStartGUIKey Key = "first_time_start_gui"
|
||||||
|
LastHeartbeatKey Key = "last_heartbeat"
|
||||||
|
APIPortKey Key = "user_port_api"
|
||||||
|
IMAPPortKey Key = "user_port_imap"
|
||||||
|
SMTPPortKey Key = "user_port_smtp"
|
||||||
|
SMTPSSLKey Key = "user_ssl_smtp"
|
||||||
|
AllowProxyKey Key = "allow_proxy"
|
||||||
|
AutostartKey Key = "autostart"
|
||||||
|
AutoUpdateKey Key = "autoupdate"
|
||||||
|
CookiesKey Key = "cookies"
|
||||||
|
LastVersionKey Key = "last_used_version"
|
||||||
|
UpdateChannelKey Key = "update_channel"
|
||||||
|
RolloutKey Key = "rollout"
|
||||||
|
PreferredKeychainKey Key = "preferred_keychain"
|
||||||
|
CacheEnabledKey Key = "cache_enabled"
|
||||||
|
CacheCompressionKey Key = "cache_compression"
|
||||||
|
CacheLocationKey Key = "cache_location"
|
||||||
|
CacheMinFreeAbsKey Key = "cache_min_free_abs"
|
||||||
|
CacheMinFreeRatKey Key = "cache_min_free_rat"
|
||||||
|
CacheConcurrencyRead Key = "cache_concurrent_read"
|
||||||
|
CacheConcurrencyWrite Key = "cache_concurrent_write"
|
||||||
|
IMAPWorkers Key = "imap_workers"
|
||||||
|
FetchWorkers Key = "fetch_workers"
|
||||||
|
AttachmentWorkers Key = "attachment_workers"
|
||||||
|
ColorScheme Key = "color_scheme"
|
||||||
|
RebrandingMigrationKey Key = "rebranding_migrated"
|
||||||
|
IsAllMailVisible Key = "is_all_mail_visible"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Settings struct {
|
||||||
|
*keyValueStore
|
||||||
|
|
||||||
|
settingsPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(settingsPath string) *Settings {
|
||||||
|
s := &Settings{
|
||||||
|
keyValueStore: newKeyValueStore(filepath.Join(settingsPath, "prefs.json")),
|
||||||
|
settingsPath: settingsPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.setDefaultValues()
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultIMAPPort = "1143"
|
||||||
|
DefaultSMTPPort = "1025"
|
||||||
|
DefaultAPIPort = "1042"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Settings) setDefaultValues() {
|
||||||
|
s.setDefault(FirstStartKey, "true")
|
||||||
|
s.setDefault(FirstStartGUIKey, "true")
|
||||||
|
s.setDefault(LastHeartbeatKey, fmt.Sprintf("%v", time.Now().YearDay()))
|
||||||
|
s.setDefault(AllowProxyKey, "true")
|
||||||
|
s.setDefault(AutostartKey, "true")
|
||||||
|
s.setDefault(AutoUpdateKey, "true")
|
||||||
|
s.setDefault(LastVersionKey, "")
|
||||||
|
s.setDefault(UpdateChannelKey, "")
|
||||||
|
s.setDefault(RolloutKey, fmt.Sprintf("%v", rand.Float64())) //nolint:gosec // G404 It is OK to use weak random number generator here
|
||||||
|
s.setDefault(PreferredKeychainKey, "")
|
||||||
|
s.setDefault(CacheEnabledKey, "true")
|
||||||
|
s.setDefault(CacheCompressionKey, "true")
|
||||||
|
s.setDefault(CacheLocationKey, "")
|
||||||
|
s.setDefault(CacheMinFreeAbsKey, "250000000")
|
||||||
|
s.setDefault(CacheMinFreeRatKey, "")
|
||||||
|
s.setDefault(CacheConcurrencyRead, "16")
|
||||||
|
s.setDefault(CacheConcurrencyWrite, "16")
|
||||||
|
s.setDefault(IMAPWorkers, "16")
|
||||||
|
s.setDefault(FetchWorkers, "16")
|
||||||
|
s.setDefault(AttachmentWorkers, "16")
|
||||||
|
s.setDefault(ColorScheme, "")
|
||||||
|
|
||||||
|
s.setDefault(APIPortKey, DefaultAPIPort)
|
||||||
|
s.setDefault(IMAPPortKey, DefaultIMAPPort)
|
||||||
|
s.setDefault(SMTPPortKey, DefaultSMTPPort)
|
||||||
|
|
||||||
|
// By default, stick to STARTTLS. If the user uses catalina+applemail they'll have to change to SSL.
|
||||||
|
s.setDefault(SMTPSSLKey, "false")
|
||||||
|
|
||||||
|
s.setDefault(IsAllMailVisible, "true")
|
||||||
|
}
|
||||||
53
internal/config/tls/cert_store_darwin.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package tls
|
||||||
|
|
||||||
|
import "golang.org/x/sys/execabs"
|
||||||
|
|
||||||
|
func addTrustedCert(certPath string) error {
|
||||||
|
return execabs.Command( //nolint:gosec
|
||||||
|
"/usr/bin/security",
|
||||||
|
"execute-with-privileges",
|
||||||
|
"/usr/bin/security",
|
||||||
|
"add-trusted-cert",
|
||||||
|
"-d",
|
||||||
|
"-r", "trustRoot",
|
||||||
|
"-p", "ssl",
|
||||||
|
"-k", "/Library/Keychains/System.keychain",
|
||||||
|
certPath,
|
||||||
|
).Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeTrustedCert(certPath string) error {
|
||||||
|
return execabs.Command( //nolint:gosec
|
||||||
|
"/usr/bin/security",
|
||||||
|
"execute-with-privileges",
|
||||||
|
"/usr/bin/security",
|
||||||
|
"remove-trusted-cert",
|
||||||
|
"-d",
|
||||||
|
certPath,
|
||||||
|
).Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TLS) InstallCerts() error {
|
||||||
|
return addTrustedCert(t.getTLSCertPath())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TLS) UninstallCerts() error {
|
||||||
|
return removeTrustedCert(t.getTLSCertPath())
|
||||||
|
}
|
||||||
26
internal/config/tls/cert_store_linux.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package tls
|
||||||
|
|
||||||
|
func (t *TLS) InstallCerts() error {
|
||||||
|
return nil // Linux doesn't have a root cert store.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TLS) UninstallCerts() error {
|
||||||
|
return nil // Linux doesn't have a root cert store.
|
||||||
|
}
|
||||||
26
internal/config/tls/cert_store_windows.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package tls
|
||||||
|
|
||||||
|
func (t *TLS) InstallCerts() error {
|
||||||
|
return nil // NOTE(GODT-986): Install certs to root cert store?
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TLS) UninstallCerts() error {
|
||||||
|
return nil // NOTE(GODT-986): Uninstall certs from root cert store?
|
||||||
|
}
|
||||||
194
internal/config/tls/tls.go
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TLS struct {
|
||||||
|
settingsPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(settingsPath string) *TLS {
|
||||||
|
return &TLS{
|
||||||
|
settingsPath: settingsPath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTLSTemplate creates a new TLS template certificate with a random serial number.
|
||||||
|
func NewTLSTemplate() (*x509.Certificate, error) {
|
||||||
|
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to generate serial number")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &x509.Certificate{
|
||||||
|
SerialNumber: serialNumber,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
Country: []string{"CH"},
|
||||||
|
Organization: []string{"Proton AG"},
|
||||||
|
OrganizationalUnit: []string{"Proton Mail"},
|
||||||
|
CommonName: "127.0.0.1",
|
||||||
|
},
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
IsCA: true,
|
||||||
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().Add(20 * 365 * 24 * time.Hour),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPEMKeyPair return a new TLS private key and certificate in PEM encoded format.
|
||||||
|
func NewPEMKeyPair() (pemCert, pemKey []byte, err error) {
|
||||||
|
template, err := NewTLSTemplate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrap(err, "failed to generate TLS template")
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrap(err, "failed to generate private key")
|
||||||
|
}
|
||||||
|
|
||||||
|
pemKey = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
||||||
|
|
||||||
|
derBytes, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrap(err, "failed to create certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
pemCert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||||
|
|
||||||
|
return pemCert, pemKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrTLSCertExpiresSoon = fmt.Errorf("TLS certificate will expire soon")
|
||||||
|
|
||||||
|
// getTLSCertPath returns path to certificate; used for TLS servers (IMAP, SMTP).
|
||||||
|
func (t *TLS) getTLSCertPath() string {
|
||||||
|
return filepath.Join(t.settingsPath, "cert.pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTLSKeyPath returns path to private key; used for TLS servers (IMAP, SMTP).
|
||||||
|
func (t *TLS) getTLSKeyPath() string {
|
||||||
|
return filepath.Join(t.settingsPath, "key.pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasCerts returns whether TLS certs have been generated.
|
||||||
|
func (t *TLS) HasCerts() bool {
|
||||||
|
if _, err := os.Stat(t.getTLSCertPath()); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(t.getTLSKeyPath()); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateCerts generates certs from the given template.
|
||||||
|
func (t *TLS) GenerateCerts(template *x509.Certificate) error {
|
||||||
|
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to generate private key")
|
||||||
|
}
|
||||||
|
|
||||||
|
derBytes, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to create certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
certOut, err := os.Create(t.getTLSCertPath())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer certOut.Close() //nolint:errcheck,gosec
|
||||||
|
|
||||||
|
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
keyOut, err := os.OpenFile(t.getTLSKeyPath(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer keyOut.Close() //nolint:errcheck,gosec
|
||||||
|
|
||||||
|
return pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConfig tries to load TLS config or generate new one which is then returned.
|
||||||
|
func (t *TLS) GetConfig() (*tls.Config, error) {
|
||||||
|
c, err := tls.LoadX509KeyPair(t.getTLSCertPath(), t.getTLSKeyPath())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to load keypair")
|
||||||
|
}
|
||||||
|
|
||||||
|
return getConfigFromKeyPair(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConfigFromPEMKeyPair load a TLS config from PEM encoded certificate and key.
|
||||||
|
func GetConfigFromPEMKeyPair(permCert, pemKey []byte) (*tls.Config, error) {
|
||||||
|
c, err := tls.X509KeyPair(permCert, pemKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to load keypair")
|
||||||
|
}
|
||||||
|
|
||||||
|
return getConfigFromKeyPair(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfigFromKeyPair(c tls.Certificate) (*tls.Config, error) {
|
||||||
|
var err error
|
||||||
|
c.Leaf, err = x509.ParseCertificate(c.Certificate[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to parse certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
if time.Now().Add(31 * 24 * time.Hour).After(c.Leaf.NotAfter) {
|
||||||
|
return nil, ErrTLSCertExpiresSoon
|
||||||
|
}
|
||||||
|
|
||||||
|
caCertPool := x509.NewCertPool()
|
||||||
|
caCertPool.AddCert(c.Leaf)
|
||||||
|
|
||||||
|
//nolint:gosec // We need to support older TLS versions for AppleMail and Outlook
|
||||||
|
return &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{c},
|
||||||
|
ServerName: "127.0.0.1",
|
||||||
|
ClientAuth: tls.VerifyClientCertIfGiven,
|
||||||
|
RootCAs: caCertPool,
|
||||||
|
ClientCAs: caCertPool,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
85
internal/config/tls/tls_test.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetOldConfig(t *testing.T) {
|
||||||
|
dir, err := os.MkdirTemp("", "test-tls")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create new tls object.
|
||||||
|
tls := New(dir)
|
||||||
|
|
||||||
|
// Create new TLS template.
|
||||||
|
tlsTemplate, err := NewTLSTemplate()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Make the template be an old key.
|
||||||
|
tlsTemplate.NotBefore = time.Now().Add(-365 * 24 * time.Hour)
|
||||||
|
tlsTemplate.NotAfter = time.Now()
|
||||||
|
|
||||||
|
// Generate the certs from the template.
|
||||||
|
require.NoError(t, tls.GenerateCerts(tlsTemplate))
|
||||||
|
|
||||||
|
// Generate the config from the certs -- it's going to expire soon so we don't want to use it.
|
||||||
|
_, err = tls.GetConfig()
|
||||||
|
require.Equal(t, err, ErrTLSCertExpiresSoon)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetValidConfig(t *testing.T) {
|
||||||
|
dir, err := os.MkdirTemp("", "test-tls")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create new tls object.
|
||||||
|
tls := New(dir)
|
||||||
|
|
||||||
|
// Create new TLS template.
|
||||||
|
tlsTemplate, err := NewTLSTemplate()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Make the template be a new key.
|
||||||
|
tlsTemplate.NotBefore = time.Now()
|
||||||
|
tlsTemplate.NotAfter = time.Now().Add(2 * 365 * 24 * time.Hour)
|
||||||
|
|
||||||
|
// Generate the certs from the template.
|
||||||
|
require.NoError(t, tls.GenerateCerts(tlsTemplate))
|
||||||
|
|
||||||
|
// Generate the config from the certs -- it's not going to expire soon so we want to use it.
|
||||||
|
config, err := tls.GetConfig()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, len(config.Certificates), 1)
|
||||||
|
|
||||||
|
// Check the cert is valid.
|
||||||
|
now, notValidAfter := time.Now(), config.Certificates[0].Leaf.NotAfter
|
||||||
|
require.False(t, now.After(notValidAfter), "new certificate expected to be valid at %v but have valid until %v", now, notValidAfter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewConfig(t *testing.T) {
|
||||||
|
pemCert, pemKey, err := NewPEMKeyPair()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = GetConfigFromPEMKeyPair(pemCert, pemKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
66
internal/config/useragent/platform.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package useragent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsCatalinaOrNewer checks whether the host is macOS Catalina 10.15.x or higher.
|
||||||
|
func IsCatalinaOrNewer() bool {
|
||||||
|
return isThisDarwinNewerOrEqual(getMinCatalina())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBigSurOrNewer checks whether the host is macOS BigSur 10.16.x or higher.
|
||||||
|
func IsBigSurOrNewer() bool {
|
||||||
|
return isThisDarwinNewerOrEqual(getMinBigSur())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsVenturaOrNewer checks whether the host is macOS BigSur 13.x or higher.
|
||||||
|
func IsVenturaOrNewer() bool {
|
||||||
|
return isThisDarwinNewerOrEqual(getMinVentura())
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMinCatalina() *semver.Version { return semver.MustParse("19.0.0") }
|
||||||
|
func getMinBigSur() *semver.Version { return semver.MustParse("20.0.0") }
|
||||||
|
func getMinVentura() *semver.Version { return semver.MustParse("22.0.0") }
|
||||||
|
|
||||||
|
func isThisDarwinNewerOrEqual(minVersion *semver.Version) bool {
|
||||||
|
if runtime.GOOS != "darwin" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
rawVersion, err := getDarwinVersion()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return isVersionEqualOrNewer(minVersion, strings.TrimSpace(rawVersion))
|
||||||
|
}
|
||||||
|
|
||||||
|
// isVersionEqualOrNewer is separated to be able to run test on other than darwin.
|
||||||
|
func isVersionEqualOrNewer(minVersion *semver.Version, rawVersion string) bool {
|
||||||
|
semVersion, err := semver.NewVersion(rawVersion)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return semVersion.GreaterThan(minVersion) || semVersion.Equal(minVersion)
|
||||||
|
}
|
||||||
@ -1,23 +1,29 @@
|
|||||||
// Copyright (c) 2020 Proton Technologies AG
|
// Copyright (c) 2022 Proton AG
|
||||||
//
|
//
|
||||||
// This file is part of ProtonMail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package pmapi
|
//go:build darwin
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
// DANGEROUSLYSetUID SHOULD NOT be used!!! This is only for testing purposes.
|
package useragent
|
||||||
func (s *Auth) DANGEROUSLYSetUID(uid string) {
|
|
||||||
s.uid = uid
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getDarwinVersion() (string, error) {
|
||||||
|
return syscall.Sysctl("kern.osrelease")
|
||||||
}
|
}
|
||||||
27
internal/config/useragent/platform_default.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//go:build !darwin
|
||||||
|
// +build !darwin
|
||||||
|
|
||||||
|
package useragent
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
func getDarwinVersion() (string, error) {
|
||||||
|
return "", errors.New("implemented only for darwin")
|
||||||
|
}
|
||||||