Compare commits

..
734 Commits
Author SHA1 Message Date
marc 43d5279acf package.json 2021-12-23 13:30:37 +01:00
Dimitri Witkowski e674607fce Merge pull request #1945 from dolfinus/develop
Install NativeMessagingHost for Chromium and Chrome dev/canary
2021-12-20 22:01:39 +01:00
dolfinus 2e6239f637 Install NativeMessagingHost for Chromium and Chrome dev/canary 2021-12-06 00:53:23 +03:00
antelle bcb8ddc17b fixed msteams html file name 2021-07-19 23:21:43 +02:00
antelle cd619582ec fixed variable usage 2021-07-13 19:05:25 +02:00
antelle 3b49562f64 added MS Teams apps 2021-07-13 19:04:27 +02:00
antelle 674ed2cc72 renames some ms teams constants 2021-07-11 16:22:38 +02:00
Dimitri Witkowski 5621fd8a06 Merge pull request #1856 from saulobejo/develop
added teams storage; added tenant id configuration for microsot
2021-07-11 16:18:53 +02:00
Saulo Alves af27e0b111 renamed teams to microsoft teams; fixed grunt prettier error in onedrive 2021-07-11 10:52:40 +02:00
Saulo Alves 5a78e12f7d added teams storage; added tenant id configuration for onedrive and teams 2021-07-11 00:56:51 +02:00
antelle 47b19d1d74 Merge branch 'master' into develop 2021-05-19 10:42:24 +02:00
antelle 070573e68a Merge branch 'release-1.18'
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-05-19 10:35:35 +02:00
antelle 946d7769f1 bump version 2021-05-19 10:33:41 +02:00
antelle f0426a3ec2 fix #1820: minimize on close on macOS 2021-05-19 10:33:17 +02:00
antelle b2d67818a8 fix #1818: extension connection error if browser cannot be identified 2021-05-19 10:16:40 +02:00
antelle c0b159d824 fix #1824: saving KDBX3 files with compression disabled 2021-05-19 10:07:59 +02:00
antelle d7e11e20c1 Merge branch 'master' into develop 2021-05-14 21:18:46 +02:00
antelle d590318153 Merge branch 'release-1.18'
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-05-14 21:02:12 +02:00
antelle 5816ca9c4d bump version 2021-05-14 21:01:43 +02:00
antelle 6627e8c0ec fix #1817: crash on files with large attachments as KDBX3 2021-05-14 21:00:43 +02:00
antelle 379e28e8a4 about version in the the bug report template 2021-05-14 20:56:48 +02:00
antelle 4fb25451a2 fix #1816: old Chromium support, such as Android Edge 2021-05-13 10:45:48 +02:00
antelle 58a6a97130 Merge branch 'master' into develop 2021-05-12 21:43:27 +02:00
antelle f59b846643 Merge branch 'release-1.18'
Build / linux (push) Canceled after 0s
Build / web (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-05-12 21:40:25 +02:00
antelle 28d7dfeb67 bump version 2021-05-12 21:39:47 +02:00
antelle 6cc39d2107 fix #1808: restore KeeWeb from system tray on extension request 2021-05-12 21:38:54 +02:00
antelle a92a01095b fix #1814: option to disable auto-type title filter by default 2021-05-12 21:00:51 +02:00
antelle 3f4cddd2a2 fix #1811: app doesn't quit during update 2021-05-12 20:50:24 +02:00
antelle aa734ea976 fix #1813: fixed custom icon selection 2021-05-11 18:50:37 +02:00
antelle 30628004fd https link in the readme 2021-05-10 20:40:38 +02:00
antelle a6e87b52fd fix #1810: fixed the extension on old macOS starting 10.11 2021-05-10 20:40:30 +02:00
antelle 6d06d825e5 fixed donate button border 2021-05-10 18:12:11 +02:00
antelle 0800d5f33a Merge branch 'master' into develop 2021-05-09 23:25:27 +02:00
antelle 337ef44b56 Merge branch 'release-1.18'
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-05-09 14:32:23 +02:00
antelle 70b4effde8 fix #1806: fixed a possible config loading error during startup 2021-05-09 14:31:58 +02:00
antelle 9e4c0f5c47 Merge branch 'release-1.18' 2021-05-09 14:12:50 +02:00
antelle 535cdc72f7 fix #1805: auto-unchecking auto-type filters if nothing found 2021-05-09 14:07:22 +02:00
antelle 8d258ecab2 fix #1804: entry table sizing in Firefox 2021-05-09 10:17:55 +02:00
antelle 3079e28f6f bump version 2021-05-09 10:09:26 +02:00
antelle 4970290614 fix #1804: filling OTP in browser extensions 2021-05-09 10:08:59 +02:00
antelle 6929931cae Merge branch 'master' into develop 2021-05-09 09:04:12 +02:00
antelle e348310602 Merge branch 'release-1.18'
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-05-08 22:33:52 +02:00
antelle 7abbd2f253 bump version 2021-05-08 22:32:53 +02:00
antelle c56698492e fix #1802: opening files with saved keyfiles 2021-05-08 22:32:35 +02:00
antelle bc4c33fcbb Merge branch 'release-1.18'
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-05-08 21:02:32 +02:00
antelle 05d5ac960c bump version 2021-05-08 21:01:00 +02:00
antelle 80331401b5 Merge branch 'master' into release-1.18 2021-05-08 21:00:47 +02:00
antelle b4459e1247 fixed entry attachments display 2021-05-08 21:00:38 +02:00
antelle 706c82d3ca fixed Windows code signing
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-05-08 19:57:02 +02:00
antelle 0e1b086096 Merge branch 'develop' 2021-05-08 19:37:01 +02:00
antelle 80359c98f5 bump version 2021-05-08 19:35:51 +02:00
antelle 034e0ec2e3 always write version with 2 spaces 2021-05-08 19:35:36 +02:00
antelle cc6839d13e fixed native messaging host mode 2021-05-08 17:48:30 +02:00
antelle 0ce436c2e9 fixed phonetic password generation 2021-05-08 17:13:10 +02:00
antelle 966d814281 fixed removing an extra url 2021-05-08 16:59:36 +02:00
antelle c983bc10ab fixed git tag name 2021-05-08 16:11:26 +02:00
antelle d603398da0 upgrading node.js on ci 2021-05-08 14:21:56 +02:00
antelle 1404d2688f ci bugfixes 2021-05-08 14:14:00 +02:00
antelle c72158cf0d release name 2021-05-08 14:12:05 +02:00
antelle 1e6ed1308f fix eslint 2021-05-08 13:50:25 +02:00
antelle fae241373a fixed tests 2021-05-08 13:46:08 +02:00
antelle 2bf4cd83df upgrading node.js 2021-05-08 13:45:14 +02:00
antelle efd3290ee8 upgraded github release publishing 2021-05-08 13:35:03 +02:00
antelle aa729d9af8 don't log extension events when not connected 2021-05-08 11:46:53 +02:00
antelle 2d6d3658a1 fixed an error during exit without launcher 2021-05-08 11:42:08 +02:00
antelle 0cbdd6281a kdbxweb v2 2021-05-08 11:38:23 +02:00
antelle a845a42d0d up deps 2021-05-08 10:56:49 +02:00
antelle 60329e086a translated application menu on macOS 2021-05-08 10:37:24 +02:00
antelle a3174b96d7 up languages 2021-05-08 09:57:33 +02:00
antelle 29c00066bc Safari extension link 2021-05-08 09:53:57 +02:00
antelle b90ffc4d3f fix #1794: update installation on Windows when minimize instead of close is enabled 2021-05-06 20:44:09 +02:00
antelle 7e030320f5 option to auto-save on file change, fix #1364 2021-05-06 20:13:58 +02:00
antelle c96bcad32d release notes formatting 2021-05-05 19:56:29 +02:00
antelle a655cce195 removed file version fix 2021-05-05 18:49:24 +02:00
antelle aafb0e7964 editorconfig 2021-05-04 17:18:26 +02:00
antelle 288f7f117f removed an http link 2021-05-02 20:51:32 +02:00
antelle 125ac2eda8 wording 2021-05-02 20:43:44 +02:00
antelle ea80bfae07 fix #keeweb/keeweb-connect#3: extension manifest file name 2021-05-01 19:20:14 +02:00
antelle 5e727a3547 fixed native messaging host permissions 2021-04-30 10:16:30 +02:00
antelle 6672b60d0f fix #1779: don't copy passwords from the inline genertor in fields 2021-04-29 14:20:36 +02:00
antelle 09fe1a4649 fix #1780: custom icon selection issue 2021-04-29 14:16:39 +02:00
antelle e4fa4cc413 fixed selecting password as custom field 2021-04-29 14:02:42 +02:00
antelle 0be60475cf selecting other fields for browser extensions 2021-04-29 12:55:00 +02:00
antelle b7e53185b0 returning otp codes 2021-04-29 12:16:53 +02:00
antelle cd8c76b3eb up deps 2021-04-29 11:30:01 +02:00
antelle a1d7a1fe48 ignoring errors during usb init 2021-04-29 10:15:41 +02:00
antelle c48153819f checking all urls in the auto-type search 2021-04-29 09:18:32 +02:00
antelle d2d5590887 replaced a constant 2021-04-29 08:31:37 +02:00
antelle 65b319e127 extension ids constants 2021-04-29 08:29:33 +02:00
antelle b34c0dd34e ignoring custom websites in the selectiong pop-up 2021-04-28 23:47:56 +02:00
antelle ddc1a371bf increased the timeout 2021-04-28 23:40:37 +02:00
antelle a0c0b7b195 cancel button in the entry selection view 2021-04-28 23:37:15 +02:00
antelle b07cea9049 option to show an empty entry selection screen 2021-04-28 23:29:13 +02:00
antelle 991388d4cd removed debugging code 2021-04-28 23:09:24 +02:00
antelle 7adc77c823 closing the selection view on inactivity 2021-04-28 23:08:01 +02:00
antelle 550f93d4ae release notes
fix #874
2021-04-28 22:12:07 +02:00
antelle b43187e5b8 update deps again 2021-04-28 21:08:42 +02:00
antelle 6230710bb0 fixing build 2021-04-28 21:07:22 +02:00
antelle ff119cbc0a Revert "audit fix"
This reverts commit 2a2ec713
2021-04-28 21:05:22 +02:00
antelle 2a2ec713af audit fix 2021-04-28 21:04:37 +02:00
antelle 41eb560d1a fixed deps 2021-04-28 21:03:49 +02:00
antelle f7b9b91144 temporary safari link 2021-04-28 20:25:46 +02:00
antelle 97ab5d066e Added extension links 2021-04-28 20:22:21 +02:00
antelle 448e19d28c fixed entry searching with subdomains 2021-04-28 18:57:41 +02:00
antelle 391f2c9c00 fixed entry creation 2021-04-28 18:29:45 +02:00
antelle 18524ad9eb returning one matching entry automatically 2021-04-28 17:59:47 +02:00
antelle 732c35625b entry selection filters 2021-04-28 17:34:48 +02:00
antelle 95857450c2 focusing KeeWeb before entry selection 2021-04-28 12:04:54 +02:00
antelle 98ea7a542d extracted select-entry-filter 2021-04-28 11:29:48 +02:00
antelle 338f6d47d1 extracted entry selection view 2021-04-28 09:44:02 +02:00
antelle fdb492c572 don't prevent closing main window during auto-type 2021-04-28 08:25:18 +02:00
antelle ce3ff8c3d8 fixed extra file opened during auto-type 2021-04-28 08:23:15 +02:00
antelle 1789d9bb67 up deps 2021-04-28 07:47:13 +02:00
antelle 1883b527a4 fixed window blur during auto-type 2021-04-27 21:38:31 +02:00
antelle 2f4a9d450a displaying extension name in views 2021-04-27 20:01:53 +02:00
antelle 12fc33a15e fixed web extension focus 2021-04-27 19:38:27 +02:00
antelle e4693ecf61 updating entries 2021-04-27 19:15:03 +02:00
antelle c15d709db2 fixed table view column width 2021-04-27 16:23:49 +02:00
antelle e09040b0fb fixed auto-type from minimized state 2021-04-27 14:49:13 +02:00
antelle 4477c3fc2c get-logins messages 2021-04-27 14:29:00 +02:00
antelle f52a00a156 implemented set-login for extensions 2021-04-27 13:58:43 +02:00
antelle 946a063993 fixed data decryption error 2021-04-26 23:55:44 +02:00
antelle d3fcbcb6a5 fixed read passwords count display 2021-04-26 23:44:41 +02:00
antelle f91cfc0f79 processing triggerUnlock sent by the extension 2021-04-26 22:35:17 +02:00
antelle f0bbc54fd5 fixed extension version 2021-04-26 22:29:02 +02:00
antelle 3ca5f94b9b creating new groups form browser extensions 2021-04-26 20:47:43 +02:00
antelle 7a35ebec73 fixed a memory leak
see #1580
2021-04-26 19:20:29 +02:00
antelle 66d3623f90 implemented new group creation 2021-04-26 16:40:36 +02:00
antelle 8c9badd440 checking available files per connection 2021-04-26 15:36:21 +02:00
antelle e0d47a474d switching to the open view on request 2021-04-26 13:40:37 +02:00
antelle 1a8aff0594 fix eslint 2021-04-26 13:34:33 +02:00
antelle cf5e01c213 wording 2021-04-26 13:31:57 +02:00
antelle ffae956e21 removing empty websites 2021-04-26 13:30:12 +02:00
antelle 5c4cb7ff6d displaying extra websites as urls 2021-04-26 13:26:27 +02:00
antelle 7794c8b89b release notes 2021-04-26 12:49:59 +02:00
antelle bb78e8e8a7 adding multiple websites to entries 2021-04-26 12:49:25 +02:00
antelle 00a2a95adc fixed read passwords display 2021-04-26 12:22:35 +02:00
antelle e55dd47f6e don't hide keeweb after successful result 2021-04-26 12:16:22 +02:00
antelle 7b10bda0a4 eslint fix 2021-04-26 12:08:30 +02:00
antelle d5b9aefa01 unlocking on request from browser extension 2021-04-26 12:06:03 +02:00
antelle cdc3f5e1df displaying a message on the open screen 2021-04-26 11:51:24 +02:00
antelle 8fc6f49fb4 reworked unlocking files for auto-type and browser extension connection
fix #1768
2021-04-26 11:20:20 +02:00
antelle 29e5c14009 setting for saving credentials 2021-04-25 23:17:14 +02:00
antelle fe9ff205b9 wording 2021-04-25 23:02:02 +02:00
antelle 87b59eccee fixed extension connection on web 2021-04-25 20:38:49 +02:00
antelle 42cb5a6c36 editing file permissions in settings 2021-04-25 19:59:52 +02:00
antelle 9826965803 session management in settings 2021-04-25 18:08:16 +02:00
antelle 47153e41d3 extension connection timeout 2021-04-25 14:09:54 +02:00
antelle 677b428e63 all other files and all files 2021-04-25 13:50:03 +02:00
antelle 15936fcef0 extension connection dialog 2021-04-25 13:46:59 +02:00
antelle b2e828e7ef up native messaging host 2021-04-25 00:17:35 +02:00
antelle ac6e5a3ae8 fixed processing first message 2021-04-24 23:47:35 +02:00
antelle 3722477eea added a warning about extension association 2021-04-24 19:31:39 +02:00
antelle 72d0f76acc getting parent info for cmd 2021-04-24 10:48:28 +02:00
antelle b7333574aa replaced .exe in app name on windows 2021-04-24 10:38:43 +02:00
antelle 5caccbd1f5 getting appName 2021-04-24 10:26:07 +02:00
antelle 698b559935 up native-messaging-host 2021-04-24 10:06:55 +02:00
antelle 36d5e8f730 windows process information 2021-04-23 23:49:14 +02:00
antelle 233721bf51 extension installation on Windows 2021-04-23 23:17:35 +02:00
antelle 822602a38f added a note about windows-registry 2021-04-23 21:25:49 +02:00
antelle e69dbf697c up keeweb-native-modules 2021-04-23 21:17:11 +02:00
antelle 184d71a1b9 fixed firefox manifest 2021-04-21 23:30:32 +02:00
antelle ccb9b03287 creating native manifests 2021-04-21 22:23:17 +02:00
antelle dd4ad6d72b starting the extension host based on settings 2021-04-21 20:27:55 +02:00
antelle 195e2cc8f0 fixed exports 2021-04-21 20:09:26 +02:00
antelle ef573336d8 moved isDev to utils 2021-04-21 19:31:37 +02:00
antelle 9377a849f6 added native messaging host package 2021-04-21 19:28:44 +02:00
antelle 00ccbf4be2 deleted native-messaging-host 2021-04-20 17:07:50 +02:00
antelle 00159d26dc extracted protocolimpl 2021-04-19 23:40:57 +02:00
antelle b4f93adf05 reorganized imports a bit 2021-04-19 23:33:16 +02:00
antelle 5bbc9003f4 enable extension settings 2021-04-19 22:40:59 +02:00
antelle c4be926b7c better extension messages 2021-04-19 21:56:08 +02:00
antelle 147e3911f1 fixed no extensions message 2021-04-19 21:31:55 +02:00
antelle c7b814fc01 browser extension settings 2021-04-19 21:29:52 +02:00
antelle 8525571afc disabled the extension setting for mobile 2021-04-19 18:18:37 +02:00
antelle 5c5de91672 fixed links in desktop apps 2021-04-19 18:15:41 +02:00
antelle 7dbb0a2e8f added a missing whitespace 2021-04-18 21:42:22 +02:00
antelle 11b54ffdf9 wording 2021-04-18 21:39:24 +02:00
antelle 5d76ac33dd wording 2021-04-18 21:38:58 +02:00
antelle 8e92bf3766 kw.runtimeData api 2021-04-18 21:37:17 +02:00
antelle 56517eec62 typo 2021-04-18 21:35:45 +02:00
antelle ed56f0733e added an option to focus KeeWeb when the file is locked 2021-04-18 21:02:43 +02:00
antelle 3fd799ee74 browser extension settings page 2021-04-18 20:55:27 +02:00
antelle 4ed8a67eda simplified a condition 2021-04-18 18:57:38 +02:00
antelle cbb9a8dbd0 sending origin information in the first message 2021-04-18 18:50:03 +02:00
antelle 4280b84459 moved browser socket connection management to the main process 2021-04-18 14:26:45 +02:00
antelle e06e9ac1f8 moved ipc handlers setup to corresponding files 2021-04-18 11:50:11 +02:00
antelle 7bea4ad282 added team id to avoid the requirement for codesigning config 2021-04-17 20:33:37 +02:00
antelle 316c14d845 shorter socket name 2021-04-17 17:25:14 +02:00
antelle d17be63312 keeweb-browse => keeweb-connect 2021-04-17 17:00:14 +02:00
antelle 2c2253db31 renamed the extension 2021-04-17 16:57:06 +02:00
antelle a3128e56a3 client.supportsNotifications 2021-04-17 09:51:18 +02:00
antelle de0cf0ce67 fixed an empty notification about closing files 2021-04-17 09:28:25 +02:00
antelle e3f33b9d4c don't require client id for ping 2021-04-16 21:06:20 +02:00
antelle 27143e2663 fixed macos socket 2021-04-15 23:46:34 +02:00
antelle 566e2fb35d creating browser socket in a group container on macOS 2021-04-15 20:38:13 +02:00
antelle dc5ada1c04 etter error handling 2021-04-14 21:31:29 +02:00
antelle 603e698f5a fixed socket cleanup on windows 2021-04-14 21:26:58 +02:00
antelle f893dcb19a native messaging host tests 2021-04-14 21:11:24 +02:00
antelle 27b116798e tests fix 2021-04-14 20:41:45 +02:00
antelle ae932e5710 windows extension connection fixes 2021-04-14 20:34:03 +02:00
antelle bab5a979be windows compilation options 2021-04-13 23:37:43 +02:00
antelle 1b8c9a3e0c fixed linux helper paths 2021-04-13 23:03:38 +02:00
antelle 99d0fa30ed removed keeweb launching 2021-04-13 22:59:39 +02:00
antelle 5cc146481b fixed grunt-cmake 2021-04-13 22:55:38 +02:00
antelle 102da5cba1 signing the native messaging host 2021-04-13 22:40:35 +02:00
antelle d374eecb18 running cmake on build 2021-04-13 22:39:07 +02:00
antelle 08d31c0ffe don't launch keeweb if it's not running 2021-04-13 21:37:00 +02:00
antelle c62e40465a eslint 2021-04-13 20:43:34 +02:00
antelle 5367f32d5c reorganized app startup 2021-04-13 20:27:20 +02:00
antelle ad20698a42 deleted the browser extension setting 2021-04-13 20:27:07 +02:00
antelle e865d80a63 moved more code to protocol-impl 2021-04-12 23:42:42 +02:00
antelle 81a311e7fa refactored browser extension connector module 2021-04-12 23:37:08 +02:00
antelle 5397fa8fd3 create-group 2021-04-12 23:23:22 +02:00
antelle b9e08cd9e3 implemented get-database-groups 2021-04-12 23:05:57 +02:00
antelle fe86cd6320 native messaging on Firefox 2021-04-11 23:28:24 +02:00
antelle bba07bb83a fixed constant name 2021-04-11 19:52:49 +02:00
antelle 83a53ee1f0 error code property 2021-04-11 19:14:34 +02:00
antelle 23ba363a0d asking questions about accessing data in the extension 2021-04-11 18:59:33 +02:00
antelle 101aee81c0 added more browser extensions to the native host 2021-04-11 18:01:36 +02:00
antelle fa60d02129 option to debug browser extension transport 2021-04-11 17:59:05 +02:00
antelle 6a9e5fd801 always returning KeeWeb hash 2021-04-11 17:22:58 +02:00
antelle 12ae7845ec renamed the file 2021-04-11 15:41:39 +02:00
antelle 045e8ee96f fixed error codes 2021-04-11 15:04:04 +02:00
antelle 993f75b14e fixed some contract types 2021-04-11 14:53:56 +02:00
antelle b48f3746bc closing browser extension connections 2021-04-11 12:48:58 +02:00
antelle dcc477acbc tightened content security policy to prevent browser extension from adding scripts to KeeWeb 2021-04-11 11:44:51 +02:00
antelle 5f4f007b31 more extension logging 2021-04-11 10:17:02 +02:00
antelle 9d4788cb7c returning appName only for own extension 2021-04-10 23:37:24 +02:00
antelle 5a1380f9ef removed extra check 2021-04-10 23:36:21 +02:00
antelle 0d82aa26f3 checking own extension 2021-04-10 23:35:42 +02:00
antelle 05f2ec19f2 reorganized the extension connector code 2021-04-10 21:32:29 +02:00
antelle 25c3c9c565 connecting to the native app from the extension 2021-04-10 21:16:48 +02:00
antelle d6e7e3d4a0 reverted the timeout 2021-04-10 19:33:31 +02:00
antelle 6148112904 max 1 minute to connect to KeeWeb 2021-04-10 19:31:34 +02:00
antelle ba338e9282 launching keeweb 2021-04-10 19:29:24 +02:00
antelle 2e132690d1 fixed deletion 2021-04-10 18:59:58 +02:00
antelle 8f1b6ca3b7 removed some manual memory management 2021-04-10 18:54:15 +02:00
antelle 85d0252972 removed double stderr 2021-04-10 18:47:00 +02:00
antelle 077f72a0c5 printing stderr 2021-04-10 18:42:12 +02:00
antelle 25bec513f3 extracted some constants 2021-04-10 18:39:23 +02:00
antelle 0a6378d84c renamed the tests 2021-04-10 18:39:12 +02:00
antelle 1e6034ce37 native host tests 2021-04-10 18:38:27 +02:00
antelle ef6e5ebcbe added native messaging host 2021-04-10 17:00:10 +02:00
antelle 19a42b7463 fix #1783: crash after disabling USB devices on Linux 2021-04-10 00:19:58 +02:00
antelle 9d0e608454 Merge branch 'master' into develop
# Conflicts:
#	app/scripts/locales/base.json
#	app/scripts/storage/impl/storage-gdrive.js
#	package-lock.json
#	release-notes.md
2021-04-09 20:10:52 +02:00
antelle bd9a98dbfd fixed docker image tagging 2021-04-09 20:09:44 +02:00
antelle 0ffca3dfe4 npm audit fix 2021-04-09 19:03:02 +02:00
antelle 01958559ee fix #1767: updater issues on macOS 2021-04-09 19:01:55 +02:00
antelle bb4aef206a fixed wait-pid 2021-04-09 19:01:54 +02:00
antelle e74227b0db added --verbose option to the installer 2021-04-09 19:01:53 +02:00
antelle a47aec1cc4 bump version 2021-04-09 19:01:52 +02:00
antelle cd9d8cde3e release notes 2021-04-09 19:01:49 +02:00
antelle d4f68d5ec7 fix #1786: refresh_token in Google Drive 2021-04-09 19:01:48 +02:00
antelle a910cfcb35 fix #618: team drive support in Google Drive 2021-04-09 19:01:47 +02:00
antelle 962de503fd up kdbxweb 2021-04-09 19:01:27 +02:00
antelle f40e72c882 shared drives variable 2021-04-09 18:20:15 +02:00
antelle b7993e1d0e renamed team drives to shared drives 2021-04-09 18:19:26 +02:00
antelle d6e5e73e82 fixed refresh_token in Google Drive
see #1786
2021-04-08 23:07:19 +02:00
antelle 1794f87f99 fix #618: team drive support in Google Drive 2021-04-08 21:47:57 +02:00
antelle 81c7d57f30 localized templates folder name 2021-04-08 20:09:25 +02:00
antelle b4f0149bd8 saving extension name 2021-04-08 19:01:08 +02:00
antelle 87ec06eef0 browser extension: generate-password method 2021-04-08 18:10:48 +02:00
antelle c764548214 browser extension: generate-password method 2021-04-08 17:59:39 +02:00
antelle c78af7c3ad setting nonce in response payload 2021-04-08 17:36:42 +02:00
antelle f9cd0d4e29 browser extension: asking for user attention 2021-04-08 17:28:41 +02:00
antelle ee03435ca7 implemented lock-database 2021-04-07 22:49:23 +02:00
antelle a9b83256c6 returning only active db hashes 2021-04-07 22:07:16 +02:00
antelle 7086ddcf58 returning the list of hashes 2021-04-07 22:04:21 +02:00
antelle 26d226bb17 browser-extension: returning appName 2021-04-07 21:16:48 +02:00
antelle dc4742bf5c browser extension connector: implemented get-databasehash 2021-04-07 21:08:22 +02:00
antelle 933df5fae4 browser extension file notifications 2021-04-06 21:27:58 +02:00
antelle 4b17ac19dc browser extension backend 101 2021-04-06 20:10:42 +02:00
antelle 43f6a4f499 clone keeweb-browser in dev-env 2021-04-05 09:53:49 +02:00
antelle 076b99676d fix #1775: special error message for cases when Touch ID is changed after setup
also added Touch ID key deletion when it's disabled
2021-04-02 18:47:04 +02:00
antelle b3dfa05a4a added menu translations 2021-04-02 13:34:27 +02:00
antelle c5946b1a0b fixed default character count calculation in the generator 2021-04-01 21:57:42 +02:00
antelle 58a85eabdf fix #1774: password generator now includes all selected character ranges 2021-04-01 21:54:42 +02:00
Dimitri Witkowski 8a1d9b96dd Merge pull request #1770 from aslafy-z/patch-1
fix(docker): publish all docker image tags
2021-03-29 20:31:41 +02:00
Zadkiel 36a3d2f766 fix(docker): publish all docker image tags 2021-03-29 20:25:37 +02:00
antelle 68010076f9 renamed external model to otp-device 2021-03-28 19:05:20 +02:00
antelle 92da783e82 moved spawn to the main process 2021-03-28 18:41:31 +02:00
antelle 7d1e343d25 fixed wait-pid 2021-03-27 14:24:54 +01:00
antelle 84d17b7255 added --verbose option to the installer 2021-03-27 14:11:28 +01:00
antelle 64e06a32ec Merge branch 'master' into develop
# Conflicts:
#	package-lock.json
#	release-notes.md
2021-03-27 10:25:29 +01:00
antelle 4ee87e197d using new accounts ykman command
Build / publish (push) Canceled after 0s
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
2021-03-27 09:19:20 +01:00
antelle cace667b26 release notes 2021-03-27 09:19:19 +01:00
antelle 67d3e28af7 bump version 2021-03-27 09:19:18 +01:00
antelle 3c5f4e7b84 release notes 2021-03-27 09:19:17 +01:00
antelle 8f913c737c removed debugging code from build 2021-03-27 09:19:16 +01:00
antelle 8ba147f52f fix #1762: line breaks in Markdown notes 2021-03-27 09:19:15 +01:00
antelle 9cfbd24b76 fix #1758: export format HTML issues 2021-03-27 09:19:14 +01:00
antelle 259abf6d25 added missing overflow to revealed passwords 2021-03-27 09:19:13 +01:00
antelle 8c59838a6f fix #1734: overlapping generator icon on password inputs 2021-03-27 09:19:12 +01:00
antelle f7d24c6379 fix #1725: setting expire date in the past 2021-03-27 09:19:11 +01:00
antelle 0f5466e075 fix #1755: calendar tooltips in different time zones 2021-03-27 09:19:10 +01:00
antelle 7ab0e83f55 ykman v4 support 2021-03-27 09:19:09 +01:00
antelle dc02ede8d7 up kdbxweb 2021-03-27 09:19:08 +01:00
antelle 42e484827f restored the missing disk icon 2021-03-27 09:19:07 +01:00
antelle 576c8f97bd removed dist from dev-env 2021-03-27 09:19:06 +01:00
antelle 808b4a687d fix #1762: line breaks in Markdown notes 2021-03-24 17:02:05 +01:00
antelle 954f2c235b fix #1758: export format HTML issues 2021-03-22 17:13:09 +01:00
antelle 51a7bbbfb8 added missing overflow to revealed passwords 2021-03-21 17:38:36 +01:00
antelle 9b5f5857bb fix #1734: overlapping generator icon on password inputs 2021-03-21 17:35:06 +01:00
antelle e66f52a8ce fix #1753: option to use short-lived tokens in cloud storages 2021-03-21 17:22:38 +01:00
antelle 8023bcea96 fix #1725: setting expire date in the past 2021-03-21 16:38:09 +01:00
antelle dfb7491503 fix #1755: calendar tooltips in different time zones 2021-03-21 16:36:18 +01:00
antelle b43d9c963a ykman v4 support 2021-03-20 22:45:42 +01:00
antelle 51c0637134 fixed list item size in firefox 2021-03-20 22:40:13 +01:00
antelle b16c23190b fixed scrolled entry modification 2021-03-20 20:26:29 +01:00
antelle fddf2892cd fix #1580: high memory usage on large files, virtual scrolling of entries 2021-03-20 19:17:57 +01:00
antelle 53b5715da0 up kdbxweb 2021-03-19 23:07:10 +01:00
antelle 3a79fcb366 optimized memory consumption for large files
see #1580
2021-03-19 22:52:41 +01:00
antelle 4df034ac55 restored the missing disk icon 2021-03-19 22:11:55 +01:00
antelle a0bb04fe3a removed dist from dev-env 2021-03-19 22:08:33 +01:00
antelle a923f28a8d Merge branch 'master' into develop
# Conflicts:
#	release-notes.md
2021-03-19 18:57:01 +01:00
antelle 78642f1c9b opening XML and CSV files using the Open button 2021-03-19 18:56:36 +01:00
antelle f8a85af859 Merge branch 'release-1.17' into master
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-03-18 17:14:58 +01:00
antelle bdb5bd0428 bump version 2021-03-18 17:14:37 +01:00
antelle 9e07f55c12 fix #1749: auto-type freezes on macOS 2021-03-18 17:14:01 +01:00
antelle 6a3e129ed5 fixed update installation when minimize on close is enabled
see #1740
2021-03-15 19:31:40 +01:00
antelle 879ba17760 Merge branch 'master' into develop
# Conflicts:
#	release-notes.md
2021-03-14 21:17:26 +01:00
antelle 0d2107e72a Merge branch 'release-1.17' into master
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-03-14 20:24:38 +01:00
antelle a23a47fded bump version 2021-03-14 20:23:03 +01:00
antelle 7737a29cca fix #1747: white screen in old Safari 2021-03-14 20:22:37 +01:00
antelle 34278b3bff fixed usb-detection link 2021-03-13 19:37:47 +01:00
antelle 40b0643eaa Merge branch 'master' into develop 2021-03-13 19:31:54 +01:00
antelle ae7f5fbff2 Merge branch 'release-1.17' into master
Build / publish (push) Canceled after 0s
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
2021-03-13 17:46:55 +01:00
antelle 0a776c6a72 Merge branch 'release-1.17' into develop
# Conflicts:
#	release-notes.md
2021-03-13 17:31:05 +01:00
antelle 5caecb1c8f fix #1738: fixed auto-type on Linux with NumLock pressed 2021-03-13 14:27:48 +01:00
antelle 0def3f8b2d fix #1745: deleting selected text in auto-type selector 2021-03-13 10:11:32 +01:00
antelle 1593acd8e9 fixed auto-type searchbox clear button alignment 2021-03-13 10:01:51 +01:00
antelle da5553364a using proper call sequence in node-usb-detection 2021-03-12 23:37:29 +01:00
antelle b7c03490d8 bump version 2021-03-12 23:17:22 +01:00
antelle b8dd7b0f71 fix #1744, fix #1743: fixed crashes in the USB module on Windows 2021-03-12 23:06:19 +01:00
antelle e7a8d5232f Merge branch 'master' into develop
# Conflicts:
#	release-notes.md
2021-03-10 21:37:23 +01:00
antelle c638fa38e8 Merge branch 'release-1.17' into master
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-03-10 19:07:57 +01:00
antelle f74b054f01 release date 2021-03-10 19:06:56 +01:00
antelle b7af33edae bump version 2021-03-10 19:01:00 +01:00
antelle 166211b6f8 fix #1735: issue with auto-typing some characters on Windows 2021-03-10 17:58:17 +01:00
antelle f8ec47bfdb release notes 2021-03-08 22:34:29 +01:00
antelle 4e84121553 cleaned up legacy auto-type 2021-03-08 22:29:59 +01:00
antelle ccf4ce8e58 package name 2021-03-07 16:25:07 +01:00
antelle 6ba3d44fa1 Merge branch 'master' into develop 2021-03-07 15:16:48 +01:00
antelle c03ed76fec increased memory limit
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-03-07 14:35:27 +01:00
antelle 31214b10e7 Merge branch 'develop' into master 2021-03-07 14:23:29 +01:00
antelle 5bbc324b2e bump version 2021-03-07 14:22:49 +01:00
antelle 7dcb22950e fixed bump version 2021-03-07 14:22:41 +01:00
antelle 3c06f1f1a3 release date 2021-03-07 14:20:32 +01:00
antelle 6bcf5161b1 release notes 2021-03-07 11:14:34 +01:00
antelle ccc6765e2e release date 2021-03-07 10:56:18 +01:00
antelle 14d40d4e2d release notes 2021-03-07 10:55:41 +01:00
antelle 75d563b320 removed debugging code 2021-03-06 21:40:23 +01:00
antelle 2fdd53b55e removed debugging code 2021-03-06 21:30:51 +01:00
antelle 4ac16a6701 debugging patching on ci 2021-03-06 21:24:45 +01:00
antelle e998788d25 git+https 2021-03-06 21:05:54 +01:00
antelle e23b80b11b languages 2021-03-06 20:52:59 +01:00
antelle 7eb884dcb9 up deps 2021-03-06 20:17:47 +01:00
antelle 92397adbb3 eslint 2021-03-06 20:17:31 +01:00
antelle 828485789f ignore password quality warnings alert box 2021-03-06 19:19:23 +01:00
antelle 2f84474216 disabled draggable window on Windows 2021-03-06 11:38:05 +01:00
antelle 762aab397d fixes for custom titlebar 2021-03-06 11:35:14 +01:00
antelle b03cb4590f release notes 2021-03-05 22:12:09 +01:00
antelle 5d40831dd7 reverted debug code 2021-03-05 21:16:42 +01:00
antelle 0d291afb05 fix #1729: prevent double encoding move path 2021-03-05 21:16:12 +01:00
antelle e107c5c108 disabled auto-switching theme 2021-03-05 21:08:09 +01:00
antelle 44905e674d fixed titlebar events 2021-03-03 23:46:23 +01:00
antelle 5b790e711a added a logo on the custom titlebar for Windows 2021-03-03 23:42:41 +01:00
antelle 1a9c54b237 extracted the inline logo 2021-03-03 22:33:29 +01:00
antelle 0f4434a361 fix #689: custom title bar on Windows 2021-03-03 22:12:49 +01:00
antelle 7e2823903b up electron 2021-03-03 17:34:57 +01:00
antelle 92937dbb86 fix #1727: tabs in notes 2021-03-03 17:13:41 +01:00
antelle 0868efe095 added --native-module-host-process option 2021-03-02 21:17:04 +01:00
antelle 97374f63f9 Merge branch 'master' into develop
# Conflicts:
#	desktop/package-lock.json
#	package-lock.json
#	release-notes.md
2021-03-02 20:10:18 +01:00
antelle 69f7b02ce6 Merge branch 'release-1.16' into master
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-03-02 19:21:21 +01:00
antelle b5ee682a41 bump version 2021-03-02 19:18:46 +01:00
antelle 70f82bdfd8 release date fix 2021-03-02 19:18:29 +01:00
antelle e70185560b release notes 2021-03-02 19:18:21 +01:00
antelle be4be0e7e8 fix #1715: storage authentication in Epiphany browser 2021-03-02 19:17:42 +01:00
antelle b9a5c64113 fix #1713: OneDrive authentication on web 2021-03-02 19:17:36 +01:00
antelle 215f398547 fix #1726: search in protected fields 2021-03-02 19:17:29 +01:00
antelle ace18f9d95 fix #1715: storage authentication in Epiphany browser 2021-03-01 18:06:45 +01:00
antelle c4ec91f61a fix #1713: OneDrive authentication on web 2021-03-01 18:03:13 +01:00
antelle a4881419df fix #1726: search in protected fields 2021-03-01 17:55:08 +01:00
antelle 839359a794 removed a readme 2021-03-01 17:53:20 +01:00
antelle d4d9b7c7a7 readme about translations 2021-02-26 18:54:43 +01:00
antelle d4526c0c8a release notes 2021-02-26 18:48:18 +01:00
Dimitri Witkowski 78f0bc6f02 Merge pull request #1721 from enter-is/develop
Option to minimize KeeWeb on copy
2021-02-26 18:46:43 +01:00
is ad3f5afeba Option to minimize KeeWeb on copy
Code review changes: https://github.com/keeweb/keeweb/pull/1721

https://github.com/keeweb/keeweb/issues/1718
2021-02-23 08:43:01 +02:00
is 7c4326939a Option to minimize KeeWeb on copy
https://github.com/keeweb/keeweb/issues/1718
2021-02-22 15:02:42 +02:00
antelle e5428b67b8 npm 2021-02-14 11:42:10 +01:00
antelle 59616bd8af disabled touch id check 2021-02-14 11:36:20 +01:00
antelle fdc9ac95d2 up native-modules 2021-02-13 21:06:14 +01:00
antelle a8f0d1af62 extended the oauth fix to iOS, see #1711 2021-02-12 20:14:21 +01:00
antelle f6e377f222 fix #1711: workaround for Dropbox on iOS PWA 2021-02-12 20:14:07 +01:00
antelle c43d7ab92a extended the oauth fix to iOS, see #1711 2021-02-12 09:34:42 +01:00
antelle 0464e1de5c fix #1711: workaround for Dropbox on iOS PWA 2021-02-11 23:01:28 +01:00
antelle 7a7381eac0 don't spawn another process 2021-02-09 21:38:49 +01:00
antelle 1379db1e92 fixed yubikey touch 2021-02-09 21:37:54 +01:00
antelle ee10894f5f added an option to prevent another process from being created 2021-02-09 20:19:12 +01:00
antelle 87d760708d removed an extra code 2021-02-09 19:54:30 +01:00
antelle ec7155809b fixed native module helper 2021-02-08 22:49:53 +01:00
antelle 5af7837468 patching electron 2021-02-06 14:46:24 +01:00
antelle d3f79af2ab up 2021-02-06 14:25:12 +01:00
antelle a6c0fc3984 option do disable caching files in offline storage
fix #404, fix #1115
2021-02-06 13:53:12 +01:00
antelle 56604de7f1 up deps 2021-02-06 13:24:59 +01:00
antelle d5e6199775 added linux dependencies 2021-02-04 21:30:52 +01:00
antelle 15920abcf7 replaced a year 2021-02-04 21:09:21 +01:00
antelle 88205d716a new npm 2021-02-04 21:05:26 +01:00
antelle fa1a16f140 added an explanation about auto-type issues on macOS 2021-02-04 20:54:13 +01:00
antelle 4f8075ca6c package-lock 2021-02-04 19:47:15 +01:00
antelle 2615d0644d up 2021-02-03 23:59:15 +01:00
antelle d2686937fd fixed linux build 2021-02-03 22:00:03 +01:00
antelle b60307e28b removed com.apple.security.get-task-allow entitlement 2021-02-03 21:58:49 +01:00
antelle 08d49ddc54 fix #679, fix #1383: Touch ID on macOS 2021-02-03 20:30:59 +01:00
antelle 4cdc80e530 Touch ID settings, see #679 2021-02-01 22:24:53 +01:00
antelle acd360efb9 cleaned up old touch id implementation 2021-02-01 21:08:02 +01:00
antelle 87ad89beeb fix #1697: creating files with Argon2id KDF 2021-02-01 17:25:46 +01:00
antelle c2663ca7d8 new completion page after storage provider authentication 2021-01-31 13:24:49 +01:00
antelle 4e58285cc2 using batch text entry 2021-01-31 12:27:20 +01:00
antelle ef354fb8af up deps 2021-01-31 00:47:27 +01:00
antelle 79ce535d4a up deps 2021-01-31 00:20:34 +01:00
antelle 50c43c4408 year 2021-01-30 23:41:32 +01:00
antelle 8fdec5aadc added a module link 2021-01-30 21:40:00 +01:00
antelle 73eccd5308 added a message about delay 2021-01-30 21:37:09 +01:00
antelle 4449677e36 entering text as key codes 2021-01-30 21:34:08 +01:00
antelle 8dc3a4b51f fixed vkey 2021-01-30 20:29:26 +01:00
antelle cd2de3a1db native auto type: unpressing keys 2021-01-30 20:22:13 +01:00
antelle 1fa0eebc3b changed ipc method naming convention 2021-01-30 18:39:25 +01:00
antelle e13002bab3 typing key codes 2021-01-30 18:24:42 +01:00
antelle a750e80ea1 auto-type progress 2021-01-30 16:00:57 +01:00
antelle 66e1b0a089 first auto-type emitter implementation 2021-01-30 15:37:25 +01:00
antelle 00c0b3f88c native auto-type helper implementation 2021-01-30 14:55:00 +01:00
antelle 674311cb4b native auto-type methods 2021-01-30 14:16:32 +01:00
antelle f54b8e00e1 up deps 2021-01-30 14:03:59 +01:00
antelle c1beac5d0d up native-modules 2021-01-30 13:18:11 +01:00
antelle 7123345341 cleaned up useless conditions 2021-01-28 08:08:11 +01:00
antelle 0e2475e667 cleanup 2021-01-17 08:20:08 +01:00
antelle acf1f1146d removed other links 2021-01-17 08:14:42 +01:00
antelle d136fa4fdf disabled discussions 2021-01-17 08:14:01 +01:00
antelle 61067391b6 added a link to node-secure-enclave 2021-01-10 17:23:23 +01:00
antelle affebb1ba4 progress logging 2021-01-10 17:16:21 +01:00
antelle cbffa3d4ef message formatting 2021-01-10 17:11:47 +01:00
antelle 6ef5ae2517 separated encrypt and decrypt 2021-01-10 16:56:53 +01:00
antelle 9701496897 better message 2021-01-10 16:40:16 +01:00
antelle 22d6a3011f added a string for touch id 2021-01-10 16:31:50 +01:00
antelle d22ddd99f6 secure enclave support for #679 2021-01-10 14:31:33 +01:00
antelle 4e8648fca0 using the provisioning profile on ci 2021-01-10 12:23:18 +01:00
antelle 9f49c357e9 using provisioning profile for codesigning macos apps 2021-01-10 12:16:27 +01:00
antelle d318467039 added a possibility to build a signed desktop app with npm dev-desktop-macos-signed 2021-01-10 11:55:00 +01:00
antelle b05c25ac1a up keeweb-native-modules 2021-01-10 11:36:51 +01:00
antelle 5dd0d51514 removed endswith 2021-01-10 09:59:51 +01:00
antelle c7574a6b83 debug message in the installer 2021-01-10 09:41:30 +01:00
antelle ab64e9343b sleep time 2021-01-10 09:36:34 +01:00
antelle b82cb0e849 fixed Windows update installation 2021-01-10 09:28:09 +01:00
antelle c44a8fab42 fixed update buttons margin 2021-01-10 09:25:12 +01:00
antelle 9a75eb0a22 removed a newline 2021-01-10 00:17:14 +01:00
antelle f994278a8b windows update installer spaces fix 2021-01-10 00:16:45 +01:00
antelle bd882d0e17 fixed windows update installer 2021-01-10 00:14:28 +01:00
antelle fddf62409a fixed updater settings view 2021-01-09 23:04:22 +01:00
antelle a8967ac764 timeout before copying files in the updater 2021-01-09 22:10:39 +01:00
antelle 20bc254a42 fixed windows uninstaller 2021-01-09 21:52:38 +01:00
antelle e298d5fbd8 ignoring cancellation errors 2021-01-09 21:37:20 +01:00
antelle ff54c2070d fixed the macos updater 2021-01-09 21:34:59 +01:00
antelle e092caa2ef better syntax 2021-01-09 18:48:48 +01:00
antelle feb44be9d0 disable updater on Linux 2021-01-09 18:00:19 +01:00
antelle a43a2e8ce2 checking minVersion against app version 2021-01-09 17:53:13 +01:00
antelle 6fcf77829d using ES5 in the installer 2021-01-09 17:41:34 +01:00
antelle 733833e8a4 fix #1661: using the new Windows installer 2021-01-09 17:15:50 +01:00
antelle 890f998fe0 updating support in win32 installer 2021-01-09 17:14:56 +01:00
antelle 28c708f599 moved desktop scripts 2021-01-09 13:30:19 +01:00
antelle f1e536cbcf moved icons to a folder 2021-01-09 13:27:28 +01:00
antelle 8dc7ad4f01 renamed a module 2021-01-09 13:26:19 +01:00
antelle 43c8976282 fixed a typo 2021-01-09 13:24:50 +01:00
antelle 8ee63ccc24 always using updater cache 2021-01-09 13:21:37 +01:00
antelle 2900fe07bf fixed app launch 2021-01-09 13:19:24 +01:00
antelle 1d572d1fa9 installing updates on macos 2021-01-09 13:15:25 +01:00
antelle 21b8e9ae8f removed globals 2021-01-09 11:50:40 +01:00
antelle cb77b7fc1e moved the installer script 2021-01-09 11:49:54 +01:00
antelle 763141592f fixed dev build 2021-01-09 11:48:40 +01:00
antelle 09fcddfad2 removed an old script 2021-01-09 11:44:52 +01:00
antelle b8ce1b5cc1 macos installer 2021-01-09 11:44:13 +01:00
antelle 6a34fffcc4 more options for auto-lock timeout 2021-01-08 22:56:48 +01:00
antelle 75fa636366 disabled desktop update archives 2021-01-08 22:54:45 +01:00
antelle bee96ebf15 reading update.json 2021-01-07 23:00:16 +01:00
antelle 9ec23fac5d semver test 2021-01-07 22:59:07 +01:00
antelle 2a7cd3574d update.json 2021-01-07 22:05:04 +01:00
antelle 4f96629e22 simplified perf-tracker 2021-01-07 20:54:00 +01:00
antelle 74bd270423 rename 2021-01-07 20:52:52 +01:00
antelle 0469f28a54 removed main.js 2021-01-07 20:52:43 +01:00
antelle a9c2da3fdb Merge branch 'master' into develop
# Conflicts:
#	release-notes.md
2021-01-07 20:01:20 +01:00
antelle 2ec37c3ece fixed a js error 2021-01-02 10:40:24 +01:00
antelle ef812c5697 updated keeweb-plugin dev certificate 2021-01-02 10:38:12 +01:00
antelle 8ab4e7c433 twitter link 2021-01-01 20:06:21 +01:00
antelle 23bb30be92 issue links 2021-01-01 19:57:42 +01:00
antelle a13d44d560 discussions link 2021-01-01 19:55:30 +01:00
antelle 42b5b002cc Merge branch 'release-1.16' into master
Build / darwin (push) Canceled after 0s
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2020-12-31 17:26:37 +01:00
antelle f889488915 bump version 2020-12-31 17:26:18 +01:00
antelle 57f3a287c2 Merge branch 'release-1.16' into master 2020-12-31 17:25:58 +01:00
antelle 143b1a73f7 fix #1671: new lines removal issue in notes 2020-12-31 17:24:59 +01:00
antelle 54a1860398 removed an extra output 2020-12-30 18:17:46 +01:00
antelle 014b6b2319 added --commit-sha 2020-12-30 18:12:28 +01:00
antelle 33763ab865 fix #1670: building without git 2020-12-30 17:46:16 +01:00
antelle 5f5ea892a3 Merge branch 'master' into develop 2020-12-30 13:30:40 +01:00
antelle a3fc2b2cdd debugging windows code signing
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2020-12-30 13:30:11 +01:00
antelle 2cad70dae0 uploading artifacts before virustotal checks 2020-12-30 13:01:55 +01:00
antelle fb56ba2f0d Merge branch 'master' into develop
# Conflicts:
#	release-notes.md
2020-12-30 13:01:12 +01:00
antelle 7c2b7a09f8 Merge branch 'master' into release-1.16 2020-12-30 13:00:41 +01:00
antelle abf5531fc5 fixed another virustotal check failure 2020-12-30 12:52:48 +01:00
antelle 18775e3cea fixed virustotal check failure 2020-12-30 12:44:48 +01:00
antelle 9d5e7be105 Merge branch 'release-1.16' into master 2020-12-30 11:35:50 +01:00
antelle eed647c867 bump version 2020-12-30 11:34:43 +01:00
antelle 072271bbe6 languages 2020-12-30 11:34:06 +01:00
antelle a84499d7d4 upgraded vulnerable dependencies 2020-12-30 11:31:37 +01:00
antelle c06db5a2b7 fix #1668: replacing more bad characters in entry fields 2020-12-30 11:30:17 +01:00
antelle fc737b1f51 fix #1665: removed a package.json dependency from keeweb-plugin 2020-12-26 11:18:48 +01:00
antelle 4f068008bd fixed issues link color 2020-12-20 18:06:20 +01:00
antelle 953f6dc61f option to disable entropy-based checks 2020-12-20 17:53:43 +01:00
antelle 01fac1f0ca password quality warnings 2020-12-20 17:44:27 +01:00
antelle 1796ac743d fixed dropdown 2020-12-19 17:33:19 +01:00
antelle 60329b3cab made the light blue background a bit better 2020-12-19 16:58:25 +01:00
antelle f489eca5c2 fix #1505: pre-selecting first field in auto-type dropdown 2020-12-19 16:47:37 +01:00
antelle 1617cae518 fixed app startup background color 2020-12-19 16:39:18 +01:00
antelle 6c7b99af58 fix #1655: separated custom icons to make the download and selection buttons more visible 2020-12-19 16:30:03 +01:00
antelle ddd242b9df fix #1655: selecting downloaded favicon automatically 2020-12-19 16:29:19 +01:00
antelle c24fe2806c contrast theme position 2020-12-19 14:05:56 +01:00
antelle 8da0a2e239 more themes 2020-12-19 14:00:51 +01:00
antelle 8aff64ce47 removed an extra border on the open view 2020-12-19 14:00:42 +01:00
antelle 677f1ce75f fix #1584: automatically switching between dark and light theme 2020-12-19 12:14:32 +01:00
antelle f99ff95a57 Merge branch 'master' into develop
# Conflicts:
#	release-notes.md
2020-12-18 20:32:44 +01:00
antelle 7a522acdd9 Merge branch 'release-1.16' into master
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2020-12-18 20:30:46 +01:00
antelle 9888a0cf5b bump version 2020-12-18 20:27:19 +01:00
antelle 9b586f7059 bump version 2020-12-18 20:27:13 +01:00
antelle 90666cea99 GitHub funding link 2020-12-18 20:26:36 +01:00
antelle cabf0d388d using custom OneDrive without a secret 2020-12-18 20:17:34 +01:00
antelle 39a33bd77a a bit more padding in advanced search options 2020-12-17 22:38:39 +01:00
antelle f184915849 fix #1657: clear searchbox button 2020-12-17 22:22:00 +01:00
antelle 683b0bc4e6 Merge branch 'master' into develop 2020-12-17 21:15:11 +01:00
antelle 212042556f Merge branch 'release-1.16' into master
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2020-12-17 19:29:14 +01:00
antelle cc4aa6fc1a bump version 2020-12-17 19:28:45 +01:00
antelle b4b6ef3206 calling oauth authorize again if the refresh token is expired 2020-12-17 19:25:36 +01:00
antelle ccfd5126bc release notes 2020-12-16 21:15:40 +01:00
antelle b733b43f8b release notes 2020-12-16 21:15:27 +01:00
antelle 3c0693fdd9 skip VirusTotal checks in ci-sandbox 2020-12-16 21:14:39 +01:00
antelle 938f2be487 passing sha to VirusTotal 2020-12-16 21:12:48 +01:00
antelle 9f2a09cc97 optional oauth secret 2020-12-16 20:32:48 +01:00
antelle 27d5fc4cba running VirusTotal checks on CI, see #1656 2020-12-16 20:15:14 +01:00
antelle 251b763ea9 fix #1656: false positive report on VirusTotal 2020-12-16 18:53:15 +01:00
antelle 8a07162e6b fixed checkbox positioning in settings 2020-12-12 11:02:56 +01:00
antelle 4097d81b8a fixed whitespace replacing in templates 2020-12-12 10:59:34 +01:00
antelle bf2d72745a removed the runtime chunk 2020-12-12 10:38:39 +01:00
antelle acc60e6297 Merge branch 'master' into develop 2020-12-10 23:16:06 +01:00
antelle 211b058686 Merge branch 'release-1.16' into master
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2020-12-10 20:35:28 +01:00
antelle 95c077a0ba bump version 2020-12-10 20:34:11 +01:00
antelle 0b70017780 fix #1650: keyfiles stored in the app can't be used 2020-12-10 20:33:48 +01:00
antelle 513e258869 removed an unused workflow 2020-12-10 19:18:36 +01:00
antelle 7cd046ecc0 Merge branch 'release-1.16' into master
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2020-12-10 19:11:47 +01:00
antelle 220a27e030 keyx 2020-12-10 19:10:18 +01:00
antelle 3728fbc367 bump version 2020-12-10 19:08:20 +01:00
antelle 6d41c71542 up 2020-12-10 19:07:54 +01:00
antelle a1b4dde3e3 release date 2020-12-10 19:06:03 +01:00
antelle 677586e882 fix #1649: missing Argon2 options 2020-12-10 17:41:59 +01:00
antelle 3177a87690 possibility to use V2 keyfiles (.keyx) 2020-12-09 19:21:18 +01:00
antelle f8fda777db Merge branch 'master' into develop 2020-12-06 10:31:16 +01:00
antelle e955c3ddc1 fix #1575: auto-type menu scrolling 2020-12-06 10:30:54 +01:00
antelle 7e36418276 fixed a js error in closing all files 2020-12-05 15:24:45 +01:00
antelle e5616dffa7 more precise license wording 2020-12-04 21:01:08 +01:00
antelle be3087d2cc removed a non-needed link 2020-12-04 20:58:10 +01:00
antelle 396721fce6 fixed a wrong copyright symbol 2020-12-04 20:57:36 +01:00
antelle d49af938d9 switched to setup-gcloud 2020-12-04 20:22:42 +01:00
antelle d2ae8522b5 release notes 2020-12-04 18:01:09 +01:00
antelle a3e27709d0 fixed local file icon 2020-12-04 16:48:48 +01:00
antelle a245553cad Merge branch 'release-1.16' into master
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2020-12-04 16:07:25 +01:00
antelle 8bcdc6d109 bump version 2020-12-04 16:06:10 +01:00
antelle 8a1b28a793 up 2020-12-04 16:05:45 +01:00
antelle 90b236e6f9 Merge branch 'release-1.16' into master 2020-12-04 16:03:42 +01:00
antelle 804b8cf17b release notes 2020-12-04 16:03:14 +01:00
antelle ed3eefbbfc Merge branch 'master' into release-1.16 2020-12-04 16:01:55 +01:00
antelle 51564056a1 Merge branch 'develop' into master 2020-12-04 16:00:48 +01:00
antelle 4884caadd4 fixed icon positioning in links 2020-12-04 09:19:19 +01:00
antelle e129e52e01 fixed icon positioning in links 2020-12-04 09:18:23 +01:00
antelle 893a0f43c1 release date 2020-12-04 08:55:22 +01:00
antelle 083cbe9840 release date 2020-12-04 08:16:27 +01:00
antelle a7fcbae65f up native modules 2020-12-04 07:25:29 +01:00
antelle 7ae80c2e7c copyrights 2020-12-03 23:57:40 +01:00
antelle f1ede2b876 license year 2020-12-03 23:31:42 +01:00
antelle 8c51adbca1 up node_modules 2020-12-03 22:12:52 +01:00
antelle 446f293da0 rebuilt KeeWebHelper as a universal app 2020-12-03 22:12:42 +01:00
antelle bbd7f9c6d6 keyfile paths are saved by default, see #325 2020-12-03 21:09:51 +01:00
antelle 9e5fb90bd6 Argon2id KDF support 2020-12-03 20:22:33 +01:00
antelle 2ed735ad95 fixed list borders on mobile 2020-12-02 21:01:56 +01:00
antelle 00be6f0181 fixed default theme 2020-12-01 19:53:18 +01:00
antelle 29a4d367f9 added missing rgba 2020-12-01 19:42:37 +01:00
antelle df43c61785 fix #1645: color contrast on the selection 2020-12-01 19:28:07 +01:00
antelle 464c49257a building arm64.dmg 2020-11-30 21:16:17 +01:00
antelle bd4a6d0eee fixed arm64 2020-11-30 21:03:20 +01:00
antelle 9410fc6a8c apple silicon build 2020-11-30 20:52:09 +01:00
antelle bd400c0797 up native modules 2020-11-30 18:51:26 +01:00
Dimitri Witkowski 54c721c265 Merge pull request #1642 from PeterDaveHello/DockerfileRefactor
Clean up apt local cache in Dockerfile
2020-11-30 07:49:33 +01:00
Peter Dave Hello 94cfa4f3d4 Replace ADD with COPY instruction in Dockerfile
Reference:

- https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#add-or-copy

> For other items (files, directories) that do not require ADD’s tar
> auto-extraction capability, you should always use COPY.
2020-11-30 10:11:32 +08:00
antelle f21ef7f444 fixed min electron version 2020-11-29 19:58:34 +01:00
Dimitri Witkowski 66a0cf4782 Merge pull request #1639 from PeterDaveHello/patch-3
Ask apk to leave no cache in Dockerfile
2020-11-29 18:57:12 +01:00
Dimitri Witkowski 52f27ce743 Merge pull request #1638 from PeterDaveHello/patch-2
Clean up apt local cache in Dockerfile
2020-11-29 18:56:38 +01:00
Peter Dave Hello 57296ba313 Ask apk to leave no cache in Dockerfile
This will make the image smaller
2020-11-30 01:51:47 +08:00
Peter Dave Hello 47dc1727df Clean up apt local cache in Dockerfile
This will make the image smaller
2020-11-30 01:49:53 +08:00
antelle cd9fe46fdb fixed jumping title in Safari 2020-11-29 18:06:56 +01:00
antelle 775cdf237c fix #1637: git commit in the About box 2020-11-29 17:10:56 +01:00
antelle 63367998bf Merge branch 'develop' into master
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2020-11-29 15:06:59 +01:00
antelle 5d3e110a9e release notes 2020-11-29 15:05:47 +01:00
antelle 42267eaef8 languages 2020-11-29 15:05:15 +01:00
antelle b716599c90 up deps 2020-11-29 14:53:21 +01:00
antelle 6d80de9216 bump version 2020-11-29 14:48:43 +01:00
antelle 212dc99cec updated screenshot 2020-11-29 14:44:39 +01:00
antelle 6f4f8ec1ea action names 2020-11-29 14:12:42 +01:00
antelle 24c64daa49 gcloud action 2020-11-29 13:29:01 +01:00
antelle bdd9a51d25 style fixes 2020-11-29 13:12:32 +01:00
antelle f27862b036 mac menubar icon 2020-11-29 12:17:49 +01:00
antelle f7e9f725b0 bugfixes 2020-11-29 11:10:34 +01:00
antelle 4ee430284b some fixes 2020-11-29 00:19:49 +01:00
antelle 96458048fc fixed menu color 2020-11-28 23:43:56 +01:00
antelle a31f9d5a50 new themes 2020-11-28 23:38:12 +01:00
antelle 33d5f0b5bb some icon fixes 2020-11-28 12:30:46 +01:00
antelle ec43f0a549 fixed themes grid size 2020-11-27 22:28:10 +01:00
antelle 8e0dc326cb better theme selector 2020-11-27 21:17:27 +01:00
antelle 9994c87446 default locale in settings 2020-11-27 20:21:24 +01:00
antelle 647ce573b0 set the default language to en-US to reflect what it is 2020-11-27 18:47:55 +01:00
antelle 1ca4ead313 release notes 2020-11-27 18:40:52 +01:00
antelle 1ef5ffab52 Merge branch 'MrSnix-feature/time-format' into develop 2020-11-27 18:40:05 +01:00
antelle f4f2ddfb3c fix 2020-11-27 18:38:11 +01:00
antelle 650b4805be fixed test runner fatal error reporting 2020-11-26 23:34:08 +01:00
antelle 22754f32ec revert 2020-11-26 23:33:37 +01:00
antelle 85a67b533a fixed test runner fatal error reporting 2020-11-26 22:27:20 +01:00
antelle 974dbd224a release notes 2020-11-26 22:04:06 +01:00
antelle b8c7a8b936 fixed the yubikey icon 2020-11-26 22:04:06 +01:00
antelle 86844064db updated macOS helper icon 2020-11-26 22:04:05 +01:00
antelle 0c9b9a1b97 removed app icon for macos 2020-11-26 22:04:01 +01:00
Giuseppe 0a9846b86f Fix: wrong argument passed 2020-11-26 21:41:24 +01:00
Giuseppe 96ee17b8cc Moved date-format.js to comp/util/ 2020-11-26 21:19:36 +01:00
Giuseppe 4b65792585 Tests now are UTC based to avoid locale conflicts 2020-11-26 20:48:43 +01:00
Giuseppe 32b871e338 Forgot to remove useless import 2020-11-26 11:45:47 +01:00
Giuseppe 46ddb725db Initial commit 2020-11-26 11:32:10 +01:00
antelle d0fdada9f7 release notes 2020-11-25 23:45:14 +01:00
antelle a343853b38 fixed the yubikey icon 2020-11-25 23:33:45 +01:00
antelle 136fbc2f77 updated macOS helper icon 2020-11-25 23:29:43 +01:00
antelle 25bc5643b1 removed app icon for macos 2020-11-25 23:21:29 +01:00
antelle a0c03363a7 removed an old favicon 2020-11-25 22:50:35 +01:00
antelle 8dd7a97769 removed a comment 2020-11-25 21:51:32 +01:00
antelle f70dfaaa28 icons adjustment 2020-11-25 21:40:54 +01:00
antelle 01ceb6dc37 replaced an icon 2020-11-25 21:07:07 +01:00
antelle d56e50b29d reverted the onedrive icon 2020-11-25 20:14:40 +01:00
antelle 0b2c722f61 replaced some icons 2020-11-25 20:10:37 +01:00
antelle 26990f38dd new pinned tab icon 2020-11-25 19:38:17 +01:00
antelle b2d87efc99 yubikey image 2020-11-25 18:28:26 +01:00
antelle 63b7b32e9c fontawesome 5 2020-11-25 18:20:53 +01:00
antelle 8925a48822 bugfix 2020-11-24 18:04:30 +01:00
antelle 8ad47181a9 removed an old edge hack 2020-11-24 17:54:04 +01:00
antelle c1b9ec7a77 icon 2020-11-23 23:39:04 +01:00
antelle 6daf34e261 new icon 2020-11-22 12:25:43 +01:00
antelle fc5c865b18 disabled contextIsolation 2020-11-20 20:46:01 +01:00
antelle 083306a1cd google-github-actions 2020-11-20 15:24:20 +01:00
antelle 5ebacbc347 building on node.js 15 2020-11-20 14:53:28 +01:00
antelle 08c4adfd46 fixed tests 2020-11-20 14:48:53 +01:00
antelle 27f87ad09f get-git-tag 2020-11-20 14:47:14 +01:00
antelle d0b33ca0c1 fix #1523: support for WebDAV servers without Last-Modified header 2020-11-20 14:08:13 +01:00
antelle bb44c7cb3e fixed some that=this 2020-11-20 13:12:06 +01:00
antelle 66e366d9d0 fix #1615: restoring the app window if it's activated when minimized 2020-11-20 10:29:47 +01:00
antelle 31390eac80 fix #1555: auto-saving files when minimize options is enabled 2020-11-19 23:37:09 +01:00
antelle 543a71f6f2 up deps 2020-11-19 18:43:50 +01:00
antelle 5d29b79dab bump electron 2020-11-19 16:52:27 +01:00
antelle 207ff77778 fix #1599: switched to Dropbox short-lived access tokens 2020-11-19 16:05:08 +01:00
antelle b85d25614b fix #1616: auto-type with no matching entries 2020-11-19 13:00:56 +01:00
antelle d1bcdca0d3 fix #1619: auto-locking ignored a field being edited 2020-11-19 11:21:45 +01:00
antelle ffb68b1651 fix #1561: error during loading configs after reset 2020-11-19 11:12:36 +01:00
antelle 49d46cfedc fixed YubiKey errors display 2020-11-11 23:08:08 +01:00
antelle 9c18e2877b fix #1589: shortcut to copy OTP 2020-11-11 19:01:12 +01:00
antelle a1da5ceabe fix #1608: improved the "Show all file" checkbox behavior 2020-11-11 18:44:57 +01:00
antelle 759b92bfd6 fixed a performance issue in searching entries 2020-11-10 23:19:26 +01:00
antelle b4f3d19259 Merge branch 'master' into develop 2020-11-09 21:35:57 +01:00
antelle 35e3271839 added a check for safety 2020-10-28 20:18:27 +01:00
antelle 90da86ebb8 fix #1621: colors on Linux 2020-10-28 20:17:13 +01:00
antelle ad978d2ed3 fix #1612: added WebCrypto check back 2020-10-02 18:07:46 +02:00
antelle 7140a2d894 npm i
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2020-09-12 12:23:51 +02:00
antelle eb86603b47 Merge branch 'release-1.15' into master 2020-09-12 12:17:03 +02:00
antelle 1e1117c28b updated vulnerable dependencies 2020-09-12 12:16:41 +02:00
antelle f3de0d8391 Merge branch 'release-1.15' into master 2020-09-12 12:12:28 +02:00
antelle 4749be20b4 bump version 2020-09-12 12:10:15 +02:00
antelle 3eea00bf6d release notes 2020-09-12 12:06:38 +02:00
antelle d301287fdc readme 2020-09-12 12:05:45 +02:00
antelle 07f4090498 readme 2020-09-12 12:04:27 +02:00
antelle 067d3778d8 PKCE enabled on Dropbox 2020-09-12 12:04:16 +02:00
antelle daa250e8fa fix 1597: dropbox 2020-09-12 12:03:20 +02:00
antelle 91f992df39 up kdbxweb, fix #1591, fix #1598 2020-09-12 11:30:15 +02:00
antelle ecaf9a37da prettier 2020-09-09 21:42:37 +02:00
antelle 31cc2fbdd4 Merge branch 'opennomad-linux_auto_type' into release-1.15 2020-09-09 21:40:58 +02:00
Matthias Johnson 6e30687919 use windowactivate via xdotool 2020-09-08 12:31:33 -06:00
antelle dbf212f0df Merge branch 'master' into develop
# Conflicts:
#	release-notes.md
2020-08-08 14:29:11 +02:00
antelle af49645f8d Merge branch 'release-1.15'
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2020-08-08 14:25:41 +02:00
antelle 59bed7593d fix #1581: double-clicking KeeWeb icon in Dock 2020-08-08 14:24:47 +02:00
antelle 43ca723c75 bump version 2020-08-08 14:23:42 +02:00
antelle 855370074f fix #1553: empty fields display for templates 2020-08-08 14:11:12 +02:00
antelle 84f707d6cd fix #1581: double-clicking KeeWeb icon in Dock 2020-08-08 12:20:08 +02:00
antelle 17be9cf266 removed some old stuff from v1.15 2020-06-21 13:04:09 +02:00
antelle 0e3d01ade6 always using native argon2 in desktop 2020-06-21 13:02:06 +02:00
antelle 02fe2b1754 Merge branch 'hesalx-fix-macos-quit' into release-1.15 2020-06-15 17:46:18 +02:00
hesalx a0c553b3a9 allow the app to quit when not minimized and window is closed 2020-06-15 11:32:48 +03:00
antelle 62ea631269 Merge branch 'release-1.15'
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2020-06-13 19:30:36 +02:00
antelle b32d842394 bump version 2020-06-13 13:08:32 +02:00
antelle 0c38b5b9f1 fixed exe code signing 2020-06-13 13:08:16 +02:00
antelle dd0b20634c release notes 2020-06-13 13:08:09 +02:00
antelle f8319408f1 don't clear cache from the ci sandbox 2020-06-13 13:08:00 +02:00
antelle c451e92a9c fixed exe code signing 2020-06-13 12:31:48 +02:00
antelle 36cf70202d Merge branch 'master' into develop
# Conflicts:
#	desktop/app.js
#	release-notes.md
2020-06-13 12:30:48 +02:00
antelle 39fe83fdd1 Merge branch 'release-1.15'
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2020-06-13 10:54:59 +02:00
antelle cc5a465dc3 up 2020-06-13 10:54:31 +02:00
antelle 2018f04bdf fixed exports 2020-06-13 10:54:10 +02:00
antelle f0e79eab95 Merge branch 'release-1.15' 2020-06-13 10:25:22 +02:00
antelle fc1b2fbbaa fix #1532: crashes on some Windows version (updated electron) 2020-06-13 08:49:59 +02:00
antelle a8e89da5e9 up deps 2020-06-13 08:48:37 +02:00
antelle e369a6b7be bump version 2020-06-12 21:15:58 +02:00
antelle ae4d4398cd fix #1536: added a missing dependency on linux 2020-06-12 21:15:36 +02:00
antelle 5781d20615 Merge branch 'release-1.15'
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2020-06-11 19:54:52 +02:00
antelle a6e9521435 bump version 2020-06-11 19:53:53 +02:00
antelle 1e72711bc3 bump version script 2020-06-11 19:53:33 +02:00
antelle 1f793e2d00 bump version script 2020-06-11 19:53:21 +02:00
antelle af8d94d102 fix #1535: network errors in Dropbox and GDrive on Windows 2020-06-11 19:48:30 +02:00
antelle a424c3286a extracting files after signing 2020-06-10 20:27:55 +02:00
antelle 5f9b33506a Merge branch 'master' into release-1.15 2020-06-09 21:04:48 +02:00
antelle 65f09c6535 fix #1530: running from directories with hash symbols 2020-06-09 19:26:41 +02:00
antelle d7db5d2c77 removed some debugging code 2020-06-09 19:09:37 +02:00
antelle 8205b01c65 added temporary debug logging 2020-06-08 22:58:11 +02:00
antelle d9ae6f312f fix #1530: recursive creation of the portable directory 2020-06-08 19:12:59 +02:00
antelle aea9a513b4 removed a non-working link 2020-06-07 16:55:42 +02:00
antelle d3b65f6280 added a vimeo link 2020-06-07 13:53:15 +02:00
antelle 3bf53aa87f Merge branch 'master' into develop 2020-06-07 12:33:31 +02:00
442 changed files with 18059 additions and 43336 deletions
+2
View File
@@ -7,6 +7,8 @@
}],
"@babel/plugin-proposal-optional-catch-binding",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator",
"@babel/plugin-proposal-logical-assignment-operators",
"@babel/plugin-external-helpers"
]
}
+1 -4
View File
@@ -15,8 +15,5 @@ trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.nsh]
indent_size = 2
[{*.yml,*.yaml}]
[{*.nsh,*.yml,*.yaml,*.json}]
indent_size = 2
+2 -2
View File
@@ -48,9 +48,9 @@
"import/no-webpack-loader-syntax": "off",
"import/no-relative-parent-imports": "error",
"import/first": "error",
"import/no-namespace": "error",
"import/no-default-export": "error",
"babel/no-unused-expressions": "error"
"babel/no-unused-expressions": "error",
"node/no-callback-literal": "off"
},
"parserOptions": {
"sourceType": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
# These are supported funding model platforms
github: antelle
github: [keeweb, antelle]
patreon: # Replace with a single Patreon username
open_collective: keeweb
ko_fi: # Replace with a single Ko-fi username
+1 -1
View File
@@ -24,7 +24,7 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem.
**Environment**
Your user-agent (from Settings/Help section)
Please copy all version information from Settings/Help
**Kdbx File**
Does it happen on Demo or New database?
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: true
-8
View File
@@ -1,8 +0,0 @@
---
name: Discussion
about: Discuss any topic or idea
title: ''
labels: ''
assignees: ''
---
+1 -1
View File
@@ -4,7 +4,7 @@ COPY entrypoint.sh /entrypoint.sh
RUN apt-get update
RUN apt-get install -y build-essential git-core unzip curl pkg-config rpm
RUN curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash -
RUN curl -sL https://deb.nodesource.com/setup_15.x | sudo -E bash -
RUN apt-get install -y nodejs
RUN npm i -g grunt-cli
@@ -2,7 +2,4 @@
cd /github/workspace
npm ci
cd desktop
npm ci
cd /github/workspace
grunt desktop-linux
@@ -1,12 +0,0 @@
name: 'Publish release'
description: 'Publish a release and update the release notes'
inputs:
version:
description: 'Release version'
required: true
id:
description: 'Release ID'
required: true
runs:
using: 'node12'
main: 'dist/index.js'
File diff suppressed because it is too large Load Diff
-419
View File
@@ -1,419 +0,0 @@
{
"name": "publish-release",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@actions/core": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.4.tgz",
"integrity": "sha512-YJCEq8BE3CdN8+7HPZ/4DxJjk/OkZV2FFIf+DlZTC/4iBlzYCD5yjRR6eiOS5llO11zbRltIRuKAjMKaWTE6cg=="
},
"@actions/exec": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.4.tgz",
"integrity": "sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw==",
"requires": {
"@actions/io": "^1.0.1"
}
},
"@actions/github": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-2.1.1.tgz",
"integrity": "sha512-kAgTGUx7yf5KQCndVeHSwCNZuDBvPyxm5xKTswW2lofugeuC1AZX73nUUVDNaysnM9aKFMHv9YCdVJbg7syEyA==",
"requires": {
"@actions/http-client": "^1.0.3",
"@octokit/graphql": "^4.3.1",
"@octokit/rest": "^16.43.1"
}
},
"@actions/http-client": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.7.tgz",
"integrity": "sha512-PY3ys/XH5WMekkHyZhYSa/scYvlE5T/TV/T++vABHuY5ZRgtiBZkn2L2tV5Pv/xDCl59lSZb9WwRuWExDyAsSg==",
"requires": {
"tunnel": "0.0.6"
}
},
"@actions/io": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz",
"integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg=="
},
"@octokit/auth-token": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz",
"integrity": "sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg==",
"requires": {
"@octokit/types": "^2.0.0"
}
},
"@octokit/endpoint": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.0.tgz",
"integrity": "sha512-3nx+MEYoZeD0uJ+7F/gvELLvQJzLXhep2Az0bBSXagbApDvDW0LWwpnAIY/hb0Jwe17A0fJdz0O12dPh05cj7A==",
"requires": {
"@octokit/types": "^2.0.0",
"is-plain-object": "^3.0.0",
"universal-user-agent": "^5.0.0"
},
"dependencies": {
"universal-user-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-5.0.0.tgz",
"integrity": "sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q==",
"requires": {
"os-name": "^3.1.0"
}
}
}
},
"@octokit/graphql": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.3.1.tgz",
"integrity": "sha512-hCdTjfvrK+ilU2keAdqNBWOk+gm1kai1ZcdjRfB30oA3/T6n53UVJb7w0L5cR3/rhU91xT3HSqCd+qbvH06yxA==",
"requires": {
"@octokit/request": "^5.3.0",
"@octokit/types": "^2.0.0",
"universal-user-agent": "^4.0.0"
}
},
"@octokit/plugin-paginate-rest": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz",
"integrity": "sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q==",
"requires": {
"@octokit/types": "^2.0.1"
}
},
"@octokit/plugin-request-log": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz",
"integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw=="
},
"@octokit/plugin-rest-endpoint-methods": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz",
"integrity": "sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ==",
"requires": {
"@octokit/types": "^2.0.1",
"deprecation": "^2.3.1"
}
},
"@octokit/request": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.4.tgz",
"integrity": "sha512-qyj8G8BxQyXjt9Xu6NvfvOr1E0l35lsXtwm3SopsYg/JWXjlsnwqLc8rsD2OLguEL/JjLfBvrXr4az7z8Lch2A==",
"requires": {
"@octokit/endpoint": "^6.0.0",
"@octokit/request-error": "^2.0.0",
"@octokit/types": "^2.0.0",
"deprecation": "^2.0.0",
"is-plain-object": "^3.0.0",
"node-fetch": "^2.3.0",
"once": "^1.4.0",
"universal-user-agent": "^5.0.0"
},
"dependencies": {
"universal-user-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-5.0.0.tgz",
"integrity": "sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q==",
"requires": {
"os-name": "^3.1.0"
}
}
}
},
"@octokit/request-error": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.0.tgz",
"integrity": "sha512-rtYicB4Absc60rUv74Rjpzek84UbVHGHJRu4fNVlZ1mCcyUPPuzFfG9Rn6sjHrd95DEsmjSt1Axlc699ZlbDkw==",
"requires": {
"@octokit/types": "^2.0.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
},
"@octokit/rest": {
"version": "16.43.1",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz",
"integrity": "sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw==",
"requires": {
"@octokit/auth-token": "^2.4.0",
"@octokit/plugin-paginate-rest": "^1.1.1",
"@octokit/plugin-request-log": "^1.0.0",
"@octokit/plugin-rest-endpoint-methods": "2.4.0",
"@octokit/request": "^5.2.0",
"@octokit/request-error": "^1.0.2",
"atob-lite": "^2.0.0",
"before-after-hook": "^2.0.0",
"btoa-lite": "^1.0.0",
"deprecation": "^2.0.0",
"lodash.get": "^4.4.2",
"lodash.set": "^4.3.2",
"lodash.uniq": "^4.5.0",
"octokit-pagination-methods": "^1.1.0",
"once": "^1.4.0",
"universal-user-agent": "^4.0.0"
},
"dependencies": {
"@octokit/request-error": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.1.tgz",
"integrity": "sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA==",
"requires": {
"@octokit/types": "^2.0.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
}
}
},
"@octokit/types": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.5.1.tgz",
"integrity": "sha512-q4Wr7RexkPRrkQpXzUYF5Fj/14Mr65RyOHj6B9d/sQACpqGcStkHZj4qMEtlMY5SnD/69jlL9ItGPbDM0dR/dA==",
"requires": {
"@types/node": ">= 8"
}
},
"@types/node": {
"version": "13.11.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.11.0.tgz",
"integrity": "sha512-uM4mnmsIIPK/yeO+42F2RQhGUIs39K2RFmugcJANppXe6J1nvH87PvzPZYpza7Xhhs8Yn9yIAVdLZ84z61+0xQ=="
},
"@zeit/ncc": {
"version": "0.22.1",
"resolved": "https://registry.npmjs.org/@zeit/ncc/-/ncc-0.22.1.tgz",
"integrity": "sha512-Qq3bMuonkcnV/96jhy9SQYdh39NXHxNMJ1O31ZFzWG9n52fR2DLtgrNzhj/ahlEjnBziMLGVWDbaS9sf03/fEw==",
"dev": true
},
"atob-lite": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz",
"integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY="
},
"before-after-hook": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz",
"integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A=="
},
"btoa-lite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
"integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc="
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"requires": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"deprecation": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
},
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"requires": {
"once": "^1.4.0"
}
},
"execa": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
"integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
"requires": {
"cross-spawn": "^6.0.0",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
"p-finally": "^1.0.0",
"signal-exit": "^3.0.0",
"strip-eof": "^1.0.0"
}
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
"requires": {
"pump": "^3.0.0"
}
},
"is-plain-object": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz",
"integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==",
"requires": {
"isobject": "^4.0.0"
}
},
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
},
"isobject": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz",
"integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA=="
},
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
},
"lodash.set": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
"integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM="
},
"lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
},
"macos-release": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz",
"integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA=="
},
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
},
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
"integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
"requires": {
"path-key": "^2.0.0"
}
},
"octokit-pagination-methods": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz",
"integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ=="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"os-name": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz",
"integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==",
"requires": {
"macos-release": "^2.2.0",
"windows-release": "^3.1.0"
}
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
},
"path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
"integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
"requires": {
"shebang-regex": "^1.0.0"
}
},
"shebang-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
},
"signal-exit": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
},
"strip-eof": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
},
"tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
},
"universal-user-agent": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz",
"integrity": "sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg==",
"requires": {
"os-name": "^3.1.0"
}
},
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"requires": {
"isexe": "^2.0.0"
}
},
"windows-release": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.0.tgz",
"integrity": "sha512-2HetyTg1Y+R+rUgrKeUEhAG/ZuOmTrI1NBb3ZyAGQMYmOJjBBPe4MTodghRkmLJZHwkuPi02anbeGP+Zf401LQ==",
"requires": {
"execa": "^1.0.0"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
}
}
@@ -1,19 +0,0 @@
{
"name": "publish-release",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "ncc build src/main.js"
},
"author": "",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.4",
"@actions/exec": "^1.0.4",
"@actions/github": "^2.1.1"
},
"devDependencies": {
"@zeit/ncc": "^0.22.1"
}
}
@@ -1,48 +0,0 @@
const fs = require('fs');
const path = require('path');
const core = require('@actions/core');
const { GitHub, context } = require('@actions/github');
async function run() {
try {
const github = new GitHub(process.env.GITHUB_TOKEN);
const { owner, repo } = context.repo;
const version = core.getInput('version', { required: false });
const draft = false;
const release_id = core.getInput('release_id', { required: false });
const releaseNotesPath = path.join(__dirname, '../../../../release-notes.md');
const releaseNotes = fs.readFileSync(releaseNotesPath, 'utf8');
const regex = new RegExp(
`#####\\s+v${version.replace(/\./g, '\\.')}.*?\n([\\s\\S]*?)\n#####`
);
const bodyReleaseNotes = releaseNotes
.match(regex)[1]
.trim()
.replace(/\s*\n/g, '\n');
const bodyTemplate =
'{release_notes}\n\n' +
'Want to keep releases happening? ' +
'Donate to KeeWeb on [OpenCollective](https://opencollective.com/keeweb). ' +
'Thank you!';
const body = bodyTemplate.replace('{release_notes}', bodyReleaseNotes);
console.log(`Updating release with notes:\n${body}`);
await github.repos.updateRelease({
owner,
repo,
release_id,
body,
draft
});
} catch (error) {
core.setFailed(error.message);
}
}
run();
+79 -197
View File
@@ -9,14 +9,20 @@ jobs:
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-tag@v2
uses: keeweb/get-git-tag@v3.0.2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@v2
with:
repository: keeweb/keeweb
ref: ${{ github.repository == 'keeweb/keeweb' && github.sha || 'develop' }}
- name: Upgrade Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
registry-url: 'https://registry.npmjs.org'
- name: Install npm modules
run: npm ci
- name: Test
@@ -28,6 +34,15 @@ jobs:
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.html
path: dist
- name: Write secrets
env:
VIRUS_TOTAL: ${{ secrets.VIRUS_TOTAL }}
run: |
mkdir keys
echo "$VIRUS_TOTAL" > keys/virus-total.json
- name: Check on VirusTotal
run: grunt virustotal
if: ${{ github.repository == 'keeweb/keeweb' }}
linux:
runs-on: ubuntu-latest
@@ -36,11 +51,12 @@ jobs:
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-tag@v2
uses: keeweb/get-git-tag@v3.0.2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@v2
with:
repository: keeweb/keeweb
ref: ${{ github.repository == 'keeweb/keeweb' && github.sha || 'develop' }}
@@ -84,11 +100,6 @@ jobs:
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x86_64.rpm
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x86_64.rpm
- name: Upload update artifact
uses: actions/upload-artifact@v1
with:
name: UpdateDesktop.zip
path: dist/desktop/UpdateDesktop.zip
darwin:
runs-on: macos-latest
@@ -97,11 +108,12 @@ jobs:
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-tag@v2
uses: keeweb/get-git-tag@v3.0.2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@v2
with:
repository: keeweb/keeweb
ref: ${{ github.repository == 'keeweb/keeweb' && github.sha || 'develop' }}
@@ -110,11 +122,13 @@ jobs:
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.html
path: dist
- name: Upgrade Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
registry-url: 'https://registry.npmjs.org'
- name: Install npm modules
run: npm ci
- name: Install desktop npm modules
working-directory: desktop
run: npm ci
- name: Install grunt
run: sudo npm i -g grunt-cli
- name: Write secrets
@@ -122,21 +136,29 @@ jobs:
CODESIGN: ${{ secrets.CODESIGN }}
APPLE_DEPLOY_PASSWORD: ${{ secrets.APPLE_DEPLOY_PASSWORD }}
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
APPLE_PROVISIONING_PROFILE: ${{ secrets.APPLE_PROVISIONING_PROFILE }}
run: |
mkdir keys
echo "$CODESIGN" > keys/codesign.json
xcrun altool --store-password-in-keychain-item "AC_PASSWORD" -u "$APPLE_ID_USERNAME" -p "$APPLE_DEPLOY_PASSWORD"
- uses: keeweb/import-codesign-certs@v1
echo "$APPLE_PROVISIONING_PROFILE" | base64 -d > keys/keeweb.provisionprofile
- name: Import certificates
uses: keeweb/import-codesign-certs@v1
with:
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
- name: Grunt
run: grunt desktop-darwin ${{ github.repository != 'keeweb/keeweb' && '--skip-sign' || '' }}
- name: Upload dmg artifact
run: grunt --max-old-space-size=4096 desktop-darwin ${{ github.repository != 'keeweb/keeweb' && '--skip-sign' || '' }}
- name: Upload x64 dmg artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.mac.dmg
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.mac.dmg
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.mac.x64.dmg
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.mac.x64.dmg
- name: Upload arm64 dmg artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.mac.arm64.dmg
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.mac.arm64.dmg
win32:
runs-on: windows-latest
@@ -145,11 +167,12 @@ jobs:
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-tag@v2
uses: keeweb/get-git-tag@v3.0.2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@v2
with:
repository: keeweb/keeweb
ref: ${{ github.repository == 'keeweb/keeweb' && github.sha || 'develop' }}
@@ -158,11 +181,13 @@ jobs:
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.html
path: dist
- name: Upgrade Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
registry-url: 'https://registry.npmjs.org'
- name: Install npm modules
run: npm ci
- name: Install desktop npm modules
working-directory: desktop
run: npm ci
- name: Install grunt
run: npm i -g grunt-cli
- name: Write secrets
@@ -220,20 +245,27 @@ jobs:
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-tag@v2
uses: keeweb/get-git-tag@v3.0.2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
- name: Setup GCloud
uses: google-github-actions/setup-gcloud@master
with:
version: '285.0.0'
service_account_key: ${{ secrets.GCP_SA_KEY }}
export_default_credentials: true
- uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@v2
with:
repository: keeweb/keeweb
path: keeweb
ref: ${{ github.repository == 'keeweb/keeweb' && github.sha || 'develop' }}
- name: Upgrade Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
registry-url: 'https://registry.npmjs.org'
- name: Install npm modules
working-directory: keeweb
run: npm ci
@@ -267,10 +299,15 @@ jobs:
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x86_64.rpm
path: assets
- name: Download darwin.dmg artifact
- name: Download darwin.x64.dmg artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.mac.dmg
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.mac.x64.dmg
path: assets
- name: Download darwin.arm64.dmg artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.mac.arm64.dmg
path: assets
- name: Download win32.ia32.exe artifact
uses: actions/download-artifact@v1
@@ -302,11 +339,6 @@ jobs:
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.zip
path: assets
- name: Download update artifact
uses: actions/download-artifact@v1
with:
name: UpdateDesktop.zip
path: assets
- name: Zip html
working-directory: html
run: zip -vr ../assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.html.zip .
@@ -337,173 +369,22 @@ jobs:
run: cp -r html keeweb/package/docker/dist
- name: Build the Docker image
working-directory: keeweb
run: docker build -t antelle/keeweb:latest package/docker
- name: Tag the Docker image
run: docker tag antelle/keeweb:latest antelle/keeweb:${{ steps.get_tag.outputs.tag }}
- name: Push the Docker image to the registry
run: docker build -t antelle/keeweb:latest -t antelle/keeweb:${{ steps.get_tag.outputs.tag }} package/docker
- name: Push the Docker image to dockerhub
if: ${{ github.repository == 'keeweb/keeweb' }}
run: docker push antelle/keeweb
- name: Create a GitHub release
id: create_release
uses: actions/create-release@latest
run: docker push --all-tags antelle/keeweb
- name: Extract release notes
run: node util/extract-release-notes.js --version=${{ steps.get_tag.outputs.tag }} --output=tmp/release-body.md
working-directory: keeweb
- name: Publish GitHub release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Desktop apps v${{ steps.get_tag.outputs.tag }}
draft: true
prerelease: false
- name: Upload html asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.html.zip
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.html.zip
asset_content_type: application/octet-stream
- name: Upload linux.AppImage asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.AppImage
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.AppImage
asset_content_type: application/octet-stream
- name: Upload linux.snap asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.snap
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.snap
asset_content_type: application/octet-stream
- name: Upload linux.deb asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.deb
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.deb
asset_content_type: application/octet-stream
- name: Upload linux.zip asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.zip
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.zip
asset_content_type: application/octet-stream
- name: Upload linux.rpm asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x86_64.rpm
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x86_64.rpm
asset_content_type: application/octet-stream
- name: Upload darwin.dmg asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.mac.dmg
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.mac.dmg
asset_content_type: application/octet-stream
- name: Upload win32.ia32.exe asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.exe
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.exe
asset_content_type: application/octet-stream
- name: Upload win32.ia32.zip asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.zip
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.zip
asset_content_type: application/octet-stream
- name: Upload win32.x64.exe asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.exe
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.exe
asset_content_type: application/octet-stream
- name: Upload win32.x64.zip asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.zip
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.zip
asset_content_type: application/octet-stream
- name: Upload win32.arm64.exe asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.exe
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.exe
asset_content_type: application/octet-stream
- name: Upload win32.arm64.zip asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.zip
asset_name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.zip
asset_content_type: application/octet-stream
- name: Upload update asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/UpdateDesktop.zip
asset_name: UpdateDesktop.zip
asset_content_type: application/octet-stream
- name: Upload verify.sign asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/Verify.sign.sha256
asset_name: Verify.sign.sha256
asset_content_type: application/octet-stream
- name: Upload verify.sha asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: assets/Verify.sha256
asset_name: Verify.sha256
asset_content_type: application/octet-stream
- name: Publish the GitHub release
uses: ./keeweb/.github/actions/publish-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
version: ${{ steps.get_tag.outputs.tag }}
release_id: ${{ steps.create_release.outputs.id }}
tag_name: v${{ steps.get_tag.outputs.tag }}
name: Desktop apps v${{ steps.get_tag.outputs.tag }}
files: assets/*
body_path: keeweb/tmp/release-body.md
- name: Checkout gh-pages
uses: actions/checkout@v2
with:
@@ -535,6 +416,7 @@ jobs:
if: ${{ github.repository == 'keeweb/keeweb' }}
run: gsutil -m rsync -r -d -x "^\." gh-pages gs://app.keeweb.info/
- name: Purge CloudFlare cache
if: ${{ github.repository == 'keeweb/keeweb' }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
run: |
-21
View File
@@ -1,21 +0,0 @@
name: Deploy
on:
push:
branches: [ 'gh-pages' ]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
ref: 'gh-pages'
- uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
with:
version: '285.0.0'
service_account_key: ${{ secrets.GCP_SA_KEY }}
export_default_credentials: true
- name: Restore git mtime
run: curl -Ss https://raw.githubusercontent.com/MestreLion/git-tools/f3cc70b73200154d027554714c354f35a08680ed/git-restore-mtime | python3
- name: Sync
run: gsutil -m rsync -r -d -x "^\.|^[a-f\d-]{36}$|^README\.md$" . gs://app.keeweb.info/
+1 -1
View File
@@ -14,6 +14,6 @@ obj/
xcuserdata/
*.suo
.idea/
.vscode
.vscode/
*.iml
test/dist/
+194 -147
View File
@@ -2,6 +2,7 @@
const fs = require('fs-extra');
const path = require('path');
const { execSync } = require('child_process');
const debug = require('debug');
const webpackConfig = require('./build/webpack.config');
@@ -24,21 +25,30 @@ module.exports = function (grunt) {
const dt = date.toISOString().replace(/T.*/, '');
const year = date.getFullYear();
const minElectronVersionForUpdate = '9.0.1';
const zipCommentPlaceholderPart = 'zip_comment_placeholder_that_will_be_replaced_with_hash';
const zipCommentPlaceholder =
zipCommentPlaceholderPart + '.'.repeat(512 - zipCommentPlaceholderPart.length);
const electronVersion = pkg.dependencies.electron.replace(/^\D/, '');
const skipSign = grunt.option('skip-sign');
const getCodeSignConfig = () =>
skipSign ? { identities: {} } : require('./keys/codesign.json');
let sha = grunt.option('commit-sha');
if (!sha) {
try {
sha = execSync('git rev-parse --short HEAD').toString('utf8').trim();
} catch (e) {
grunt.warn(
"Cannot get commit sha from git. It's recommended to build KeeWeb from a git repo " +
'because commit sha is displayed in the UI, however if you would like to build from a folder, ' +
'you can override what will be displayed in the UI with --commit-sha=xxx.'
);
}
}
grunt.log.writeln(`Building KeeWeb v${pkg.version} (${sha})`);
const webpackOptions = {
date,
beta: !!grunt.option('beta'),
get sha() {
return grunt.config.get('gitinfo.local.branch.current.shortSHA');
}
sha,
appleTeamId: '3LE7JZ657W'
};
const windowsAppVersionString = {
@@ -49,18 +59,35 @@ module.exports = function (grunt) {
InternalName: 'KeeWeb'
};
const appdmgOptions = (arch) => ({
title: 'KeeWeb',
icon: 'graphics/icon.icns',
background: 'graphics/dmg-background.png',
'background-color': '#E0E6F9',
'icon-size': 80,
window: { size: { width: 658, height: 498 } },
contents: [
{ x: 438, y: 344, type: 'link', path: '/Applications' },
{
x: 192,
y: 344,
type: 'file',
path: `tmp/desktop/KeeWeb-darwin-${arch}/KeeWeb.app`
}
]
});
const linuxDependencies = [
'libappindicator1',
'libgconf-2-4',
'gnome-keyring',
'libxtst6',
'libx11-6',
'libatspi2.0-0'
];
grunt.initConfig({
noop: { noop: {} },
gitinfo: {
branch: {
current: {
SHA: 'Current HEAD SHA',
shortSHA: 'Current HEAD short SHA',
name: 'Current branch name',
lastCommitTime: 'Last commit time'
}
}
},
clean: {
dist: ['dist', 'tmp'],
desktop: ['tmp/desktop', 'dist/desktop']
@@ -78,11 +105,6 @@ module.exports = function (grunt) {
expand: true,
nonull: true
},
favicon: {
src: 'app/favicon.png',
dest: 'tmp/favicon.png',
nonull: true
},
icons: {
cwd: 'app/icons/',
src: ['*.png', '*.svg'],
@@ -111,13 +133,6 @@ module.exports = function (grunt) {
expand: true,
nonull: true
},
fonts: {
src: 'node_modules/font-awesome/fonts/fontawesome-webfont.*',
dest: 'tmp/fonts/',
nonull: true,
expand: true,
flatten: true
},
'desktop-html': {
src: 'dist/index.html',
dest: 'tmp/desktop/app/index.html',
@@ -130,26 +145,8 @@ module.exports = function (grunt) {
expand: true,
nonull: true
},
'desktop-update': {
cwd: 'tmp/desktop/keeweb-linux-x64/resources/',
src: 'app.asar',
dest: 'tmp/desktop/update/',
expand: true,
nonull: true
},
'desktop-update-helper': {
src: ['helper/darwin/KeeWebHelper', 'helper/win32/KeeWebHelper.exe'],
dest: 'tmp/desktop/update/',
nonull: true
},
'desktop-darwin-helper': {
src: 'helper/darwin/KeeWebHelper',
dest: 'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app/Contents/Resources/',
nonull: true,
options: { mode: '0755' }
},
'desktop-darwin-installer-helper': {
cwd: 'package/osx/KeeWeb Installer.app',
'desktop-darwin-installer-helper-x64': {
cwd: 'tmp/desktop/KeeWeb Installer.app',
src: '**',
dest:
'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app/Contents/Installer/KeeWeb Installer.app',
@@ -157,20 +154,14 @@ module.exports = function (grunt) {
nonull: true,
options: { mode: true }
},
'desktop-windows-helper-x64': {
src: 'helper/win32/KeeWebHelper.exe',
dest: 'tmp/desktop/KeeWeb-win32-x64/Resources/',
nonull: true
},
'desktop-windows-helper-ia32': {
src: 'helper/win32/KeeWebHelper.exe',
dest: 'tmp/desktop/KeeWeb-win32-ia32/Resources/',
nonull: true
},
'desktop-windows-helper-arm64': {
src: 'helper/win32/KeeWebHelper.exe',
dest: 'tmp/desktop/KeeWeb-win32-arm64/Resources/',
nonull: true
'desktop-darwin-installer-helper-arm64': {
cwd: 'tmp/desktop/KeeWeb Installer.app',
src: '**',
dest:
'tmp/desktop/KeeWeb-darwin-arm64/KeeWeb.app/Contents/Installer/KeeWeb Installer.app',
expand: true,
nonull: true,
options: { mode: true }
},
'desktop-win32-dist-x64': {
src: 'tmp/desktop/KeeWeb.win.x64.exe',
@@ -187,11 +178,16 @@ module.exports = function (grunt) {
dest: `dist/desktop/KeeWeb-${pkg.version}.win.arm64.exe`,
nonull: true
},
'native-modules-darwin': {
'native-modules-darwin-x64': {
src: 'node_modules/@keeweb/keeweb-native-modules/*-darwin-x64.node',
dest: 'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app/Contents/Resources/',
nonull: true
},
'native-modules-darwin-arm64': {
src: 'node_modules/@keeweb/keeweb-native-modules/*-darwin-arm64.node',
dest: 'tmp/desktop/KeeWeb-darwin-arm64/KeeWeb.app/Contents/Resources/',
nonull: true
},
'native-modules-win32-x64': {
src: 'node_modules/@keeweb/keeweb-native-modules/*-win32-x64.node',
dest: 'tmp/desktop/KeeWeb-win32-x64/resources/',
@@ -226,6 +222,52 @@ module.exports = function (grunt) {
src: `tmp/desktop/electron-builder/keeweb-${pkg.version}.AppImage`,
dest: `dist/desktop/KeeWeb-${pkg.version}.linux.AppImage`,
nonull: true
},
'darwin-installer-icon': {
src: 'graphics/icon.icns',
dest: 'tmp/desktop/KeeWeb Installer.app/Contents/Resources/applet.icns',
nonull: true
},
'native-messaging-host-darwin-x64': {
src:
'node_modules/@keeweb/keeweb-native-messaging-host/darwin-x64/keeweb-native-messaging-host',
dest:
'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app/Contents/MacOS/util/keeweb-native-messaging-host',
nonull: true,
options: { mode: '0755' }
},
'native-messaging-host-darwin-arm64': {
src:
'node_modules/@keeweb/keeweb-native-messaging-host/darwin-arm64/keeweb-native-messaging-host',
dest:
'tmp/desktop/KeeWeb-darwin-arm64/KeeWeb.app/Contents/MacOS/util/keeweb-native-messaging-host',
nonull: true,
options: { mode: '0755' }
},
'native-messaging-host-linux-x64': {
src:
'node_modules/@keeweb/keeweb-native-messaging-host/linux-x64/keeweb-native-messaging-host',
dest: 'tmp/desktop/keeweb-linux-x64/keeweb-native-messaging-host',
nonull: true,
options: { mode: '0755' }
},
'native-messaging-host-win32-x64': {
src:
'node_modules/@keeweb/keeweb-native-messaging-host/win32-x64/keeweb-native-messaging-host.exe',
dest: 'tmp/desktop/KeeWeb-win32-x64/keeweb-native-messaging-host.exe',
nonull: true
},
'native-messaging-host-win32-ia32': {
src:
'node_modules/@keeweb/keeweb-native-messaging-host/win32-ia32/keeweb-native-messaging-host.exe',
dest: 'tmp/desktop/KeeWeb-win32-ia32/keeweb-native-messaging-host.exe',
nonull: true
},
'native-messaging-host-win32-arm64': {
src:
'node_modules/@keeweb/keeweb-native-messaging-host/win32-arm64/keeweb-native-messaging-host.exe',
dest: 'tmp/desktop/KeeWeb-win32-arm64/keeweb-native-messaging-host.exe',
nonull: true
}
},
eslint: {
@@ -233,7 +275,8 @@ module.exports = function (grunt) {
desktop: ['desktop/**/*.js', '!desktop/node_modules/**'],
build: ['Gruntfile.js', 'grunt.*.js', 'build/**/*.js', 'webpack.config.js'],
plugins: ['plugins/**/*.js'],
util: ['util/**/*.js']
util: ['util/**/*.js'],
installer: ['package/osx/installer.js']
},
inline: {
app: {
@@ -246,7 +289,7 @@ module.exports = function (grunt) {
algo: 'sha512',
expected: {
style: 1,
script: 3
script: 1
}
},
app: {
@@ -266,20 +309,20 @@ module.exports = function (grunt) {
}
},
'string-replace': {
manifest: {
'update-manifest': {
options: {
replacements: [
{
pattern: '# YYYY-MM-DD:v0.0.0',
replacement: '# ' + dt + ':v' + pkg.version
pattern: /"version":\s*".*?"/,
replacement: `"version": "${pkg.version}"`
},
{
pattern: '# updmin:v0.0.0',
replacement: '# updmin:v' + minElectronVersionForUpdate
pattern: /"date":\s*".*?"/,
replacement: `"date": "${dt}"`
}
]
},
files: { 'dist/manifest.appcache': 'app/manifest.appcache' }
files: { 'dist/update.json': 'app/update.json' }
},
'service-worker': {
options: { replacements: [{ pattern: '0.0.0', replacement: pkg.version }] },
@@ -337,20 +380,30 @@ module.exports = function (grunt) {
asar: true,
appCopyright: `Copyright © ${year} Antelle`,
appVersion: pkg.version,
buildVersion: '<%= gitinfo.local.branch.current.shortSHA %>'
buildVersion: sha
},
linux: {
options: {
name: 'keeweb',
platform: 'linux',
arch: ['x64'],
arch: 'x64',
icon: 'graphics/icon.ico'
}
},
darwin: {
'darwin-x64': {
options: {
platform: 'darwin',
arch: ['x64'],
arch: 'x64',
icon: 'graphics/icon.icns',
appBundleId: 'net.antelle.keeweb',
appCategoryType: 'public.app-category.productivity',
extendInfo: 'package/osx/extend.plist'
}
},
'darwin-arm64': {
options: {
platform: 'darwin',
arch: 'arm64',
icon: 'graphics/icon.icns',
appBundleId: 'net.antelle.keeweb',
appCategoryType: 'public.app-category.productivity',
@@ -410,26 +463,37 @@ module.exports = function (grunt) {
category: 'Utility'
},
rpm: {
// depends: ['libappindicator1', 'libgconf-2-4']
// depends: linuxDependencies
},
snap: {
stagePackages: ['libappindicator1', 'libgconf-2-4']
stagePackages: linuxDependencies
}
}
}
}
},
'electron-patch': {
'win32-x64': 'tmp/desktop/KeeWeb-win32-x64/KeeWeb.exe',
'win32-ia32': 'tmp/desktop/KeeWeb-win32-ia32/KeeWeb.exe',
'win32-arm64': 'tmp/desktop/KeeWeb-win32-arm64/KeeWeb.exe',
'darwin-x64': 'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app',
'darwin-arm64': 'tmp/desktop/KeeWeb-darwin-arm64/KeeWeb.app',
'linux': 'tmp/desktop/KeeWeb-linux-x64/keeweb'
},
osacompile: {
options: {
language: 'JavaScript'
},
installer: {
files: {
'tmp/desktop/KeeWeb Installer.app': 'package/osx/installer.js'
}
}
},
compress: {
options: {
level: 6
},
'desktop-update': {
options: {
archive: 'dist/desktop/UpdateDesktop.zip',
comment: zipCommentPlaceholder
},
files: [{ cwd: 'tmp/desktop/update', src: '**', expand: true, nonull: true }]
},
'win32-x64': {
options: { archive: `dist/desktop/KeeWeb-${pkg.version}.win.x64.zip` },
files: [{ cwd: 'tmp/desktop/KeeWeb-win32-x64', src: '**', expand: true }]
@@ -451,34 +515,20 @@ module.exports = function (grunt) {
}
},
appdmg: {
options: {
title: 'KeeWeb',
icon: 'graphics/icon.icns',
background: 'graphics/background.png',
'background-color': '#E0E6F9',
'icon-size': 80,
window: { size: { width: 658, height: 498 } },
contents: [
{ x: 438, y: 344, type: 'link', path: '/Applications' },
{
x: 192,
y: 344,
type: 'file',
path: 'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app'
}
]
x64: {
options: appdmgOptions('x64'),
dest: `dist/desktop/KeeWeb-${pkg.version}.mac.x64.dmg`
},
app: {
dest: `dist/desktop/KeeWeb-${pkg.version}.mac.dmg`
arm64: {
options: appdmgOptions('arm64'),
dest: `dist/desktop/KeeWeb-${pkg.version}.mac.arm64.dmg`
}
},
nsis: {
options: {
vars: {
version: pkg.version,
rev() {
return grunt.config.get('gitinfo.local.branch.current.shortSHA');
},
rev: sha,
homepage: pkg.homepage
}
},
@@ -542,9 +592,7 @@ module.exports = function (grunt) {
description: pkg.description,
author: pkg.author,
homepage: pkg.homepage,
rev() {
return grunt.config.get('gitinfo.local.branch.current.shortSHA');
}
rev: sha
}
},
'linux-x64': {
@@ -554,7 +602,7 @@ module.exports = function (grunt) {
pkgName: `KeeWeb-${pkg.version}.linux.x64.deb`,
targetDir: 'dist/desktop',
appName: 'KeeWeb',
depends: 'libappindicator1, libgconf-2-4',
depends: linuxDependencies.join(', '),
scripts: {
postinst: 'package/deb/scripts/postinst'
}
@@ -577,47 +625,30 @@ module.exports = function (grunt) {
]
}
},
'sign-archive': {
'desktop-update': {
options: {
file: 'dist/desktop/UpdateDesktop.zip',
signature: zipCommentPlaceholder
}
}
},
'sign-desktop-files': {
'desktop-update': {
options: {
path: 'tmp/desktop/update'
}
}
},
'validate-desktop-update': {
desktop: {
options: {
file: 'dist/desktop/UpdateDesktop.zip',
expected: [
'app.asar',
'helper/darwin/KeeWebHelper',
'helper/win32/KeeWebHelper.exe'
],
expectedCount: 7,
publicKey: 'app/resources/public-key.pem'
}
}
},
'osx-sign': {
options: {
get identity() {
return getCodeSignConfig().identities.app;
},
hardenedRuntime: true,
entitlements: 'package/osx/entitlements.mac.plist',
'entitlements-inherit': 'package/osx/entitlements.mac.plist',
entitlements: 'package/osx/entitlements.plist',
'entitlements-inherit': 'package/osx/entitlements-inherit.plist',
'gatekeeper-assess': false
},
desktop: {
'desktop-x64': {
options: {
'provisioning-profile': './keys/keeweb.provisionprofile'
},
src: 'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app'
},
'desktop-arm64': {
options: {
'provisioning-profile': './keys/keeweb.provisionprofile'
},
src: 'tmp/desktop/KeeWeb-darwin-arm64/KeeWeb.app'
},
'installer': {
src: 'tmp/desktop/KeeWeb Installer.app'
}
},
notarize: {
@@ -631,8 +662,11 @@ module.exports = function (grunt) {
return getCodeSignConfig().teamId;
}
},
desktop: {
'desktop-x64': {
src: 'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app'
},
'desktop-arm64': {
src: 'tmp/desktop/KeeWeb-darwin-arm64/KeeWeb.app'
}
},
'sign-exe': {
@@ -649,6 +683,8 @@ module.exports = function (grunt) {
options: {
files: {
'tmp/desktop/KeeWeb-win32-x64/KeeWeb.exe': 'KeeWeb',
'tmp/desktop/KeeWeb-win32-x64/keeweb-native-messaging-host.exe':
'KeeWeb Native Messaging Host',
'tmp/desktop/KeeWeb-win32-x64/ffmpeg.dll': '',
'tmp/desktop/KeeWeb-win32-x64/libEGL.dll':
'ANGLE libEGL Dynamic Link Library',
@@ -661,6 +697,8 @@ module.exports = function (grunt) {
options: {
files: {
'tmp/desktop/KeeWeb-win32-ia32/KeeWeb.exe': 'KeeWeb',
'tmp/desktop/KeeWeb-win32-ia32/keeweb-native-messaging-host.exe':
'KeeWeb Native Messaging Host',
'tmp/desktop/KeeWeb-win32-ia32/ffmpeg.dll': '',
'tmp/desktop/KeeWeb-win32-ia32/libEGL.dll':
'ANGLE libEGL Dynamic Link Library',
@@ -673,6 +711,8 @@ module.exports = function (grunt) {
options: {
files: {
'tmp/desktop/KeeWeb-win32-arm64/KeeWeb.exe': 'KeeWeb',
'tmp/desktop/KeeWeb-win32-arm64/keeweb-native-messaging-host.exe':
'KeeWeb Native Messaging Host',
'tmp/desktop/KeeWeb-win32-arm64/ffmpeg.dll': '',
'tmp/desktop/KeeWeb-win32-arm64/libEGL.dll':
'ANGLE libEGL Dynamic Link Library',
@@ -730,10 +770,7 @@ module.exports = function (grunt) {
sign: 'dist/desktop/Verify.sign.sha256'
},
files: {
'dist/desktop/Verify.sha256': [
'dist/desktop/KeeWeb-*',
'dist/desktop/UpdateDesktop.zip'
]
'dist/desktop/Verify.sha256': ['dist/desktop/KeeWeb-*']
}
}
},
@@ -742,6 +779,16 @@ module.exports = function (grunt) {
headless: true
},
default: 'test/runner.html'
},
virustotal: {
options: {
prefix: `keeweb.v${pkg.version}-${sha}.`,
timeout: 10 * 60 * 1000,
get apiKey() {
return require('./keys/virus-total.json').apiKey;
}
},
html: 'dist/index.html'
}
});
};
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 Antelle
Copyright (c) 2021 Antelle https://antelle.net
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1 -1
View File
@@ -90,7 +90,7 @@ Please note: donation does not imply any type of service contract.
Notable contributions to KeeWeb:
- Florian Reuschel ([@Loilo](https://github.com/Loilo)): [German translation](http://keeweb.oneskyapp.com/collaboration/translate/project/project/173183/language/550)
- Florian Reuschel ([@Loilo](https://github.com/Loilo)): [German translation](https://keeweb.oneskyapp.com/collaboration/translate/project/project/173183/language/550)
- Dennis Ploeger ([@dploeger](https://github.com/dploeger)): [auto-type improvements](https://github.com/keeweb/keeweb/pulls?q=is%3Apr+is%3Aclosed+author%3Adploeger)
- Hackmanit ([hackmanit.de](https://www.hackmanit.de)): [penetration test](https://www.hackmanit.de/en/blog-en/104-pro-bono-penetration-test-keeweb)
- Peter Bittner ([@bittner](https://github.com/bittner)): [Wikipedia article](https://en.wikipedia.org/wiki/KeeWeb)
+16
View File
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>KeeWeb</title>
</head>
<body>
<script>
window.opener.postMessage(
{ storage: 'teams', search: location.search },
window.location.origin
);
window.close();
</script>
</body>
</html>
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

+1 -1
View File
@@ -1 +1 @@
<svg version="1" xmlns="http://www.w3.org/2000/svg" width="922.667" height="922.667" viewBox="0 0 692.000000 692.000000"><path d="M266.9 57.4c-.2.2-4 .6-8.3 1-4.4.4-8.2.8-8.5 1-.3.2-2.7.7-5.3 1-5.5.7-21.6 4.8-29.8 7.5-46.3 15.4-93.3 48.9-126.5 90.1-25.7 31.8-46.8 74.8-52.9 107.4-.5 2.8-1.5 8.3-2.1 12.1-1.9 11.6-2.6 35.9-1.3 45 .3 2.7.9 7 1.2 9.5 3.3 27.4 16.9 55.4 36.1 74.5 12.1 12.1 27.3 22.7 40 27.8 3.9 1.6 7.9 3.3 9 3.7 1.1.5 4.3 1.5 7 2.4 4.4 1.3 7.1 1.9 18.5 4.1 12.1 2.4 42.4 2.4 56.5 0 1.7-.3 4.4-.7 6-1 1.7-.3 4.1-.7 5.5-1 1.4-.3 3.7-.7 5.1-1 4.5-.8 19.1-5.2 28.4-8.6 16.8-6.1 42.9-20.2 59.4-32.2l5.3-3.9 130.7 130.5C567.7 654 571.7 657.8 577.5 660c6.2 2.2 15.3 2.9 21.4 1.6 12.8-2.7 27.8-15.7 34.7-30.1 2.5-5.3 2.9-7.1 2.9-15-.1-15.8 1.1-14.3-49.6-65.1l-44-44 18.1-18.2 18.1-18.1 6.7 7.1c20.7 21.9 32.6 32.8 38.6 35.3 5.6 2.4 11.5-1.2 26.5-15.8 16.1-15.8 21.8-23.8 20-28.6-.9-2.3-4.6-7.8-7.4-10.7-.5-.6-4.1-4.8-8-9.2-7.2-8.3-64.7-65.9-91-91.1-8.2-7.9-16-14.7-17.3-15.2-6.8-2.6-12.6.8-28.4 16.3-13.1 12.9-19.8 21.8-19.8 26.3 0 5.6 13.3 21 37.4 43.4l5.8 5.4-18.1 18.1-18.1 18.1-67-67c-36.9-36.8-67-67.3-67-67.7 0-.4 3-5.1 6.7-10.4 16.1-23.5 30.3-53.8 35.8-76.4.7-2.8 4.3-22.5 5.3-28.5 1.1-6.5.8-41.1-.3-48.5-1.4-9.2-6.4-26.8-10-35.3-9.9-23.3-28.4-44.9-50.5-58.8-21.8-13.8-47.4-20.5-78.6-20.7-7.3-.1-13.3 0-13.5.2zm22 74c12.1 2.8 22.6 7.6 31.1 14.4 13.3 10.7 23.7 27.4 26.3 42.7 6 34.4-9.5 65.3-40.5 80.7-16.1 7.9-40.9 8.5-58.1 1.3-4.4-1.8-5.2-1.9-4-.3 1.1 1.4 4 9.8 4.8 13.9 5.3 25.7-.6 47.7-17.7 66-8.9 9.6-19.7 16.4-31.3 19.8-9.7 2.9-11.7 3.1-22.2 3.2-24.8 0-47.2-12.3-61-33.3-15.4-23.7-15.3-56.4.4-79.3 11.1-16.2 26.6-27.1 44.3-31.1 14.4-3.2 32.9-1.7 43.9 3.7 2 .9 3.7 1.6 3.8 1.5.2-.2-.8-3.4-2.2-7.3-3.4-9.7-3.7-11.4-4-23.3-.4-17.1 3.8-30.7 13.8-44 11.4-15.2 26.5-24.8 44.7-28.4 6.2-1.2 22.9-1.4 27.9-.2z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-64 -64 640 640"><path d="M136.736 512c-4.892-.008-10.322-2.061-15.92-4.997-11.941-6.265-20.13-14.185-13.293-27.218l166.101-316.638c-1.972-1.022-3.619-1.88-4.743-2.469-59.892-31.418-86.463-77.87-64.952-118.877 24.726-47.135 101.304-55.69 171.042-19.107 69.738 36.584 106.227 104.451 81.501 151.586-21.264 40.536-76.784 44.151-132.61 14.866-1.538-.807-4.033-2.103-6.857-3.567L150.768 502.471c-3.632 6.924-8.488 9.538-14.032 9.529zm-30.582-67.835l-3.144-1.649-.7-.366-42.546-22.32v-.001l-10.256-5.38c-1.738-.911-2.407-3.057-1.496-4.794l13.465-25.667c.906-1.728 3.036-2.399 4.768-1.509l.028.013 20.513 10.761c1.736.911 3.883.241 4.794-1.496l13.466-25.667c.911-1.737.24-3.883-1.497-4.794l-10.256-5.382-10.256-5.38c-1.737-.911-2.406-3.057-1.495-4.794l13.464-25.669c.911-1.737 3.058-2.406 4.794-1.494l10.258 5.38 26.697 14.005 16.548 8.681c.007.003.014.007.021.011l3.125 1.639-14.142 26.957-.973 1.855c0 .001-.001.003-.002.004l-20.063 38.245-2.041 3.888-11.424 21.779c-.002.004-.005.009-.008.014zm67.232-128.162l-3.145-1.651-14.938-7.837-11.794-6.187c-1.737-.911-2.408-3.057-1.497-4.794l13.466-25.667c.911-1.737 3.058-2.408 4.794-1.497l11.795 6.188 14.939 7.837c.007.003.014.007.021.01l3.125 1.64-1.651 3.145-6.731 12.834-6.733 12.833c-.003.005-.006.008-.009.013zm219.778-151.914c8.857.016 15.784-3.552 20.386-12.326 14.027-26.737-11.92-67.987-57.953-92.135-46.032-24.148-94.719-22.049-108.746 4.688-14.026 26.737 24.987 44.293 71.02 68.441 30.928 16.224 57.156 31.296 75.293 31.332z"/></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1007 KiB

After

Width:  |  Height:  |  Size: 1013 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 947 KiB

After

Width:  |  Height:  |  Size: 960 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1020 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 KiB

After

Width:  |  Height:  |  Size: 408 KiB

+1 -4
View File
@@ -8,7 +8,7 @@
content="
default-src 'self';
font-src data:;
script-src 'self' blob: 'unsafe-eval';
script-src 'self' 'unsafe-eval';
style-src 'self' blob:;
connect-src 'self' ws: https:;
child-src 'self' blob:;
@@ -19,7 +19,6 @@
"
/>
<meta name="application-name" content="KeeWeb" />
<meta name="kw-signature" content="" />
<meta name="kw-config" content="(no-config)" />
<meta
name="viewport"
@@ -72,9 +71,7 @@
/>
<link rel="manifest" href="manifest.json" />
<link rel="stylesheet" href="css/app.css?__inline=true" />
<script src="js/vendor.js?__inline=true"></script>
<script src="js/app.js?__inline=true"></script>
<script src="js/runtime.js?__inline=true"></script>
</head>
<body class="th-d">
<noscript>
-7
View File
@@ -1,7 +0,0 @@
CACHE MANIFEST
# YYYY-MM-DD:v0.0.0
# updmin:v0.0.0
NETWORK:
*
-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M339 314.9L175.4 32h161.2l163.6 282.9H339zm-137.5 23.6L120.9 480h310.5L512 338.5H201.5zM154.1 67.4L0 338.5 80.6 480 237 208.8 154.1 67.4z"/></svg>

Before

Width:  |  Height:  |  Size: 217 B

-1
View File
@@ -1 +0,0 @@
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="256" height="256" version="1.1" viewBox="0 0 256 256"><g transform="translate(296.64282,-100.61434)"><g transform="translate(222.85714,-11.428576)"><g transform="matrix(0.83394139,0,0,0.83394139,-86.101383,10.950635)"><path d="m-419.5 365.94c-18.48-4.62-28.77-19.31-28.81-41.1-0.01-6.97 0.49-10.31 2.23-14.79 4.26-10.99 15.55-19.27 30.41-22.33 7.39-1.52 9.67-3.15 9.67-6.92 0-1.18 0.88-4.71 1.95-7.83 4.88-14.2 13.93-26.03 23.59-30.87 10.11-5.07 15.22-6.21 27.45-6.14 17.38 0.09 26.04 3.86 38.17 16.6l6.67 7 5.97-2.07c28.91-10.01 57.73 7.03 60.06 35.49l0.64 7.79 5.69 2.04c16.26 5.83 23.9 18.06 22.52 36.04-0.91 11.76-6.4 21.15-15.11 25.81l-4.09 2.19-91 0.18c-69.93 0.13-92.16-0.11-96-1.07zM-487.72 353.36" fill="#000"/><path d="m-487.72 353.36c-10.79-2.56-22.22-12.09-27.58-22.99-3.04-6.18-3.2-7.09-3.2-18.03 0-10.4 0.26-12.07 2.68-17.23 5.1-10.89 14.88-18.75 27.15-21.84 2.59-0.65 5.02-1.69 5.41-2.31 0.38-0.62 0.81-4 0.95-7.5 0.85-21.78 15.15-40.97 35.1-47.14 10.78-3.33 24.33-2.51 36.05 2.18 3.72 1.49 3.3 1.81 11.16-8.5 4.65-6.1 14.05-13.68 21.74-17.55 8.3-4.17 16.94-6.09 27.26-6.07 28.86 0.07 53.73 18.12 62.92 45.67 2.94 8.8 2.79 11.27-0.67 11.34-1.51 0.03-5.85 0.86-9.63 1.85l-6.88 1.79-6.28-6.28c-17.7-17.7-46.59-21.53-71.15-9.42-9.81 4.84-17.7 11.78-23.65 20.83-4.25 6.45-9.66 18.48-9.66 21.47 0 2.12-1.72 3.18-9.05 5.58-22.69 7.44-35.94 24.63-35.93 46.62 0 8 2.06 17.8 4.93 23.41 1.08 2.11 1.68 4.13 1.34 4.47-0.88 0.88-29.11 0.58-33.01-0.35z" /></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

-1
View File
@@ -1 +0,0 @@
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M465.69 311.917V200.083H363.175v111.834zm-69.897-18.639a4.66 4.66 0 01-4.659-4.659v-65.237a4.66 4.66 0 014.659-4.66h37.278a4.66 4.66 0 014.66 4.66v65.237a4.66 4.66 0 01-4.66 4.66h-37.278z"/><path d="M400.453 251.34v-23.299h27.959v23.299zm0 32.618v-23.299h27.959v23.299zM46.31 187.656v136.688c0 8.564 6.968 15.532 15.532 15.532h21.746V204.742a4.66 4.66 0 019.319 0v135.134h245.415c8.565 0 15.533-6.968 15.533-15.532V187.656c0-8.564-6.968-15.532-15.533-15.532H61.842c-8.565 0-15.532 6.968-15.532 15.532zm241.936 68.6c.122 9.637-7.529 17.293-17.166 17.416-9.359-.354-17.016-8.006-16.662-17.366-.122-9.636 7.529-17.293 16.889-16.938 9.637-.124 17.293 7.528 16.939 16.888zm-41.921-28.528a4.658 4.658 0 011.679 6.372c-3.865 6.631-5.909 14.266-5.909 22.082v.014c-.143 8.152 1.746 15.601 5.603 22.206a4.66 4.66 0 01-8.048 4.7c-4.73-8.099-7.057-17.196-6.873-26.984v-.021c0-9.381 2.48-18.637 7.176-26.69a4.657 4.657 0 016.372-1.679zm-23.7-17.48a4.659 4.659 0 011.395 6.441c-7.725 11.993-11.78 25.622-11.613 39.406-.164 14.287 3.794 28.031 11.327 39.791a4.66 4.66 0 01-7.849 5.027c-8.511-13.288-12.909-28.806-12.799-44.815-.13-15.503 4.387-30.927 13.099-44.455a4.66 4.66 0 016.44-1.395zm-22.433-16.741a4.656 4.656 0 011.272 6.466c-11.136 16.59-17.074 35.936-17.174 55.945-.244 20.163 5.497 39.619 16.606 56.34a4.66 4.66 0 01-7.762 5.158c-11.901-17.914-18.172-38.71-18.172-60.224 0-.452.003-.901.008-1.354.108-21.815 6.594-42.941 18.756-61.059a4.66 4.66 0 016.466-1.272z"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

+17 -7
View File
@@ -6,9 +6,11 @@ import { ExportApi } from 'comp/app/export-api';
import { SingleInstanceChecker } from 'comp/app/single-instance-checker';
import { Updater } from 'comp/app/updater';
import { UsbListener } from 'comp/app/usb-listener';
import { BrowserExtensionConnector } from 'comp/extension/browser-extension-connector';
import { FeatureTester } from 'comp/browser/feature-tester';
import { FocusDetector } from 'comp/browser/focus-detector';
import { IdleTracker } from 'comp/browser/idle-tracker';
import { ThemeWatcher } from 'comp/browser/theme-watcher';
import { KeyHandler } from 'comp/browser/key-handler';
import { PopupNotifier } from 'comp/browser/popup-notifier';
import { Launcher } from 'comp/launcher';
@@ -44,6 +46,7 @@ ready(() => {
.then(loadRemoteConfig)
.then(ensureCanRun)
.then(initStorage)
.then(initUsbListener)
.then(showApp)
.then(postInit)
.catch((e) => {
@@ -91,6 +94,8 @@ ready(() => {
KdbxwebInit.init();
FocusDetector.init();
AutoType.init();
ThemeWatcher.init();
SettingsManager.init();
window.kw = ExportApi;
return PluginManager.init().then(() => {
StartProfiler.milestone('initializing modules');
@@ -111,13 +116,13 @@ ready(() => {
function loadRemoteConfig() {
return Promise.resolve()
.then(() => {
SettingsManager.setBySettings(appModel.settings);
SettingsManager.setBySettings();
const configParam = getConfigParam();
if (configParam) {
return appModel
.loadConfig(configParam)
.then(() => {
SettingsManager.setBySettings(appModel.settings);
SettingsManager.setBySettings();
})
.catch((e) => {
if (!appModel.settings.cacheConfigSettings) {
@@ -139,6 +144,11 @@ ready(() => {
StartProfiler.milestone('initializing storage');
}
function initUsbListener() {
UsbListener.init();
StartProfiler.milestone('starting usb');
}
function showApp() {
return Promise.resolve().then(() => {
const skipHttpsWarning =
@@ -169,12 +179,12 @@ ready(() => {
}
function postInit() {
Updater.init();
SingleInstanceChecker.init();
AppRightsChecker.init();
IdleTracker.init();
UsbListener.init();
setTimeout(() => {
Updater.init();
SingleInstanceChecker.init();
AppRightsChecker.init();
IdleTracker.init();
BrowserExtensionConnector.init(appModel);
PluginManager.runAutoUpdate();
}, Timeouts.AutoUpdatePluginsAfterStart);
}
@@ -1,14 +0,0 @@
import { Launcher } from 'comp/launcher';
const AutoTypeEmitterFactory = {
create(callback, windowId) {
if (Launcher && Launcher.autoTypeSupported) {
const { AutoTypeEmitter } = require('./emitter/auto-type-emitter-' +
Launcher.platform());
return new AutoTypeEmitter(callback, windowId);
}
return null;
}
};
export { AutoTypeEmitterFactory };
+160
View File
@@ -0,0 +1,160 @@
import { Features } from 'util/features';
import { NativeModules } from 'comp/launcher/native-modules';
import { Logger } from 'util/logger';
import { Launcher } from 'comp/launcher';
import { Timeouts } from 'const/timeouts';
const logger = new Logger('auto-type-emitter');
const KeyMap = {
tab: 'Tab',
enter: 'Return',
space: 'Space',
up: 'UpArrow',
down: 'DownArrow',
left: 'LeftArrow',
right: 'RightArrow',
home: 'Home',
end: 'End',
pgup: 'PageUp',
pgdn: 'PageDown',
ins: 'Insert',
del: 'ForwardDelete',
bs: 'BackwardDelete',
esc: 'Escape',
win: 'Meta',
rwin: 'RightMeta',
f1: 'F1',
f2: 'F2',
f3: 'F3',
f4: 'F4',
f5: 'F5',
f6: 'F6',
f7: 'F7',
f8: 'F8',
f9: 'F9',
f10: 'F10',
f11: 'F11',
f12: 'F12',
f13: 'F13',
f14: 'F14',
f15: 'F15',
f16: 'F16',
add: 'KeypadPlus',
subtract: 'KeypadMinus',
multiply: 'KeypadMultiply',
divide: 'KeypadDivide',
n0: 'D0',
n1: 'D1',
n2: 'D2',
n3: 'D3',
n4: 'D4',
n5: 'D5',
n6: 'D6',
n7: 'D7',
n8: 'D8',
n9: 'D9'
};
const ModMap = {
'^': Features.isMac ? 'Command' : 'Ctrl',
'+': 'Shift',
'%': 'Alt',
'^^': 'Ctrl'
};
class AutoTypeEmitter {
constructor(callback) {
this.callback = callback;
this.mod = {};
}
begin() {
this.withCallback(NativeModules.kbdEnsureModifierNotPressed());
}
setMod(mod, enabled) {
const nativeMod = ModMap[mod];
if (!nativeMod) {
return this.callback(`Bad modifier: ${mod}`);
}
NativeModules.kbdKeyMoveWithModifier(!!enabled, [nativeMod]).catch((e) => {
logger.error('Error moving modifier', mod, enabled ? 'down' : 'up', e);
});
if (enabled) {
this.mod[nativeMod] = true;
} else {
delete this.mod[nativeMod];
}
}
text(str) {
if (!str) {
return this.withCallback(Promise.resolve());
}
const mods = Object.keys(this.mod);
if (mods.length) {
this.withCallback(NativeModules.kbdTextAsKeys(str, mods));
} else {
this.withCallback(NativeModules.kbdText(str));
}
}
key(key) {
const mods = Object.keys(this.mod);
if (typeof key === 'number') {
this.withCallback(NativeModules.kbdKeyPressWithCharacter(0, key, mods));
} else {
if (!KeyMap[key]) {
return this.callback('Bad key: ' + key);
}
const code = KeyMap[key];
this.withCallback(NativeModules.kbdKeyPress(code, mods));
}
}
copyPaste(text) {
setTimeout(() => {
Launcher.setClipboardText(text);
setTimeout(() => {
this.withCallback(NativeModules.kbdShortcut('V'));
}, Timeouts.AutoTypeCopyPaste);
}, Timeouts.AutoTypeCopyPaste);
}
wait(time) {
setTimeout(() => this.withCallback(Promise.resolve()), time);
}
waitComplete() {
this.withCallback(Promise.resolve());
}
setDelay() {
this.callback('Not implemented');
}
withCallback(promise) {
promise
.then(() => {
try {
this.callback();
} catch (err) {
logger.error('Callback error', err);
}
})
.catch((err) => {
const keyPressFailed = err.message === 'Key press failed';
if (keyPressFailed) {
err.keyPressFailed = true;
}
try {
this.callback(err);
} catch (err) {
logger.error('Callback error', err);
}
});
}
}
export { AutoTypeEmitter };
-90
View File
@@ -1,90 +0,0 @@
import { SearchResultCollection } from 'collections/search-result-collection';
import { Ranking } from 'util/data/ranking';
const urlPartsRegex = /^(\w+:\/\/)?(?:(?:www|wwws|secure)\.)?([^\/]+)\/?(.*)/;
const AutoTypeFilter = function (windowInfo, appModel) {
this.title = windowInfo.title;
this.url = windowInfo.url;
this.text = '';
this.ignoreWindowInfo = false;
this.appModel = appModel;
};
AutoTypeFilter.prototype.getEntries = function () {
const filter = {
text: this.text,
autoType: true
};
this.prepareFilter();
let entries = this.appModel.getEntriesByFilter(filter).map((e) => [e, this.getEntryRank(e)]);
if (!this.ignoreWindowInfo) {
entries = entries.filter((e) => e[1]);
}
entries = entries.sort((x, y) =>
x[1] === y[1] ? x[0].title.localeCompare(y[0].title) : y[1] - x[1]
);
entries = entries.map((p) => p[0]);
return new SearchResultCollection(entries, { comparator: 'none' });
};
AutoTypeFilter.prototype.hasWindowInfo = function () {
return this.title || this.url;
};
AutoTypeFilter.prototype.prepareFilter = function () {
this.titleLower = this.title ? this.title.toLowerCase() : null;
this.urlLower = this.url ? this.url.toLowerCase() : null;
this.urlParts = this.url ? urlPartsRegex.exec(this.urlLower) : null;
};
AutoTypeFilter.prototype.getEntryRank = function (entry) {
let rank = 0;
if (this.titleLower && entry.title) {
rank += Ranking.getStringRank(entry.title.toLowerCase(), this.titleLower);
}
if (this.urlParts) {
if (entry.url) {
const entryUrlParts = urlPartsRegex.exec(entry.url.toLowerCase());
if (entryUrlParts) {
const [, scheme, domain, path] = entryUrlParts;
const [, thisScheme, thisDomain, thisPath] = this.urlParts;
if (domain === thisDomain || thisDomain.indexOf('.' + domain) > 0) {
if (domain === thisDomain) {
rank += 20;
} else {
rank += 10;
}
if (path === thisPath) {
rank += 10;
} else if (path && thisPath) {
if (path.lastIndexOf(thisPath, 0) === 0) {
rank += 5;
} else if (thisPath.lastIndexOf(path, 0) === 0) {
rank += 3;
}
}
if (scheme === thisScheme) {
rank += 1;
}
} else {
if (entry.searchText.indexOf(this.urlLower) >= 0) {
// the url is in some field; include it
rank += 5;
} else {
// another domain; don't show this record at all, ignore title match
return 0;
}
}
}
} else {
if (entry.searchText.indexOf(this.urlLower) >= 0) {
// the url is in some field; include it
rank += 5;
}
}
}
return rank;
};
export { AutoTypeFilter };
@@ -1,13 +0,0 @@
import { Launcher } from 'comp/launcher';
const AutoTypeHelperFactory = {
create() {
if (Launcher && Launcher.autoTypeSupported) {
const { AutoTypeHelper } = require('./helper/auto-type-helper-' + Launcher.platform());
return new AutoTypeHelper();
}
return null;
}
};
export { AutoTypeHelperFactory };
+16
View File
@@ -0,0 +1,16 @@
import { NativeModules } from 'comp/launcher/native-modules';
class AutoTypeHelper {
getActiveWindowInfo(callback) {
NativeModules.kbdGetActiveWindow({
getWindowTitle: true,
getBrowserUrl: true
})
.then((win) => {
callback(undefined, win);
})
.catch((err) => callback(err));
}
}
export { AutoTypeHelper };
+5 -5
View File
@@ -1,4 +1,4 @@
import { AutoTypeEmitterFactory } from 'auto-type/auto-type-emitter-factory';
import { AutoTypeEmitter } from 'auto-type/auto-type-emitter';
import { AutoTypeObfuscator } from 'auto-type/auto-type-obfuscator';
import { StringFormat } from 'util/formatting/string-format';
import { Logger } from 'util/logger';
@@ -6,7 +6,7 @@ import { Logger } from 'util/logger';
const emitterLogger = new Logger(
'auto-type-emitter',
undefined,
localStorage.debugAutoType ? Logger.Level.All : Logger.Level.Warn
localStorage.debugAutoType ? Logger.Level.All : Logger.Level.Info
);
const AutoTypeRunner = function (ops) {
@@ -239,7 +239,7 @@ AutoTypeRunner.prototype.tryParseCommand = function (op) {
// {VKEY 10} {VKEY 0x1F}
op.type = 'key';
op.value = parseInt(op.arg);
if (isNaN(op.value) || op.value <= 0) {
if (isNaN(op.value) || op.value < 0) {
throw 'Bad vkey: ' + op.arg;
}
return true;
@@ -432,7 +432,7 @@ AutoTypeRunner.prototype.obfuscateOp = function (op) {
};
AutoTypeRunner.prototype.run = function (callback, windowId) {
this.emitter = AutoTypeEmitterFactory.create(this.emitNext.bind(this), windowId);
this.emitter = new AutoTypeEmitter(this.emitNext.bind(this), windowId);
this.emitterState = {
callback,
stack: [],
@@ -442,7 +442,7 @@ AutoTypeRunner.prototype.run = function (callback, windowId) {
activeMod: {},
finished: null
};
this.emitNext();
this.emitter.begin();
};
AutoTypeRunner.prototype.emitNext = function (err) {
@@ -1,129 +0,0 @@
import { AutoTypeNativeHelper } from 'auto-type/helper/auto-type-native-helper';
import { Launcher } from 'comp/launcher';
// http://eastmanreference.com/complete-list-of-applescript-key-codes/
const KeyMap = {
tab: 48,
enter: 36,
space: 49,
up: 126,
down: 125,
left: 123,
right: 124,
home: 115,
end: 119,
pgup: 116,
pgdn: 121,
ins: 114,
del: 117,
bs: 51,
esc: 53,
win: 55,
rwin: 55,
f1: 122,
f2: 120,
f3: 99,
f4: 118,
f5: 96,
f6: 97,
f7: 98,
f8: 100,
f9: 101,
f10: 109,
f11: 103,
f12: 111,
f13: 105,
f14: 107,
f15: 113,
f16: 106,
add: 69,
subtract: 78,
multiply: 67,
divide: 75,
n0: 82,
n1: 83,
n2: 84,
n3: 85,
n4: 86,
n5: 87,
n6: 88,
n7: 89,
n8: 91,
n9: 92
};
const ModMap = {
'^': '@',
'+': '+',
'%': '%',
'^^': '^'
};
const AutoTypeEmitter = function (callback) {
this.callback = callback;
this.mod = {};
this.pendingScript = [];
};
AutoTypeEmitter.prototype.setMod = function (mod, enabled) {
if (enabled) {
this.mod[ModMap[mod]] = true;
} else {
delete this.mod[ModMap[mod]];
}
};
AutoTypeEmitter.prototype.text = function (text) {
this.pendingScript.push('text ' + this.modString() + ' ' + text);
this.callback();
};
AutoTypeEmitter.prototype.key = function (key) {
if (typeof key !== 'number') {
if (!KeyMap[key]) {
return this.callback('Bad key: ' + key);
}
key = KeyMap[key];
}
this.pendingScript.push('key ' + this.modString() + key);
this.callback();
};
AutoTypeEmitter.prototype.copyPaste = function (text) {
this.pendingScript.push('copypaste ' + text);
this.callback();
};
AutoTypeEmitter.prototype.wait = function (time) {
this.pendingScript.push('wait ' + time);
this.callback();
};
AutoTypeEmitter.prototype.waitComplete = function () {
if (this.pendingScript.length) {
const script = this.pendingScript.join('\n');
this.pendingScript.length = 0;
this.runScript(script);
} else {
this.callback();
}
};
AutoTypeEmitter.prototype.setDelay = function (delay) {
this.delay = delay || 0;
this.callback('Not implemented');
};
AutoTypeEmitter.prototype.modString = function () {
return Object.keys(this.mod).join('');
};
AutoTypeEmitter.prototype.runScript = function (script) {
Launcher.spawn({
cmd: AutoTypeNativeHelper.getHelperPath(),
data: script,
complete: this.callback
});
};
export { AutoTypeEmitter };
@@ -1,164 +0,0 @@
import { Launcher } from 'comp/launcher';
import { Locale } from 'util/locale';
// https://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h
const KeyMap = {
tab: 'Tab',
enter: 'KP_Enter',
space: 'KP_Space',
up: 'Up',
down: 'Down',
left: 'Left',
right: 'Right',
home: 'Home',
end: 'End',
pgup: 'Page_Up',
pgdn: 'Page_Down',
ins: 'Insert',
del: 'Delete',
bs: 'BackSpace',
esc: 'Escape',
win: 'Meta_L',
rwin: 'Meta_R',
f1: 'F1',
f2: 'F2',
f3: 'F3',
f4: 'F4',
f5: 'F5',
f6: 'F6',
f7: 'F7',
f8: 'F8',
f9: 'F9',
f10: 'F10',
f11: 'F11',
f12: 'F12',
f13: 'F13',
f14: 'F14',
f15: 'F15',
f16: 'F16',
add: 'KP_Add',
subtract: 'KP_Subtract',
multiply: 'KP_Multiply',
divide: 'KP_Divide',
n0: 'KP_0',
n1: 'KP_1',
n2: 'KP_2',
n3: 'KP_3',
n4: 'KP_4',
n5: 'KP_5',
n6: 'KP_6',
n7: 'KP_7',
n8: 'KP_8',
n9: 'KP_9'
};
const ModMap = {
'^': 'ctrl',
'+': 'shift',
'%': 'alt',
'^^': 'ctrl'
};
const AutoTypeEmitter = function (callback, windowId) {
this.callback = callback;
if (typeof windowId !== 'undefined' && windowId) {
this.windowParameter = '--window ' + windowId + ' ';
} else {
this.windowParameter = '';
}
this.mod = {};
this.pendingScript = [];
};
AutoTypeEmitter.prototype.setMod = function (mod, enabled) {
if (enabled) {
this.mod[ModMap[mod]] = true;
} else {
delete this.mod[ModMap[mod]];
}
};
AutoTypeEmitter.prototype.text = function (text) {
this.pendingScript.push('keyup ctrl alt shift t');
Object.keys(this.mod).forEach((mod) => {
this.pendingScript.push('keydown ' + this.windowParameter + ModMap[mod]);
});
text.split('').forEach((char) => {
this.pendingScript.push(
'key ' + this.windowParameter + 'U' + char.charCodeAt(0).toString(16)
);
});
Object.keys(this.mod).forEach((mod) => {
this.pendingScript.push('keyup ' + this.windowParameter + ModMap[mod]);
});
this.waitComplete();
};
AutoTypeEmitter.prototype.key = function (key) {
const isSpecialKey = typeof key !== 'number';
if (isSpecialKey) {
if (!KeyMap[key]) {
return this.callback('Bad key: ' + key);
}
key = KeyMap[key].toString();
}
this.pendingScript.push(
'key --clearmodifiers ' + this.windowParameter + this.modString() + key
);
if (isSpecialKey) {
this.waitComplete();
} else {
this.callback();
}
};
AutoTypeEmitter.prototype.copyPaste = function (text) {
this.pendingScript.push('sleep 0.5');
Launcher.setClipboardText(text);
this.pendingScript.push('key --clearmodifiers ' + this.windowParameter + 'shift+Insert');
this.pendingScript.push('sleep 0.5');
this.waitComplete();
};
AutoTypeEmitter.prototype.wait = function (time) {
this.pendingScript.push('sleep ' + time / 1000);
this.callback();
};
AutoTypeEmitter.prototype.waitComplete = function (callback) {
if (this.pendingScript.length) {
const script = this.pendingScript.join(' ');
this.pendingScript.length = 0;
this.runScript(script, callback);
} else {
this.callback();
}
};
AutoTypeEmitter.prototype.modString = function () {
let mod = '';
Object.keys(this.mod).forEach((key) => {
mod += key + '+';
});
return mod;
};
AutoTypeEmitter.prototype.runScript = function (script, callback) {
// xdotool doesn't like it when stdin doesn't end with a linebreak
// see https://github.com/keeweb/keeweb/issues/1409
const data = script + '\n';
Launcher.spawn({
cmd: 'xdotool',
args: ['-'],
data,
complete: (err, stdout, code) => {
if (err && err.code === 'ENOENT') {
err = Locale.autoTypeErrorNotInstalled.replace('{}', 'xdotool');
}
const cb = callback || this.callback;
cb(err, stdout, code);
}
});
};
export { AutoTypeEmitter };
@@ -1,131 +0,0 @@
import { AutoTypeNativeHelper } from 'auto-type/helper/auto-type-native-helper';
import { Launcher } from 'comp/launcher';
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
const KeyMap = {
tab: 0x09,
enter: 0x0d,
space: 0x20,
up: 0x26,
down: 0x28,
left: 0x25,
right: 0x27,
home: 0x24,
end: 0x23,
pgup: 0x21,
pgdn: 0x22,
ins: 0x2d,
del: 0x2e,
bs: 0x08,
esc: 0x1b,
win: 0x5b,
rwin: 0x5c,
f1: 0x70,
f2: 0x71,
f3: 0x72,
f4: 0x73,
f5: 0x74,
f6: 0x75,
f7: 0x76,
f8: 0x77,
f9: 0x78,
f10: 0x79,
f11: 0x7a,
f12: 0x7b,
f13: 0x7c,
f14: 0x7d,
f15: 0x7e,
f16: 0x7f,
add: 0x6b,
subtract: 0x6d,
multiply: 0x6a,
divide: 0x6f,
n0: 0x30,
n1: 0x31,
n2: 0x32,
n3: 0x33,
n4: 0x34,
n5: 0x35,
n6: 0x36,
n7: 0x37,
n8: 0x38,
n9: 0x39
};
const ModMap = {
'^': '^',
'+': '+',
'%': '%',
'^^': '^'
};
const AutoTypeEmitter = function (callback) {
this.callback = callback;
this.mod = {};
this.pendingScript = [];
};
AutoTypeEmitter.prototype.setMod = function (mod, enabled) {
if (enabled) {
this.mod[ModMap[mod]] = true;
} else {
delete this.mod[ModMap[mod]];
}
};
AutoTypeEmitter.prototype.text = function (text) {
if (text) {
this.pendingScript.push('text ' + this.modStr() + ' ' + text);
}
this.callback();
};
AutoTypeEmitter.prototype.key = function (key) {
if (typeof key !== 'number') {
if (!KeyMap[key]) {
return this.callback('Bad key: ' + key);
}
key = KeyMap[key];
}
this.pendingScript.push('key ' + this.modStr() + key);
this.callback();
};
AutoTypeEmitter.prototype.copyPaste = function (text) {
this.pendingScript.push('copypaste ' + text);
this.callback();
};
AutoTypeEmitter.prototype.wait = function (time) {
this.pendingScript.push('wait ' + time);
this.callback();
};
AutoTypeEmitter.prototype.waitComplete = function () {
if (this.pendingScript.length) {
const script = this.pendingScript.join('\n');
this.pendingScript.length = 0;
this.runScript(script);
} else {
this.callback();
}
};
AutoTypeEmitter.prototype.setDelay = function (delay) {
this.delay = delay || 0;
this.callback('Not implemented');
};
AutoTypeEmitter.prototype.modStr = function () {
return Object.keys(this.mod).join('');
};
AutoTypeEmitter.prototype.runScript = function (script) {
Launcher.spawn({
cmd: AutoTypeNativeHelper.getHelperPath(),
data: script,
complete: this.callback
});
};
export { AutoTypeEmitter };
@@ -1,92 +0,0 @@
import { Launcher } from 'comp/launcher';
const ForeMostAppScript =
'tell application "System Events"\n' +
' set frontAppName to name of first process whose frontmost is true\n' +
' set frontAppId to id of first process whose frontmost is true\n' +
'end tell\n' +
'"" & frontAppId & " " & frontAppName';
const ChromeScript =
'tell application "{}" to set appUrl to URL of active tab of front window\n' +
'tell application "{}" to set appTitle to title of active tab of front window\n' +
'return appUrl & "\n" & appTitle';
const SafariScript =
'tell application "{}" to set appUrl to URL of front document\n' +
'tell application "{}" to set appTitle to name of front document\n' +
'return appUrl & "\n" & appTitle';
const OtherAppsScript =
'tell application "System Events"\n' +
' tell process "{}"\n' +
' tell (1st window whose value of attribute "AXMain" is true)\n' +
' set windowTitle to value of attribute "AXTitle"\n' +
' end tell\n' +
' end tell\n' +
'end tell';
const AutoTypeHelper = function () {};
AutoTypeHelper.prototype.getActiveWindowInfo = function (callback) {
AutoTypeHelper.exec(ForeMostAppScript, (err, out) => {
if (err) {
return callback(err);
}
const output = out.trim();
const spaceIx = output.indexOf(' ');
let id = '',
appName = '';
if (spaceIx >= 0) {
id = output.substr(0, spaceIx);
appName = output.substr(spaceIx + 1).trim();
}
// getting urls and titles from Chrome or Safari:
// - will suit in 90% cases
// - does not require assistive access
// - allows to get url
if (['Google Chrome', 'Chromium', 'Google Chrome Canary'].indexOf(appName) >= 0) {
AutoTypeHelper.exec(ChromeScript.replace(/\{}/g, appName), (err, out) => {
if (err) {
return callback(err, { id });
}
const parts = out.split('\n');
return callback(null, {
id,
url: parts[0].trim(),
title: (parts[1] || '').trim()
});
});
} else if (['Safari', 'Webkit'].indexOf(appName) >= 0) {
AutoTypeHelper.exec(SafariScript.replace(/\{}/g, appName), (err, out) => {
if (err) {
return callback(err, { id });
}
const parts = out.split('\n');
return callback(null, {
id,
url: parts[0].trim(),
title: (parts[1] || '').trim()
});
});
} else {
// special cases are not available. this method may ask the user about assistive access
AutoTypeHelper.exec(OtherAppsScript.replace(/\{}/g, appName), (err, out) => {
if (err) {
return callback(err, { id });
}
return callback(null, {
id,
title: out.trim()
});
});
}
});
};
AutoTypeHelper.exec = function (script, callback) {
Launcher.spawn({
cmd: 'osascript',
args: ['-e', script],
complete: callback
});
};
export { AutoTypeHelper };
@@ -1,23 +0,0 @@
import { Launcher } from 'comp/launcher';
const AutoTypeHelper = function () {};
AutoTypeHelper.prototype.getActiveWindowInfo = function (callback) {
Launcher.spawn({
cmd: 'xdotool',
args: ['getactivewindow', 'getwindowname', 'getactivewindow'],
complete(err, out) {
let windowInfo;
if (out) {
const [title, id] = out.trim().split('\n');
windowInfo = {
id,
title
};
}
return callback(err, windowInfo);
}
});
};
export { AutoTypeHelper };
@@ -1,25 +0,0 @@
import { AutoTypeNativeHelper } from 'auto-type/helper/auto-type-native-helper';
import { Launcher } from 'comp/launcher';
const AutoTypeHelper = function () {};
AutoTypeHelper.prototype.getActiveWindowInfo = function (callback) {
Launcher.spawn({
cmd: AutoTypeNativeHelper.getHelperPath(),
args: ['--window-info'],
complete(err, out) {
if (err) {
return callback(err);
}
const [id, title, url] = out.trim().split('\n');
const windowInfo = {
id,
title,
url
};
return callback(null, windowInfo);
}
});
};
export { AutoTypeHelper };
@@ -1,21 +0,0 @@
import { Launcher } from 'comp/launcher';
import { Logger } from 'util/logger';
const logger = new Logger('auto-type');
const AutoTypeNativeHelper = {
getHelperPath() {
if (this._helperPath) {
return this._helperPath;
}
const ext = process.platform === 'win32' ? '.exe' : '';
const helperRelPath = `helper/${process.platform}/KeeWebHelper${ext}`;
const helperPath = Launcher.getAppPath(helperRelPath);
Launcher.ensureRunnable(helperPath);
logger.debug('Using auto-type helper', helperPath);
this._helperPath = helperPath;
return helperPath;
}
};
export { AutoTypeNativeHelper };
+73 -69
View File
@@ -1,25 +1,25 @@
import { Events } from 'framework/events';
import { AutoTypeFilter } from 'auto-type/auto-type-filter';
import { AutoTypeHelperFactory } from 'auto-type/auto-type-helper-factory';
import { AutoTypeHelper } from 'auto-type/auto-type-helper';
import { AutoTypeParser } from 'auto-type/auto-type-parser';
import { SelectEntryFilter } from 'comp/app/select-entry-filter';
import { Launcher } from 'comp/launcher';
import { Features } from 'util/features';
import { Alerts } from 'comp/ui/alerts';
import { Timeouts } from 'const/timeouts';
import { AppSettingsModel } from 'models/app-settings-model';
import { AppModel } from 'models/app-model';
import { Locale } from 'util/locale';
import { Logger } from 'util/logger';
import { AutoTypeSelectView } from 'views/auto-type/auto-type-select-view';
import { Links } from 'const/links';
import { SelectEntryView } from 'views/select/select-entry-view';
const logger = new Logger('auto-type');
const clearTextAutoTypeLog = !!localStorage.debugAutoType;
const AutoType = {
helper: AutoTypeHelperFactory.create(),
enabled: !!(Launcher && Launcher.autoTypeSupported),
supportsEventsWithWindowId: !!(Launcher && Launcher.platform() === 'linux'),
selectEntryView: false,
pendingEvent: null,
running: false,
init() {
@@ -27,10 +27,6 @@ const AutoType = {
return;
}
Events.on('auto-type', (e) => this.handleEvent(e));
Events.on('main-window-blur', (e) => this.mainWindowBlur(e));
Events.on('main-window-focus', (e) => this.mainWindowFocus(e));
Events.on('main-window-will-close', (e) => this.mainWindowWillClose(e));
Events.on('closed-open-view', (e) => this.processPendingEvent(e));
},
handleEvent(e) {
@@ -64,9 +60,16 @@ const AutoType = {
runAndHandleResult(result, windowId) {
this.run(result, windowId, (err) => {
if (err) {
let body = Locale.autoTypeErrorGeneric.replace('{}', err.message || err.toString());
let link;
if (err.keyPressFailed && Features.isMac) {
body = Locale.autoTypeErrorAccessibilityMacOS;
link = Links.AutoTypeMacOS;
}
Alerts.error({
header: Locale.autoTypeError,
body: Locale.autoTypeErrorGeneric.replace('{}', err.toString())
body,
link
});
}
});
@@ -161,15 +164,14 @@ const AutoType = {
getActiveWindowInfo(callback) {
logger.debug('Getting window info');
return this.helper.getActiveWindowInfo((err, windowInfo) => {
const helper = new AutoTypeHelper();
return helper.getActiveWindowInfo((err, windowInfo) => {
if (err) {
logger.error('Error getting window info', err);
} else {
if (!windowInfo.url) {
// try to find a URL in the title
const urlMatcher = new RegExp(
'https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,4}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)'
);
const urlMatcher = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\\+.~#?&\/=]*)/;
const urlMatches = urlMatcher.exec(windowInfo.title);
windowInfo.url = urlMatches && urlMatches.length > 0 ? urlMatches[0] : null;
}
@@ -209,25 +211,42 @@ const AutoType = {
},
selectEntryAndRun() {
this.getActiveWindowInfo((e, windowInfo) => {
const filter = new AutoTypeFilter(windowInfo, AppModel.instance);
this.getActiveWindowInfo(async (e, windowInfo) => {
const filter = new SelectEntryFilter(
windowInfo,
AppModel.instance,
AppModel.instance.files,
{
autoType: true
}
);
const evt = { filter, windowInfo };
if (!AppModel.instance.files.hasOpenFiles()) {
this.pendingEvent = evt;
logger.debug('auto-type event delayed');
this.focusMainWindow();
} else {
this.processEventWithFilter(evt);
try {
await AppModel.instance.unlockAnyFile('autoTypeUnlockMessage');
} catch {
logger.debug('auto-type event canceled');
return;
}
if (this.selectEntryView) {
this.selectEntryView.show();
}
}
logger.debug('processing auto-type event');
this.processEventWithFilter(evt);
});
},
focusMainWindow() {
setTimeout(() => Launcher.showMainWindow(), Timeouts.RedrawInactiveWindow);
if (!Launcher.isAppFocused()) {
setTimeout(() => Launcher.showMainWindow(), Timeouts.RedrawInactiveWindow);
}
},
processEventWithFilter(evt) {
const entries = evt.filter.getEntries();
let entries = evt.filter.getEntries();
if (entries.length === 1 && AppSettingsModel.directAutotype) {
this.hideWindow(() => {
this.runAndHandleResult({ entry: entries[0] }, evt.windowInfo.id);
@@ -235,15 +254,38 @@ const AutoType = {
return;
}
this.focusMainWindow();
evt.filter.ignoreWindowInfo = true;
this.selectEntryView = new AutoTypeSelectView({ filter: evt.filter });
if (entries.length === 0) {
if (evt.filter.useUrl) {
evt.filter.useUrl = false;
if (evt.filter.title && AppSettingsModel.autoTypeTitleFilterEnabled) {
evt.filter.useTitle = true;
}
}
entries = evt.filter.getEntries();
if (entries.length === 0 && evt.filter.useTitle) {
evt.filter.useTitle = false;
}
}
const humanReadableTarget = evt.filter.title || evt.filter.url;
const topMessage = humanReadableTarget
? Locale.autoTypeMsgMatchedByWindow.replace('{}', humanReadableTarget)
: Locale.autoTypeMsgNoWindow;
this.selectEntryView = new SelectEntryView({
isAutoType: true,
itemOptions: true,
filter: evt.filter,
topMessage
});
this.selectEntryView.on('result', (result) => {
logger.debug('Entry selected', result);
this.selectEntryView.off('result');
this.selectEntryView.remove();
this.selectEntryView = null;
this.hideWindow(() => {
if (result) {
if (result?.entry) {
this.activeWindowMatches(evt.windowInfo, (matches, activeWindowInfo) => {
if (matches) {
this.runAndHandleResult(result, evt.windowInfo.id);
@@ -253,55 +295,17 @@ const AutoType = {
});
});
this.selectEntryView.render();
this.selectEntryView.on('show-open-files', () => {
this.selectEntryView.on('show-open-files', async () => {
this.selectEntryView.hide();
Events.emit('open-file');
});
},
mainWindowBlur() {
this.mainWindowBlurTimer = setTimeout(() => {
// macOS emits focus-blur-focus event in a row when triggering auto-type from minimized state
delete this.mainWindowBlurTimer;
this.resetPendingEvent();
if (this.selectEntryView) {
try {
await AppModel.instance.unlockAnyFile('autoTypeUnlockMessage');
} catch {
this.selectEntryView.emit('result', undefined);
return;
}
}, Timeouts.AutoTypeWindowFocusAfterBlur);
},
mainWindowFocus() {
if (this.mainWindowBlurTimer) {
clearTimeout(this.mainWindowBlurTimer);
this.mainWindowBlurTimer = null;
}
},
mainWindowWillClose() {
this.resetPendingEvent();
if (this.selectEntryView) {
this.selectEntryView.emit('result', undefined);
}
},
resetPendingEvent() {
if (this.pendingEvent) {
this.pendingEvent = null;
logger.debug('auto-type event canceled');
}
},
processPendingEvent() {
if (this.selectEntryView) {
this.selectEntryView.show();
}
if (!this.pendingEvent) {
return;
}
logger.debug('processing pending auto-type event');
const evt = this.pendingEvent;
this.pendingEvent = null;
this.processEventWithFilter(evt);
this.selectEntryView.render();
});
}
};
@@ -1,8 +0,0 @@
import { Collection } from 'framework/collection';
import { ExternalEntryModel } from 'models/external/external-entry-model';
class ExternalEntryCollection extends Collection {
static model = ExternalEntryModel;
}
export { ExternalEntryCollection };
@@ -58,6 +58,7 @@ const AppRightsChecker = {
runInstaller() {
Launcher.spawn({
cmd: this.AppPath + '/Contents/Installer/KeeWeb Installer.app/Contents/MacOS/applet',
args: ['--install'],
complete: () => {
this.needRunInstaller((needRun) => {
if (this.alert && !needRun) {
+2 -2
View File
@@ -150,7 +150,7 @@ const ChalRespCalculator = {
header: Locale.yubiKeyNoKeyHeader,
body: Locale.yubiKeyNoKeyBody.replace('{}', serial),
buttons: [Alerts.buttons.cancel],
iconSvg: 'usb-token',
icon: 'usb-token',
cancel: () => {
logger.info('No key alert closed');
@@ -173,7 +173,7 @@ const ChalRespCalculator = {
header: Locale.yubiKeyTouchRequestedHeader,
body: Locale.yubiKeyTouchRequestedBody.replace('{}', serial),
buttons: [Alerts.buttons.cancel],
iconSvg: 'usb-token',
icon: 'usb-token',
cancel: () => {
logger.info('Touch alert closed');
+12
View File
@@ -1,4 +1,5 @@
import { AppSettingsModel } from 'models/app-settings-model';
import { RuntimeDataModel } from 'models/runtime-data-model';
const ExportApi = {
settings: {
@@ -11,6 +12,17 @@ const ExportApi = {
del(key) {
delete AppSettingsModel[key];
}
},
runtimeData: {
get(key) {
return key ? RuntimeDataModel[key] : { ...RuntimeDataModel };
},
set(key, value) {
RuntimeDataModel[key] = value;
},
del(key) {
delete RuntimeDataModel[key];
}
}
};
+12
View File
@@ -13,6 +13,18 @@ const GeneratorPresets = {
};
},
get browserExtensionPreset() {
return {
name: 'BrowserExtension',
length: 20,
upper: true,
lower: true,
special: true,
brackets: true,
ambiguous: true
};
},
get builtIn() {
return [
this.defaultPreset,
@@ -0,0 +1,37 @@
import * as kdbxweb from 'kdbxweb';
import { Logger } from 'util/logger';
const logger = new Logger('online-password-checker');
const exposedPasswords = {};
function checkIfPasswordIsExposedOnline(password) {
if (!password || !password.isProtected || !password.byteLength) {
return false;
}
const saltedValue = password.saltedValue();
const cached = exposedPasswords[saltedValue];
if (cached !== undefined) {
return cached;
}
const passwordBytes = password.getBinary();
return crypto.subtle
.digest({ name: 'SHA-1' }, passwordBytes)
.then((sha1) => {
kdbxweb.ByteUtils.zeroBuffer(passwordBytes);
sha1 = kdbxweb.ByteUtils.bytesToHex(sha1).toUpperCase();
const shaFirst = sha1.substr(0, 5);
return fetch(`https://api.pwnedpasswords.com/range/${shaFirst}`)
.then((response) => response.text())
.then((response) => {
const isPresent = response.includes(sha1.substr(5));
exposedPasswords[saltedValue] = isPresent;
return isPresent;
});
})
.catch((e) => {
logger.error('Error checking password online', e);
});
}
export { checkIfPasswordIsExposedOnline };
@@ -0,0 +1,99 @@
import { SearchResultCollection } from 'collections/search-result-collection';
import { Ranking } from 'util/data/ranking';
const urlPartsRegex = /^(\w+:\/\/)?(?:(?:www|wwws|secure)\.)?([^\/]+)\/?(.*)/;
class SelectEntryFilter {
constructor(windowInfo, appModel, files, filterOptions) {
this.title = windowInfo.title;
this.useTitle = !!windowInfo.title && !windowInfo.url;
this.url = windowInfo.url;
this.useUrl = !!windowInfo.url;
this.subdomains = true;
this.text = '';
this.appModel = appModel;
this.files = files;
this.filterOptions = filterOptions;
}
getEntries() {
const filter = {
text: this.text,
...this.filterOptions
};
let entries = this.appModel
.getEntriesByFilter(filter, this.files)
.map((e) => [e, this._getEntryRank(e)]);
if (this.useUrl || this.useTitle) {
entries = entries.filter((e) => e[1]);
}
entries = entries.sort((x, y) =>
x[1] === y[1] ? x[0].title.localeCompare(y[0].title) : y[1] - x[1]
);
entries = entries.map((p) => p[0]);
return new SearchResultCollection(entries, { comparator: 'none' });
}
_getEntryRank(entry) {
let titleRank = 0;
let urlRank = 0;
if (this.useTitle && this.title && entry.title) {
titleRank = Ranking.getStringRank(entry.title.toLowerCase(), this.title.toLowerCase());
if (!titleRank) {
return 0;
}
}
if (this.useUrl && this.url) {
const searchUrlLower = this.url.toLowerCase();
const searchUrlParts = urlPartsRegex.exec(searchUrlLower);
for (const url of entry.getAllUrls()) {
const entryUrlParts = urlPartsRegex.exec(url.toLowerCase());
if (entryUrlParts) {
const [, scheme, domain, path] = entryUrlParts;
const [, searchScheme, searchDomain, searchPath] = searchUrlParts;
if (
domain === searchDomain ||
(this.subdomains && searchDomain.indexOf('.' + domain) > 0)
) {
if (domain === searchDomain) {
urlRank += 20;
} else {
urlRank += 10;
}
if (path === searchPath) {
urlRank += 10;
} else if (path && searchPath) {
if (path.lastIndexOf(searchPath, 0) === 0) {
urlRank += 5;
} else if (searchPath.lastIndexOf(path, 0) === 0) {
urlRank += 3;
}
}
if (scheme === searchScheme) {
urlRank += 1;
}
}
}
}
if (entry.searchText.includes(searchUrlLower)) {
// the url is in some field; include it
urlRank += 5;
}
}
if (this.useTitle && !titleRank) {
return 0;
}
if (this.useUrl && !urlRank) {
return 0;
}
return titleRank + urlRank;
}
}
export { SelectEntryFilter };
+110 -82
View File
@@ -1,3 +1,4 @@
import * as kdbxweb from 'kdbxweb';
import { Events } from 'framework/events';
import { RuntimeInfo } from 'const/runtime-info';
import { Transport } from 'comp/browser/transport';
@@ -15,10 +16,9 @@ const Updater = {
UpdateInterval: 1000 * 60 * 60 * 24,
MinUpdateTimeout: 500,
MinUpdateSize: 10000,
UpdateCheckFiles: ['app.asar'],
nextCheckTimeout: null,
updateCheckDate: new Date(0),
enabled: Launcher && Launcher.updaterEnabled(),
enabled: Launcher?.updaterEnabled(),
getAutoUpdateType() {
if (!this.enabled) {
@@ -34,7 +34,7 @@ const Updater = {
updateInProgress() {
return (
UpdateModel.status === 'checking' ||
['downloading', 'extracting'].indexOf(UpdateModel.updateStatus) >= 0
['downloading', 'extracting', 'updating'].indexOf(UpdateModel.updateStatus) >= 0
);
},
@@ -97,13 +97,12 @@ const Updater = {
}
logger.info('Checking for update...');
Transport.httpGet({
url: Links.Manifest,
utf8: true,
success: (data) => {
url: Links.UpdateJson,
json: true,
success: (updateJson) => {
const dt = new Date();
const match = data.match(/#\s*(\d+\-\d+\-\d+):v([\d+\.\w]+)/);
logger.info('Update check: ' + (match ? match[0] : 'unknown'));
if (!match) {
logger.info('Update check: ' + (updateJson.version || 'unknown'));
if (!updateJson.version) {
const errMsg = 'No version info found';
UpdateModel.set({
status: 'error',
@@ -114,16 +113,15 @@ const Updater = {
this.scheduleNextCheck();
return;
}
const updateMinVersionMatch = data.match(/#\s*updmin:v([\d+\.\w]+)/);
const prevLastVersion = UpdateModel.lastVersion;
UpdateModel.set({
status: 'ok',
lastCheckDate: dt,
lastSuccessCheckDate: dt,
lastVersionReleaseDate: new Date(match[1]),
lastVersion: match[2],
lastVersionReleaseDate: new Date(updateJson.date),
lastVersion: updateJson.version,
lastCheckError: null,
lastCheckUpdMin: updateMinVersionMatch ? updateMinVersionMatch[1] : null
lastCheckUpdMin: updateJson.minVersion || null
});
UpdateModel.save();
this.scheduleNextCheck();
@@ -161,7 +159,7 @@ const Updater = {
canAutoUpdate() {
const minLauncherVersion = UpdateModel.lastCheckUpdMin;
if (minLauncherVersion) {
const cmp = SemVer.compareVersions(Launcher.version, minLauncherVersion);
const cmp = SemVer.compareVersions(RuntimeInfo.version, minLauncherVersion);
if (cmp < 0) {
UpdateModel.set({ updateStatus: 'ready', updateManual: true });
return false;
@@ -182,28 +180,55 @@ const Updater = {
}
UpdateModel.set({ updateStatus: 'downloading', updateError: null });
logger.info('Downloading update', ver);
const updateAssetName = this.getUpdateAssetName(ver);
if (!updateAssetName) {
logger.error('Empty updater asset name for', Launcher.platform(), Launcher.arch());
return;
}
const updateUrlBasePath = Links.UpdateBasePath.replace('{ver}', ver);
const updateAssetUrl = updateUrlBasePath + updateAssetName;
Transport.httpGet({
url: Links.UpdateDesktop.replace('{ver}', ver),
file: 'KeeWeb-' + ver + '.zip',
cache: !startedByUser,
success: (filePath) => {
UpdateModel.set({ updateStatus: 'extracting' });
logger.info('Extracting update file', this.UpdateCheckFiles, filePath);
this.extractAppUpdate(filePath, (err) => {
if (err) {
logger.error('Error extracting update', err);
url: updateAssetUrl,
file: updateAssetName,
cleanupOldFiles: true,
cache: true,
success: (assetFilePath) => {
logger.info('Downloading update signatures');
Transport.httpGet({
url: updateUrlBasePath + 'Verify.sign.sha256',
text: true,
file: updateAssetName + '.sign',
cleanupOldFiles: true,
cache: true,
success: (assetFileSignaturePath) => {
this.verifySignature(assetFilePath, updateAssetName, (err, valid) => {
if (err || !valid) {
UpdateModel.set({
updateStatus: 'error',
updateError: err
? 'Error verifying update signature'
: 'Invalid update signature'
});
Launcher.deleteFile(assetFilePath);
Launcher.deleteFile(assetFileSignaturePath);
return;
}
logger.info('Update is ready', assetFilePath);
UpdateModel.set({ updateStatus: 'ready', updateError: null });
if (!startedByUser) {
Events.emit('update-app');
}
if (typeof successCallback === 'function') {
successCallback();
}
});
},
error(e) {
logger.error('Error downloading update signatures', e);
UpdateModel.set({
updateStatus: 'error',
updateError: 'Error extracting update'
updateError: 'Error downloading update signatures'
});
} else {
UpdateModel.set({ updateStatus: 'ready', updateError: null });
if (!startedByUser) {
Events.emit('update-app');
}
if (typeof successCallback === 'function') {
successCallback();
}
}
});
},
@@ -217,61 +242,64 @@ const Updater = {
});
},
extractAppUpdate(updateFile, cb) {
const expectedFiles = this.UpdateCheckFiles;
const appPath = Launcher.getUserDataPath();
const StreamZip = Launcher.req('node-stream-zip');
StreamZip.setFs(Launcher.req('original-fs'));
const zip = new StreamZip({ file: updateFile, storeEntries: true });
zip.on('error', cb);
zip.on('ready', () => {
const containsAll = expectedFiles.every((expFile) => {
const entry = zip.entry(expFile);
return entry && entry.isFile;
verifySignature(assetFilePath, assetName, callback) {
logger.info('Verifying update signature', assetName);
const fs = Launcher.req('fs');
const signaturesTxt = fs.readFileSync(assetFilePath + '.sign', 'utf8');
const assetSignatureLine = signaturesTxt
.split('\n')
.find((line) => line.endsWith(assetName));
if (!assetSignatureLine) {
logger.error('Signature not found for asset', assetName);
callback('Asset signature not found');
return;
}
const signature = kdbxweb.ByteUtils.hexToBytes(assetSignatureLine.split(' ')[0]);
const fileBytes = fs.readFileSync(assetFilePath);
SignatureVerifier.verify(fileBytes, signature)
.catch((e) => {
logger.error('Error verifying signature', e);
callback('Error verifying signature');
})
.then((valid) => {
logger.info(`Update asset signature is ${valid ? 'valid' : 'invalid'}`);
callback(undefined, valid);
});
if (!containsAll) {
return cb('Bad archive');
}
this.validateArchiveSignature(updateFile, zip)
.then(() => {
zip.extract(null, appPath, (err) => {
zip.close();
if (err) {
return cb(err);
}
Launcher.deleteFile(updateFile);
cb();
});
})
.catch((e) => {
return cb('Invalid archive: ' + e);
});
});
},
validateArchiveSignature(archivePath, zip) {
if (!zip.comment) {
return Promise.reject('No comment in ZIP');
getUpdateAssetName(ver) {
const platform = Launcher.platform();
const arch = Launcher.arch();
switch (platform) {
case 'win32':
switch (arch) {
case 'x64':
return `KeeWeb-${ver}.win.x64.exe`;
case 'ia32':
return `KeeWeb-${ver}.win.ia32.exe`;
case 'arm64':
return `KeeWeb-${ver}.win.arm64.exe`;
}
break;
case 'darwin':
switch (arch) {
case 'x64':
return `KeeWeb-${ver}.mac.x64.dmg`;
case 'arm64':
return `KeeWeb-${ver}.mac.arm64.dmg`;
}
break;
}
if (zip.comment.length !== 512) {
return Promise.reject('Bad comment length in ZIP: ' + zip.comment.length);
}
try {
const zipFileData = Launcher.req('fs').readFileSync(archivePath);
const dataToVerify = zipFileData.slice(0, zip.centralDirectory.headerOffset + 22);
const signature = window.Buffer.from(zip.comment, 'hex');
return SignatureVerifier.verify(dataToVerify, signature)
.catch(() => {
throw new Error('Error verifying signature');
})
.then((isValid) => {
if (!isValid) {
throw new Error('Invalid signature');
}
});
} catch (err) {
return Promise.reject(err.toString());
return undefined;
},
installAndRestart() {
if (!Launcher) {
return;
}
const updateAssetName = this.getUpdateAssetName(UpdateModel.lastVersion);
const updateFilePath = Transport.cacheFilePath(updateAssetName);
Launcher.requestRestartAndUpdate(updateFilePath);
}
};
+2 -2
View File
@@ -203,7 +203,7 @@ const YubiKey = {
this.process = Launcher.spawn({
cmd: this.cmd(),
args: ['-d', serial, 'oath', 'code'],
args: ['-d', serial, 'oath', 'accounts', 'code'],
noStdOutLogging: true,
throwOnStdErr: true,
complete: (err, stdout) => {
@@ -241,7 +241,7 @@ const YubiKey = {
getOtp(serial, entry, callback) {
return Launcher.spawn({
cmd: this.cmd(),
args: ['-d', serial, 'oath', 'code', '--single', entry],
args: ['-d', serial, 'oath', 'accounts', 'code', '--single', entry],
noStdOutLogging: true,
complete: (err, stdout) => {
if (err) {
+8 -1
View File
@@ -4,7 +4,8 @@ const FeatureTester = {
test() {
return Promise.resolve()
.then(() => this.checkWebAssembly())
.then(() => this.checkLocalStorage());
.then(() => this.checkLocalStorage())
.then(() => this.checkWebCrypto());
},
checkWebAssembly() {
@@ -28,6 +29,12 @@ const FeatureTester = {
} catch (e) {
throw 'LocalStorage is not supported';
}
},
checkWebCrypto() {
if (!global.crypto.subtle) {
throw 'WebCrypto is not supported';
}
}
};
+1
View File
@@ -10,6 +10,7 @@ const IdleTracker = {
const idleMinutes = (Date.now() - this.actionTime) / 1000 / 60;
const maxIdleMinutes = AppSettingsModel.idleMinutes;
if (maxIdleMinutes && idleMinutes > maxIdleMinutes) {
Events.emit('before-user-idle');
Events.emit('user-idle');
}
},
+18 -2
View File
@@ -45,12 +45,28 @@ const PopupNotifier = {
Timeouts.CheckWindowClosed
);
} else {
const loc = PopupNotifier.tryGetLocationSearch(win);
if (loc) {
try {
win.close();
} catch {}
PopupNotifier.triggerClosed(win, loc);
return;
}
PopupNotifier.deferCheckClosed(win);
}
},
triggerClosed(win) {
Events.emit('popup-closed', win);
tryGetLocationSearch(win) {
try {
if (win.location.host === location.host) {
return win.location.search;
}
} catch {}
},
triggerClosed(window, locationSearch) {
Events.emit('popup-closed', { window, locationSearch });
}
};
+4 -3
View File
@@ -1,4 +1,4 @@
import kdbxweb from 'kdbxweb';
import * as kdbxweb from 'kdbxweb';
const SecureInput = function () {
this.el = null;
@@ -79,11 +79,12 @@ Object.defineProperty(SecureInput.prototype, 'value', {
const len = pseudoValue.length;
let byteLength = 0;
const valueBytes = new Uint8Array(len * 4);
const saltBytes = kdbxweb.Random.getBytes(len * 4);
const saltBytes = kdbxweb.CryptoEngine.random(len * 4);
let ch;
let bytes;
for (let i = 0; i < len; i++) {
ch = String.fromCharCode(pseudoValue.charCodeAt(i) ^ salt[i]);
const pseudoCharCode = pseudoValue.charCodeAt(i);
ch = String.fromCharCode(salt[i] ^ pseudoCharCode);
bytes = kdbxweb.ByteUtils.stringToBytes(ch);
for (let j = 0; j < bytes.length; j++) {
valueBytes[byteLength] = bytes[j] ^ saltBytes[byteLength];
+19
View File
@@ -0,0 +1,19 @@
import { Events } from 'framework/events';
const ThemeWatcher = {
dark: false,
init() {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
if (mediaQuery && mediaQuery.addEventListener) {
mediaQuery.addEventListener('change', (e) => {
const dark = e.matches;
this.dark = dark;
Events.emit('dark-mode-changed', { dark });
});
}
this.dark = !!mediaQuery.matches;
}
};
export { ThemeWatcher };
+27 -2
View File
@@ -1,15 +1,33 @@
import { Launcher } from 'comp/launcher';
import { Logger } from 'util/logger';
import { noop } from 'util/fn';
import { StringFormat } from 'util/formatting/string-format';
const logger = new Logger('transport');
const Transport = {
cacheFilePath(fileName) {
return Launcher.getTempPath(fileName);
},
httpGet(config) {
let tmpFile;
const fs = Launcher.req('fs');
if (config.file) {
tmpFile = Launcher.getTempPath(config.file);
const baseTempPath = Launcher.getTempPath();
if (config.cleanupOldFiles) {
const allFiles = fs.readdirSync(baseTempPath);
for (const file of allFiles) {
if (
file !== config.file &&
StringFormat.replaceVersion(file, '0') ===
StringFormat.replaceVersion(config.file, '0')
) {
fs.unlinkSync(Launcher.joinPath(baseTempPath, file));
}
}
}
tmpFile = Launcher.joinPath(baseTempPath, config.file);
if (fs.existsSync(tmpFile)) {
try {
if (config.cache && fs.statSync(tmpFile).size > 0) {
@@ -62,9 +80,16 @@ const Transport = {
});
res.on('end', () => {
data = window.Buffer.concat(data);
if (config.utf8) {
if (config.text || config.json) {
data = data.toString('utf8');
}
if (config.json) {
try {
data = JSON.parse(data);
} catch (e) {
config.error('Error parsing JSON: ' + e.message);
}
}
config.success(data);
});
}
@@ -0,0 +1,248 @@
import { Launcher } from 'comp/launcher';
import { Logger } from 'util/logger';
import { ProtocolImpl } from './protocol-impl';
import { RuntimeInfo } from 'const/runtime-info';
import { AppSettingsModel } from 'models/app-settings-model';
import { Features } from 'util/features';
const WebConnectionInfo = {
connectionId: 1,
extensionName: 'KeeWeb Connect',
supportsNotifications: true
};
const SupportedExtensions = [
{ alias: 'KWC', name: 'KeeWeb Connect' },
{ alias: 'KPXC', name: 'KeePassXC-Browser' }
];
const SupportedBrowsers = ['Chrome', 'Firefox', 'Edge', 'Other'];
if (Features.isMac) {
SupportedBrowsers.unshift('Safari');
}
const logger = new Logger('browser-extension-connector');
if (!localStorage.debugBrowserExtension) {
logger.level = Logger.Level.Info;
}
const connections = new Map();
const pendingBrowserMessages = [];
let processingBrowserMessage = false;
const BrowserExtensionConnector = {
started: false,
logger,
init(appModel) {
const sendEvent = this.sendEvent.bind(this);
ProtocolImpl.init({ appModel, logger, sendEvent });
this.browserWindowMessage = this.browserWindowMessage.bind(this);
if (Launcher) {
const { ipcRenderer } = Launcher.electron();
ipcRenderer.on('browserExtensionSocketConnected', (e, socketId, connectionInfo) =>
this.socketConnected(socketId, connectionInfo)
);
ipcRenderer.on('browserExtensionSocketClosed', (e, socketId) =>
this.socketClosed(socketId)
);
ipcRenderer.on('browserExtensionSocketRequest', (e, socketId, request) =>
this.socketRequest(socketId, request)
);
AppSettingsModel.on('change', () => this.appSettingsChanged());
}
if (this.isEnabled()) {
this.start();
}
},
start() {
if (Launcher) {
this.startDesktopAppListener();
} else {
this.startWebMessageListener();
}
this.started = true;
},
stop() {
if (Launcher) {
this.stopDesktopAppListener();
} else {
this.stopWebMessageListener();
}
ProtocolImpl.cleanup();
connections.clear();
this.started = false;
},
appSettingsChanged() {
if (this.isEnabled()) {
if (!this.started) {
this.start();
}
} else if (this.started) {
this.stop();
}
},
isEnabled() {
if (!Launcher) {
return true;
}
for (const ext of SupportedExtensions) {
for (const browser of SupportedBrowsers) {
if (AppSettingsModel[`extensionEnabled${ext.alias}${browser}`]) {
return true;
}
}
}
return false;
},
startWebMessageListener() {
window.addEventListener('message', this.browserWindowMessage);
logger.info('Started');
},
stopWebMessageListener() {
window.removeEventListener('message', this.browserWindowMessage);
},
enable(browser, extension, enabled) {
const { ipcRenderer } = Launcher.electron();
ipcRenderer.invoke('browserExtensionConnectorEnable', browser, extension, enabled);
},
async startDesktopAppListener() {
const { ipcRenderer } = Launcher.electron();
ipcRenderer.invoke('browserExtensionConnectorStart', {
appleTeamId: RuntimeInfo.appleTeamId
});
},
stopDesktopAppListener() {
const { ipcRenderer } = Launcher.electron();
ipcRenderer.invoke('browserExtensionConnectorStop');
},
browserWindowMessage(e) {
if (e.origin !== location.origin) {
return;
}
if (e.source !== window) {
return;
}
if (e?.data?.kwConnect !== 'request') {
return;
}
logger.debug('Extension -> KeeWeb', e.data);
pendingBrowserMessages.push(e.data);
this.processBrowserMessages();
},
async processBrowserMessages() {
if (!pendingBrowserMessages.length || processingBrowserMessage) {
return;
}
if (!connections.has(WebConnectionInfo.connectionId)) {
connections.set(WebConnectionInfo.connectionId, WebConnectionInfo);
}
processingBrowserMessage = true;
const request = pendingBrowserMessages.shift();
const response = await ProtocolImpl.handleRequest(request, WebConnectionInfo);
processingBrowserMessage = false;
if (response) {
this.sendWebResponse(response);
}
this.processBrowserMessages();
},
sendWebResponse(response) {
logger.debug('KeeWeb -> Extension', response);
response.kwConnect = 'response';
postMessage(response, window.location.origin);
},
sendSocketEvent(data) {
const { ipcRenderer } = Launcher.electron();
ipcRenderer.invoke('browserExtensionConnectorSocketEvent', data);
},
sendSocketResult(socketId, data) {
const { ipcRenderer } = Launcher.electron();
ipcRenderer.invoke('browserExtensionConnectorSocketResult', socketId, data);
},
sendEvent(data) {
if (!this.isEnabled() || !connections.size) {
return;
}
if (Launcher) {
this.sendSocketEvent(data);
} else {
this.sendWebResponse(data);
}
},
socketConnected(socketId, connectionInfo) {
connections.set(socketId, connectionInfo);
},
socketClosed(socketId) {
connections.delete(socketId);
ProtocolImpl.deleteConnection(socketId);
},
async socketRequest(socketId, request) {
let result;
const connectionInfo = connections.get(socketId);
if (connectionInfo) {
result = await ProtocolImpl.handleRequest(request, connectionInfo);
} else {
const message = `Connection not found: ${socketId}`;
result = ProtocolImpl.errorToResponse({ message }, request);
}
this.sendSocketResult(socketId, result);
},
get sessions() {
return ProtocolImpl.sessions;
},
terminateConnection(connectionId) {
connectionId = +connectionId;
if (Launcher) {
const { ipcRenderer } = Launcher.electron();
ipcRenderer.invoke('browserExtensionConnectorCloseSocket', connectionId);
} else {
ProtocolImpl.deleteConnection(connectionId);
}
},
getClientPermissions(clientId) {
return ProtocolImpl.getClientPermissions(clientId);
},
setClientPermissions(clientId, permissions) {
ProtocolImpl.setClientPermissions(clientId, permissions);
}
};
export { BrowserExtensionConnector, SupportedExtensions, SupportedBrowsers };
+940
View File
@@ -0,0 +1,940 @@
import * as kdbxweb from 'kdbxweb';
import { Events } from 'framework/events';
import { Launcher } from 'comp/launcher';
import { box as tweetnaclBox } from 'tweetnacl';
import { PasswordGenerator } from 'util/generators/password-generator';
import { GeneratorPresets } from 'comp/app/generator-presets';
import { Alerts } from 'comp/ui/alerts';
import { Locale } from 'util/locale';
import { RuntimeInfo } from 'const/runtime-info';
import { KnownAppVersions } from 'const/known-app-versions';
import { ExtensionConnectView } from 'views/extension/extension-connect-view';
import { ExtensionCreateGroupView } from 'views/extension/extension-create-group-view';
import { ExtensionSaveEntryView } from 'views/extension/extension-save-entry-view';
import { RuntimeDataModel } from 'models/runtime-data-model';
import { AppSettingsModel } from 'models/app-settings-model';
import { Timeouts } from 'const/timeouts';
import { SelectEntryView } from 'views/select/select-entry-view';
import { SelectEntryFieldView } from 'views/select/select-entry-field-view';
import { SelectEntryFilter } from 'comp/app/select-entry-filter';
const KeeWebAssociationId = 'KeeWeb';
const KeeWebHash = '398d9c782ec76ae9e9877c2321cbda2b31fc6d18ccf0fed5ca4bd746bab4d64a'; // sha256('KeeWeb')
const ExtensionGroupIconId = 1;
const DefaultExtensionGroupName = 'Browser';
const ExtensionGroupNames = new Set(['KeePassXC-Browser Passwords', DefaultExtensionGroupName]);
const Errors = {
noOpenFiles: {
message: Locale.extensionErrorNoOpenFiles,
code: '1'
},
userRejected: {
message: Locale.extensionErrorUserRejected,
code: '6'
},
noMatches: {
message: Locale.extensionErrorNoMatches,
code: '15'
}
};
const connectedClients = new Map();
let logger;
let appModel;
let sendEvent;
function setupListeners() {
Events.on('file-opened', () => {
sendEvent({ action: 'database-unlocked' });
});
Events.on('one-file-closed', () => {
if (!appModel.files.hasOpenFiles()) {
sendEvent({ action: 'database-locked' });
}
});
Events.on('all-files-closed', () => {
sendEvent({ action: 'database-locked' });
});
}
function incrementNonce(nonce) {
// from libsodium/utils.c, like it is in KeePassXC
let i = 0;
let c = 1;
for (; i < nonce.length; ++i) {
c += nonce[i];
nonce[i] = c;
c >>= 8;
}
}
function getClient(request) {
if (!request.clientID) {
throw new Error('Empty clientID');
}
const client = connectedClients.get(request.clientID);
if (!client) {
throw new Error(`Client not connected: ${request.clientID}`);
}
return client;
}
function decryptRequest(request) {
const client = getClient(request);
if (!request.nonce) {
throw new Error('Empty nonce');
}
if (!request.message) {
throw new Error('Empty message');
}
const nonce = kdbxweb.ByteUtils.base64ToBytes(request.nonce);
const message = kdbxweb.ByteUtils.base64ToBytes(request.message);
const data = tweetnaclBox.open(message, nonce, client.publicKey, client.keys.secretKey);
if (!data) {
throw new Error('Failed to decrypt data');
}
const json = new TextDecoder().decode(data);
const payload = JSON.parse(json);
logger.debug('Extension -> KeeWeb -> (decrypted)', payload);
if (!payload) {
throw new Error('Empty request payload');
}
if (payload.action !== request.action) {
throw new Error(`Bad action in decrypted payload`);
}
return payload;
}
function encryptResponse(request, payload) {
logger.debug('KeeWeb -> Extension (decrypted)', payload);
const nonceBytes = kdbxweb.ByteUtils.base64ToBytes(request.nonce);
incrementNonce(nonceBytes);
const nonce = kdbxweb.ByteUtils.bytesToBase64(nonceBytes);
const client = getClient(request);
payload.nonce = nonce;
const json = JSON.stringify(payload);
const data = new TextEncoder().encode(json);
const encrypted = tweetnaclBox(data, nonceBytes, client.publicKey, client.keys.secretKey);
const message = kdbxweb.ByteUtils.bytesToBase64(encrypted);
return {
action: request.action,
message,
nonce
};
}
function makeError(def) {
const e = new Error(def.message);
e.code = def.code;
return e;
}
function ensureAtLeastOneFileIsOpen() {
if (!appModel.files.hasOpenFiles()) {
throw makeError(Errors.noOpenFiles);
}
}
async function checkContentRequestPermissions(request) {
if (!appModel.files.hasOpenFiles()) {
if (AppSettingsModel.extensionFocusIfLocked) {
try {
focusKeeWeb();
await appModel.unlockAnyFile(
'extensionUnlockMessage',
Timeouts.KeeWebConnectRequest
);
} catch {
throw makeError(Errors.noOpenFiles);
}
} else {
throw makeError(Errors.noOpenFiles);
}
}
const client = getClient(request);
if (client.permissions) {
return;
}
if (Alerts.alertDisplayed) {
throw new Error(Locale.extensionErrorAlertDisplayed);
}
focusKeeWeb();
const config = RuntimeDataModel.extensionConnectConfig;
const files = appModel.files.map((f) => ({
id: f.id,
name: f.name,
checked: !config || config.allFiles || config.files.includes(f.id)
}));
if (!files.some((f) => f.checked)) {
for (const f of files) {
f.checked = true;
}
}
const extensionConnectView = new ExtensionConnectView({
extensionName: getHumanReadableExtensionName(client),
identityVerified: !Launcher,
files,
allFiles: config?.allFiles ?? true,
askGet: config?.askGet || 'multiple'
});
try {
await alertWithTimeout({
header: Locale.extensionConnectHeader,
icon: 'exchange-alt',
buttons: [Alerts.buttons.allow, Alerts.buttons.deny],
view: extensionConnectView,
wide: true,
opaque: true
});
} catch (e) {
client.permissionsDenied = true;
Events.emit('browser-extension-sessions-changed');
throw e;
}
RuntimeDataModel.extensionConnectConfig = extensionConnectView.config;
client.permissions = extensionConnectView.config;
Events.emit('browser-extension-sessions-changed');
}
function alertWithTimeout(config) {
return new Promise((resolve, reject) => {
let inactivityTimer = 0;
const alert = Alerts.alert({
...config,
enter: 'yes',
esc: '',
success: (res) => {
clearTimeout(inactivityTimer);
resolve(res);
},
cancel: () => {
clearTimeout(inactivityTimer);
reject(makeError(Errors.userRejected));
}
});
inactivityTimer = setTimeout(() => {
alert.closeWithResult('');
}, Timeouts.KeeWebConnectRequest);
});
}
function getAvailableFiles(request) {
const client = getClient(request);
if (!client.permissions) {
return;
}
const files = appModel.files.filter(
(file) =>
file.active &&
(client.permissions.allFiles || client.permissions.files.includes(file.id))
);
if (!files.length) {
throw makeError(Errors.noOpenFiles);
}
return files;
}
function getVersion(request) {
return isKeePassXcBrowser(request) ? KnownAppVersions.KeePassXC : RuntimeInfo.version;
}
function isKeeWebConnect(request) {
return getClient(request).connection.extensionName === 'KeeWeb Connect';
}
function isKeePassXcBrowser(request) {
return getClient(request).connection.extensionName === 'KeePassXC-Browser';
}
function getHumanReadableExtensionName(client) {
return client.connection.appName
? `${client.connection.extensionName} (${client.connection.appName})`
: client.connection.extensionName;
}
function focusKeeWeb() {
logger.debug('Focus KeeWeb');
if (Launcher) {
if (!Launcher.isAppFocused()) {
Launcher.showMainWindow();
}
} else {
sendEvent({ action: 'attention-required' });
}
}
async function findEntry(request, returnIfOneMatch, filterOptions) {
const payload = decryptRequest(request);
await checkContentRequestPermissions(request);
if (!payload.url) {
throw new Error('Empty url');
}
const files = getAvailableFiles(request);
const client = getClient(request);
const filter = new SelectEntryFilter(
{ url: payload.url, title: payload.title },
appModel,
files,
filterOptions
);
filter.subdomains = false;
let entries = filter.getEntries();
filter.subdomains = true;
let entry;
if (entries.length) {
if (entries.length === 1 && returnIfOneMatch && client.permissions.askGet === 'multiple') {
entry = entries[0];
}
} else {
entries = filter.getEntries();
if (!entries.length) {
if (AppSettingsModel.extensionFocusIfEmpty) {
filter.useUrl = false;
if (filter.title && AppSettingsModel.autoTypeTitleFilterEnabled) {
filter.useTitle = true;
entries = filter.getEntries();
if (!entries.length) {
filter.useTitle = false;
}
}
} else {
throw makeError(Errors.noMatches);
}
}
}
if (!entry) {
const extName = getHumanReadableExtensionName(client);
const topMessage = Locale.extensionSelectPasswordFor.replace('{}', extName);
const selectEntryView = new SelectEntryView({ filter, topMessage });
focusKeeWeb();
const inactivityTimer = setTimeout(() => {
selectEntryView.emit('result', undefined);
}, Timeouts.KeeWebConnectRequest);
const result = await selectEntryView.showAndGetResult();
clearTimeout(inactivityTimer);
entry = result?.entry;
if (!entry) {
throw makeError(Errors.userRejected);
}
}
client.stats.passwordsRead++;
return entry;
}
const ProtocolHandlers = {
'ping'({ data }) {
return { data };
},
'change-public-keys'(request, connection) {
let { publicKey, version, clientID: clientId } = request;
if (connectedClients.has(clientId)) {
throw new Error('Changing keys is not allowed');
}
if (!Launcher) {
// on web there can be only one connected client
connectedClients.clear();
}
const keys = tweetnaclBox.keyPair();
publicKey = kdbxweb.ByteUtils.base64ToBytes(publicKey);
const stats = {
connectedDate: new Date(),
passwordsRead: 0,
passwordsWritten: 0
};
connectedClients.set(clientId, { connection, publicKey, version, keys, stats });
Events.emit('browser-extension-sessions-changed');
logger.info('New client key created', clientId, version);
const nonceBytes = kdbxweb.ByteUtils.base64ToBytes(request.nonce);
incrementNonce(nonceBytes);
const nonce = kdbxweb.ByteUtils.bytesToBase64(nonceBytes);
return {
action: 'change-public-keys',
version: getVersion(request),
publicKey: kdbxweb.ByteUtils.bytesToBase64(keys.publicKey),
nonce,
success: 'true',
...(isKeeWebConnect(request) ? { appName: 'KeeWeb' } : undefined)
};
},
async 'get-databasehash'(request) {
decryptRequest(request);
if (request.triggerUnlock) {
await checkContentRequestPermissions(request);
} else {
ensureAtLeastOneFileIsOpen();
}
return encryptResponse(request, {
hash: KeeWebHash,
success: 'true',
version: getVersion(request)
});
},
'generate-password'(request) {
const password = PasswordGenerator.generate(GeneratorPresets.browserExtensionPreset);
return encryptResponse(request, {
version: getVersion(request),
success: 'true',
entries: [{ password }]
});
},
'lock-database'(request) {
decryptRequest(request);
ensureAtLeastOneFileIsOpen();
Events.emit('lock-workspace');
if (Alerts.alertDisplayed) {
focusKeeWeb();
}
return encryptResponse(request, {
success: 'true',
version: getVersion(request)
});
},
'associate'(request) {
decryptRequest(request);
ensureAtLeastOneFileIsOpen();
return encryptResponse(request, {
success: 'true',
version: getVersion(request),
hash: KeeWebHash,
id: KeeWebAssociationId
});
},
'test-associate'(request) {
const payload = decryptRequest(request);
// ensureAtLeastOneFileIsOpen();
if (payload.id !== KeeWebAssociationId) {
throw makeError(Errors.noOpenFiles);
}
return encryptResponse(request, {
success: 'true',
version: getVersion(request),
hash: KeeWebHash,
id: payload.id
});
},
async 'get-logins'(request) {
const entry = await findEntry(request, true);
return encryptResponse(request, {
success: 'true',
version: getVersion(request),
hash: KeeWebHash,
count: 1,
entries: [
{
group: entry.group.title,
login: entry.user || '',
name: entry.title || '',
password: entry.password?.getText() || '',
skipAutoSubmit: 'false',
stringFields: [],
uuid: kdbxweb.ByteUtils.bytesToHex(entry.entry.uuid.bytes)
}
],
id: ''
});
},
async 'get-totp-by-url'(request) {
const entry = await findEntry(request, true, { otp: true });
entry.initOtpGenerator();
if (!entry.otpGenerator) {
throw makeError(Errors.noMatches);
}
let selectEntryFieldView;
if (entry.needsTouch) {
selectEntryFieldView = new SelectEntryFieldView({
needsTouch: true,
deviceShortName: entry.device.shortName
});
selectEntryFieldView.render();
}
const otpPromise = new Promise((resolve, reject) => {
selectEntryFieldView?.on('result', () => reject(makeError(Errors.userRejected)));
entry.otpGenerator.next((err, otp) => {
if (otp) {
resolve(otp);
} else {
reject(err || makeError(Errors.userRejected));
}
});
});
let totp;
try {
totp = await otpPromise;
} finally {
selectEntryFieldView?.remove();
}
return encryptResponse(request, {
success: 'true',
version: getVersion(request),
totp
});
},
async 'get-any-field'(request) {
const entry = await findEntry(request, false);
const selectEntryFieldView = new SelectEntryFieldView({
entry
});
const inactivityTimer = setTimeout(() => {
selectEntryFieldView.emit('result', undefined);
}, Timeouts.KeeWebConnectRequest);
const field = await selectEntryFieldView.showAndGetResult();
clearTimeout(inactivityTimer);
if (!field) {
throw makeError(Errors.userRejected);
}
let value = entry.getAllFields()[field];
if (value.isProtected) {
value = value.getText();
}
return encryptResponse(request, {
success: 'true',
version: getVersion(request),
field,
value
});
},
async 'get-totp'(request) {
decryptRequest(request);
await checkContentRequestPermissions(request);
throw new Error('Not implemented');
},
async 'set-login'(request) {
const payload = decryptRequest(request);
await checkContentRequestPermissions(request);
focusKeeWeb();
if (!payload.url) {
throw new Error('Empty url');
}
const url = new URL(payload.url);
const files = getAvailableFiles(request);
const client = getClient(request);
let selectedGroup;
let entryToUpdate;
if (payload.uuid) {
for (const file of files) {
const entryId = kdbxweb.ByteUtils.bytesToBase64(
kdbxweb.ByteUtils.hexToBytes(payload.uuid)
);
const foundEntry = file.getEntry(file.subId(entryId));
if (foundEntry) {
if (entryToUpdate) {
throw new Error('Two entries with the same ID found');
} else {
entryToUpdate = foundEntry;
selectedGroup = foundEntry.group;
}
}
}
if (!entryToUpdate) {
throw new Error('Updated entry not found');
}
}
if (client.permissions.askSave === 'auto' && client.permissions.saveTo && !selectedGroup) {
const file = files.find((f) => f.id === client.permissions.saveTo.fileId);
selectedGroup = file?.getGroup(client.permissions.saveTo.groupId);
}
if (client.permissions.askSave !== 'auto' || !selectedGroup) {
if (!selectedGroup && RuntimeDataModel.extensionSaveConfig) {
const file = files.find(
(f) => f.id === RuntimeDataModel.extensionSaveConfig.fileId
);
selectedGroup = file?.getGroup(RuntimeDataModel.extensionSaveConfig.groupId);
}
const allGroups = [];
for (const file of files) {
file.forEachGroup((group) => {
const spaces = [];
for (let parent = group; parent.parentGroup; parent = parent.parentGroup) {
spaces.push(' ', ' ');
}
if (
!selectedGroup &&
group.iconId === ExtensionGroupIconId &&
ExtensionGroupNames.has(group.title)
) {
selectedGroup = group;
}
allGroups.push({
id: group.id,
fileId: file.id,
spaces,
title: group.title,
selected: group.id === selectedGroup?.id
});
});
}
if (!selectedGroup) {
allGroups.splice(1, 0, {
id: '',
fileId: files[0].id,
spaces: [' ', ' '],
title: `${DefaultExtensionGroupName} (${Locale.extensionSaveEntryNewGroup})`,
selected: true
});
}
const saveEntryView = new ExtensionSaveEntryView({
extensionName: getHumanReadableExtensionName(client),
url: payload.url,
user: payload.login,
askSave: RuntimeDataModel.extensionSaveConfig?.askSave || 'always',
update: !!entryToUpdate,
allGroups
});
await alertWithTimeout({
header: Locale.extensionSaveEntryHeader,
icon: 'plus',
buttons: [Alerts.buttons.allow, Alerts.buttons.deny],
view: saveEntryView
});
const config = { ...saveEntryView.config };
if (!entryToUpdate) {
if (config.groupId) {
const file = files.find((f) => f.id === config.fileId);
selectedGroup = file.getGroup(config.groupId);
} else {
selectedGroup = appModel.createNewGroupWithName(
files[0].groups[0],
files[0],
DefaultExtensionGroupName
);
selectedGroup.setIcon(ExtensionGroupIconId);
config.groupId = selectedGroup.id;
}
RuntimeDataModel.extensionSaveConfig = config;
client.permissions.saveTo = { fileId: config.fileId, groupId: config.groupId };
}
client.permissions.askSave = config.askSave;
}
const entryFields = {
Title: url.hostname,
UserName: payload.login,
Password: kdbxweb.ProtectedValue.fromString(payload.password || ''),
URL: payload.url
};
if (entryToUpdate) {
for (const [field, value] of Object.entries(entryFields)) {
if (value) {
entryToUpdate.setField(field, value);
}
}
} else {
appModel.createNewEntryWithFields(selectedGroup, entryFields);
}
client.stats.passwordsWritten++;
Events.emit('browser-extension-sessions-changed');
Events.emit('refresh');
return encryptResponse(request, {
success: 'true',
version: getVersion(request),
count: null,
entries: null,
hash: KeeWebHash
});
},
async 'get-database-groups'(request) {
decryptRequest(request);
await checkContentRequestPermissions(request);
const makeGroups = (group) => {
const res = {
name: group.title,
uuid: kdbxweb.ByteUtils.bytesToHex(group.group.uuid.bytes),
children: []
};
for (const subGroup of group.items) {
if (subGroup.matches()) {
res.children.push(makeGroups(subGroup));
}
}
return res;
};
const groups = [];
for (const file of getAvailableFiles(request)) {
for (const group of file.groups) {
groups.push(makeGroups(group));
}
}
return encryptResponse(request, {
success: 'true',
version: getVersion(request),
groups: { groups }
});
},
async 'create-new-group'(request) {
const payload = decryptRequest(request);
await checkContentRequestPermissions(request);
if (!payload.groupName) {
throw new Error('No groupName');
}
const groupNames = payload.groupName
.split('/')
.map((g) => g.trim())
.filter((g) => g);
if (!groupNames.length) {
throw new Error('Empty group path');
}
const files = getAvailableFiles(request);
for (const file of files) {
for (const rootGroup of file.groups) {
let foundGroup = rootGroup;
const pendingGroups = [...groupNames];
while (pendingGroups.length && foundGroup) {
const title = pendingGroups.shift();
foundGroup = foundGroup.items.find((g) => g.title === title);
}
if (foundGroup) {
return encryptResponse(request, {
success: 'true',
version: getVersion(request),
name: foundGroup.title,
uuid: kdbxweb.ByteUtils.bytesToHex(foundGroup.group.uuid.bytes)
});
}
}
}
const client = getClient(request);
const createGroupView = new ExtensionCreateGroupView({
extensionName: getHumanReadableExtensionName(client),
groupPath: groupNames.join(' / '),
files: files.map((f, ix) => ({ id: f.id, name: f.name, selected: ix === 0 }))
});
await alertWithTimeout({
header: Locale.extensionNewGroupHeader,
icon: 'folder-plus',
buttons: [Alerts.buttons.allow, Alerts.buttons.deny],
view: createGroupView
});
const selectedFile = files.find((f) => f.id === createGroupView.selectedFile);
let newGroup = selectedFile.groups[0];
const pendingGroups = [...groupNames];
while (pendingGroups.length) {
const title = pendingGroups.shift();
const item = newGroup.items.find((g) => g.title === title);
if (item) {
newGroup = item;
} else {
newGroup = appModel.createNewGroupWithName(newGroup, selectedFile, title);
}
}
return encryptResponse(request, {
success: 'true',
version: getVersion(request),
name: newGroup.title,
uuid: kdbxweb.ByteUtils.bytesToHex(newGroup.group.uuid.bytes)
});
}
};
const ProtocolImpl = {
init(vars) {
appModel = vars.appModel;
logger = vars.logger;
sendEvent = vars.sendEvent;
setupListeners();
},
cleanup() {
const wasNotEmpty = connectedClients.size;
connectedClients.clear();
if (wasNotEmpty) {
Events.emit('browser-extension-sessions-changed');
}
},
deleteConnection(connectionId) {
for (const [clientId, client] of connectedClients.entries()) {
if (client.connection.connectionId === connectionId) {
connectedClients.delete(clientId);
}
}
Events.emit('browser-extension-sessions-changed');
},
getClientPermissions(clientId) {
return connectedClients.get(clientId)?.permissions;
},
setClientPermissions(clientId, permissions) {
const client = connectedClients.get(clientId);
if (client?.permissions) {
client.permissions = { ...client.permissions, ...permissions };
}
},
errorToResponse(e, request) {
return {
action: request?.action,
error: e.message || 'Unknown error',
errorCode: e.code || 0
};
},
async handleRequest(request, connectionInfo) {
const appWindowWasFocused = Launcher?.isAppFocused();
let result;
try {
const handler = ProtocolHandlers[request.action];
if (!handler) {
throw new Error(`Handler not found: ${request.action}`);
}
result = await handler(request, connectionInfo);
if (!result) {
throw new Error(`Handler returned an empty result: ${request.action}`);
}
} catch (e) {
if (!e.code) {
logger.error(`Error in handler ${request.action}`, e);
}
result = this.errorToResponse(e, request);
}
if (!appWindowWasFocused && Launcher?.isAppFocused()) {
Launcher.hideApp();
}
return result;
},
get sessions() {
return [...connectedClients.entries()]
.map(([clientId, client]) => ({
clientId,
connectionId: client.connection.connectionId,
appName: client.connection.appName,
extensionName: client.connection.extensionName,
connectedDate: client.stats.connectedDate,
passwordsRead: client.stats.passwordsRead,
passwordsWritten: client.stats.passwordsWritten,
permissions: client.permissions,
permissionsDenied: client.permissionsDenied
}))
.sort((x, y) => y.connectedDate - x.connectedDate);
}
};
export { ProtocolImpl };
+6 -6
View File
@@ -1,8 +1,8 @@
/* eslint-disable import/no-commonjs */
import kdbxweb from 'kdbxweb';
import * as kdbxweb from 'kdbxweb';
import { RuntimeInfo } from 'const/runtime-info';
import { Links } from 'const/links';
import { DateFormat } from 'util/formatting/date-format';
import { DateFormat } from 'comp/i18n/date-format';
import { StringFormat } from 'util/formatting/string-format';
import { Locale } from 'util/locale';
@@ -51,14 +51,14 @@ function walkEntry(db, entry, parents) {
});
}
}
for (const fieldName of Object.keys(entry.fields)) {
for (const [fieldName, fieldValue] of entry.fields) {
if (!KnownFields[fieldName]) {
const value = entryField(entry, fieldName);
if (value) {
fields.push({
title: fieldName,
value,
protect: entry.fields[fieldName].isProtected
protect: fieldValue.isProtected
});
}
}
@@ -69,7 +69,7 @@ function walkEntry(db, entry, parents) {
expires = DateFormat.dtStr(entry.times.expiryTime);
}
const attachments = Object.entries(entry.binaries)
const attachments = [...entry.binaries]
.map(([name, data]) => {
if (data && data.ref) {
data = data.value;
@@ -95,7 +95,7 @@ function walkEntry(db, entry, parents) {
}
function entryField(entry, fieldName) {
const value = entry.fields[fieldName];
const value = entry.fields.get(fieldName);
return (value && value.isProtected && value.getText()) || value || '';
}
+77
View File
@@ -0,0 +1,77 @@
import { SettingsManager } from 'comp/settings/settings-manager';
import { StringFormat } from 'util/formatting/string-format';
const DateFormat = {
months() {
const format = new Intl.DateTimeFormat(SettingsManager.activeLocale, { month: 'long' });
const months = [];
for (let month = 0; month < 12; month++) {
months.push(format.format(new Date(2008, month)));
}
return months;
},
weekDays() {
const format = new Intl.DateTimeFormat(SettingsManager.activeLocale, { weekday: 'long' });
const weekdays = [];
for (let day = 1; day < 8; day++) {
weekdays.push(format.format(new Date(2007, 9, 6 + day)));
}
return weekdays;
},
shortWeekDays() {
const format = new Intl.DateTimeFormat(SettingsManager.activeLocale, { weekday: 'short' });
const weekdays = [];
for (let day = 1; day < 8; day++) {
weekdays.push(format.format(new Date(2007, 9, 6 + day)));
}
return weekdays;
},
dtStr(dt) {
if (typeof dt === 'number') {
dt = new Date(dt);
}
return dt
? new Intl.DateTimeFormat(SettingsManager.activeLocale, {
dateStyle: 'medium',
timeStyle: 'medium'
}).format(dt)
: '';
},
dStr(dt) {
if (typeof dt === 'number') {
dt = new Date(dt);
}
return dt
? new Intl.DateTimeFormat(SettingsManager.activeLocale, {
year: 'numeric',
month: 'short',
day: 'numeric'
}).format(dt)
: '';
},
dtStrFs(dt) {
if (typeof dt === 'number') {
dt = new Date(dt);
}
return dt
? dt.getFullYear() +
'-' +
StringFormat.pad(dt.getMonth() + 1, 2) +
'-' +
StringFormat.pad(dt.getDate(), 2) +
'T' +
StringFormat.pad(dt.getHours(), 2) +
'-' +
StringFormat.pad(dt.getMinutes(), 2) +
'-' +
StringFormat.pad(dt.getSeconds(), 2)
: '';
}
};
export { DateFormat };
+58 -70
View File
@@ -17,6 +17,9 @@ const Launcher = {
platform() {
return process.platform;
},
arch() {
return process.arch;
},
electron() {
return this.req('electron');
},
@@ -55,7 +58,15 @@ const Launcher = {
return this.joinPath(this.userDataPath, fileName || '');
},
getTempPath(fileName) {
return this.joinPath(this.remoteApp().getPath('temp'), fileName || '');
let tempPath = this.joinPath(this.remoteApp().getPath('temp'), 'KeeWeb');
const fs = this.req('fs');
if (!fs.existsSync(tempPath)) {
fs.mkdirSync(tempPath);
}
if (fileName) {
tempPath = this.joinPath(tempPath, fileName);
}
return tempPath;
},
getDocumentsPath(fileName) {
return this.joinPath(this.remoteApp().getPath('documents'), fileName || '');
@@ -142,17 +153,6 @@ const Launcher = {
saveConfig(name, data) {
return this.remoteApp().saveConfig(name, data);
},
ensureRunnable(path) {
if (process.platform !== 'win32') {
const fs = this.req('fs');
const stat = fs.statSync(path);
if ((stat.mode & 0o0111) === 0) {
const mode = stat.mode | 0o0100;
logger.info(`chmod 0${mode.toString(8)} ${path}`);
fs.chmodSync(path, mode);
}
}
},
preventExit(e) {
e.returnValue = false;
return false;
@@ -164,18 +164,18 @@ const Launcher = {
requestExit() {
const app = this.remoteApp();
app.setHookBeforeQuitEvent(false);
if (this.restartPending) {
app.restartApp();
if (this.pendingUpdateFile) {
app.restartAndUpdate(this.pendingUpdateFile);
} else {
app.quit();
}
},
requestRestart() {
this.restartPending = true;
requestRestartAndUpdate(updateFilePath) {
this.pendingUpdateFile = updateFilePath;
this.requestExit();
},
cancelRestart() {
this.restartPending = false;
this.pendingUpdateFile = undefined;
},
setClipboardText(text) {
return this.electron().clipboard.writeText(text);
@@ -191,7 +191,7 @@ const Launcher = {
}
},
quitOnRealQuitEventIfMinimizeOnQuitIsEnabled() {
return this.platform() === 'darwin';
return !!this.pendingUpdateFile;
},
minimizeApp() {
this.remoteApp().minimizeApp({
@@ -203,7 +203,7 @@ const Launcher = {
return process.platform !== 'linux';
},
updaterEnabled() {
return this.electron().remote.process.argv.indexOf('--disable-updater') === -1;
return process.platform !== 'linux';
},
getMainWindow() {
return this.remoteApp().getMainWindow();
@@ -233,57 +233,32 @@ const Launcher = {
},
spawn(config) {
const ts = logger.ts();
let complete = config.complete;
const ps = this.req('child_process').spawn(config.cmd, config.args);
[ps.stdin, ps.stdout, ps.stderr].forEach((s) => s.setEncoding('utf-8'));
let stderr = '';
let stdout = '';
ps.stderr.on('data', (d) => {
stderr += d.toString('utf-8');
if (config.throwOnStdErr) {
try {
ps.kill();
} catch {}
}
});
ps.stdout.on('data', (d) => {
stdout += d.toString('utf-8');
});
ps.on('close', (code) => {
stdout = stdout.trim();
stderr = stderr.trim();
const msg = 'spawn ' + config.cmd + ': ' + code + ', ' + logger.ts(ts);
if (code !== 0) {
logger.error(msg + '\n' + stdout + '\n' + stderr);
} else {
logger.info(msg + (stdout && !config.noStdOutLogging ? '\n' + stdout : ''));
}
if (complete) {
complete(code !== 0 ? 'Exit code ' + code : null, stdout, code);
const { ipcRenderer } = this.electron();
let { complete } = config;
delete config.complete;
ipcRenderer
.invoke('spawnProcess', config)
.then((res) => {
if (res.err) {
logger.error('spawn error: ' + config.cmd + ', ' + logger.ts(ts), res.err);
complete?.(res.err);
} else {
const code = res.code;
const stdout = res.stdout || '';
const stderr = res.stderr || '';
const msg = 'spawn ' + config.cmd + ': ' + code + ', ' + logger.ts(ts);
if (code !== 0) {
logger.error(msg + '\n' + stdout + '\n' + stderr);
} else {
logger.info(msg + (stdout && !config.noStdOutLogging ? '\n' + stdout : ''));
}
complete?.(code !== 0 ? 'Exit code ' + code : null, stdout, code);
}
complete = null;
}
});
ps.on('error', (err) => {
logger.error('spawn error: ' + config.cmd + ', ' + logger.ts(ts), err);
if (complete) {
complete(err);
complete = null;
}
});
if (config.data) {
try {
ps.stdin.end(config.data);
} catch (e) {
logger.error('spawn write error', e);
}
}
process.nextTick(() => {
// it should work without destroy, but a process doesn't get launched
// xubuntu-desktop 19.04 / xfce
// see https://github.com/keeweb/keeweb/issues/1234
ps.stdin.destroy();
});
return ps;
})
.catch((err) => {
complete?.(err);
});
},
checkOpenFiles() {
this.readyToOpenFiles = true;
@@ -301,6 +276,18 @@ const Launcher = {
},
setGlobalShortcuts(appSettings) {
this.remoteApp().setGlobalShortcuts(appSettings);
},
minimizeMainWindow() {
this.getMainWindow().minimize();
},
maximizeMainWindow() {
this.getMainWindow().maximize();
},
restoreMainWindow() {
this.getMainWindow().restore();
},
mainWindowMaximized() {
return this.getMainWindow().isMaximized();
}
};
@@ -308,9 +295,10 @@ Events.on('launcher-exit-request', () => {
setTimeout(() => Launcher.exit(), 0);
});
Events.on('launcher-minimize', () => setTimeout(() => Events.emit('app-minimized'), 0));
Events.on('launcher-maximize', () => setTimeout(() => Events.emit('app-maximized'), 0));
Events.on('launcher-unmaximize', () => setTimeout(() => Events.emit('app-unmaximized'), 0));
Events.on('launcher-started-minimized', () => setTimeout(() => Launcher.minimizeApp(), 0));
Events.on('start-profile', (data) => StartProfiler.reportAppProfile(data));
Events.on('log', (e) => new Logger(e.category || 'remote-app')[e.method || 'info'](e.message));
window.launcherOpen = (file) => Launcher.openFile(file);
if (window.launcherOpenedFile) {
+111 -37
View File
@@ -1,3 +1,4 @@
import * as kdbxweb from 'kdbxweb';
import { Events } from 'framework/events';
import { Logger } from 'util/logger';
import { Launcher } from 'comp/launcher';
@@ -8,11 +9,19 @@ let NativeModules;
if (Launcher) {
const logger = new Logger('native-module-connector');
let host;
let hostRunning = false;
let hostStartPromise;
let callId = 0;
let promises = {};
let ykChalRespCallbacks = {};
const { ipcRenderer } = Launcher.electron();
ipcRenderer.on('nativeModuleCallback', (e, msg) => NativeModules.hostCallback(msg));
ipcRenderer.on('nativeModuleHostError', (e, err) => NativeModules.hostError(err));
ipcRenderer.on('nativeModuleHostExit', (e, { code, sig }) => NativeModules.hostExit(code, sig));
ipcRenderer.on('nativeModuleHostDisconnect', () => NativeModules.hostDisconnect());
ipcRenderer.on('log', (e, ...args) => NativeModules.log(...args));
const handlers = {
yubikeys(numYubiKeys) {
Events.emit('native-modules-yubikeys', { numYubiKeys });
@@ -35,7 +44,7 @@ if (Launcher) {
}
},
'yk-chal-resp-result'({ callbackId, error, result }) {
yubiKeyChallengeResponseResult({ callbackId, error, result }) {
const callback = ykChalRespCallbacks[callbackId];
if (callback) {
const willBeCalledAgain = error && error.touchRequested;
@@ -49,39 +58,39 @@ if (Launcher) {
NativeModules = {
startHost() {
if (host) {
return;
if (hostRunning) {
return Promise.resolve();
}
if (hostStartPromise) {
return hostStartPromise;
}
logger.debug('Starting native module host');
const path = Launcher.req('path');
const appContentRoot = Launcher.remoteApp().getAppContentRoot();
const mainModulePath = path.join(appContentRoot, 'native-module-host.js');
hostStartPromise = this.callNoWait('start').then(() => {
hostStartPromise = undefined;
hostRunning = true;
const { fork } = Launcher.req('child_process');
if (this.usbListenerRunning) {
return this.call('startUsbListener');
}
});
host = fork(mainModulePath);
host.on('message', (message) => this.hostCallback(message));
host.on('error', (e) => this.hostError(e));
host.on('exit', (code, sig) => this.hostExit(code, sig));
this.call('init', Launcher.remoteApp().getAppMainRoot());
if (this.usbListenerRunning) {
this.call('start-usb');
}
return hostStartPromise;
},
hostError(e) {
logger.error('Host error', e);
},
hostDisconnect() {
logger.error('Host disconnected');
},
hostExit(code, sig) {
logger.error(`Host exited with code ${code} and signal ${sig}`);
host = null;
hostRunning = false;
const err = new Error('Native module host crashed');
@@ -110,6 +119,14 @@ if (Launcher) {
}
},
log(name, level, ...args) {
if (!name) {
return;
}
const logger = new Logger(name);
logger[level](...args);
},
autoRestartHost() {
setTimeout(() => {
try {
@@ -121,56 +138,113 @@ if (Launcher) {
},
call(cmd, ...args) {
return new Promise((resolve, reject) => {
if (!host) {
try {
this.startHost();
} catch (e) {
return reject(e);
}
}
return this.startHost().then(() => this.callNoWait(cmd, ...args));
},
callNoWait(cmd, ...args) {
return new Promise((resolve, reject) => {
callId++;
if (callId === Number.MAX_SAFE_INTEGER) {
callId = 1;
}
// logger.debug('Call', cmd, args, callId);
promises[callId] = { cmd, resolve, reject };
host.send({ cmd, args, callId });
ipcRenderer.send('nativeModuleCall', { cmd, args, callId });
});
},
startUsbListener() {
this.call('start-usb');
this.call('startUsbListener');
this.usbListenerRunning = true;
},
stopUsbListener() {
this.usbListenerRunning = false;
if (host) {
this.call('stop-usb');
if (hostRunning) {
this.call('stopUsbListener');
}
},
getYubiKeys(config) {
return this.call('get-yubikeys', config);
return this.call('getYubiKeys', config);
},
yubiKeyChallengeResponse(yubiKey, challenge, slot, callback) {
ykChalRespCallbacks[callId] = callback;
return this.call('yk-chal-resp', yubiKey, challenge, slot, callId);
return this.call('yubiKeyChallengeResponse', yubiKey, challenge, slot, callId);
},
yubiKeyCancelChallengeResponse() {
if (host) {
this.call('yk-cancel-chal-resp');
if (hostRunning) {
this.call('yubiKeyCancelChallengeResponse');
}
},
argon2(password, salt, options) {
return this.call('argon2', password, salt, options);
},
hardwareCryptoDeleteKey: async () => {
await ipcRenderer.invoke('hardwareCryptoDeleteKey');
},
hardwareEncrypt: async (value) => {
const { data, salt } = await ipcRenderer.invoke('hardwareEncrypt', value.dataAndSalt());
return new kdbxweb.ProtectedValue(data, salt);
},
hardwareDecrypt: async (value, touchIdPrompt) => {
const { data, salt } = await ipcRenderer.invoke(
'hardwareDecrypt',
value.dataAndSalt(),
touchIdPrompt
);
return new kdbxweb.ProtectedValue(data, salt);
},
kbdGetActiveWindow(options) {
return this.call('kbdGetActiveWindow', options);
},
kbdGetActivePid() {
return this.call('kbdGetActivePid');
},
kbdShowWindow(win) {
return this.call('kbdShowWindow', win);
},
kbdText(str) {
return this.call('kbdText', str);
},
kbdTextAsKeys(str, mods) {
return this.call('kbdTextAsKeys', str, mods);
},
kbdKeyPress(code, modifiers) {
return this.call('kbdKeyPress', code, modifiers);
},
kbdShortcut(code, modifiers) {
return this.call('kbdShortcut', code, modifiers);
},
kbdKeyMoveWithModifier(down, modifiers) {
return this.call('kbdKeyMoveWithModifier', down, modifiers);
},
kbdKeyPressWithCharacter(character, code, modifiers) {
return this.call('kbdKeyPressWithCharacter', character, code, modifiers);
},
kbdEnsureModifierNotPressed() {
return this.call('kbdEnsureModifierNotPressed');
}
};
global.NativeModules = NativeModules;
}
export { NativeModules };
+104 -21
View File
@@ -1,44 +1,86 @@
import { Events } from 'framework/events';
import { Features } from 'util/features';
import { Locale } from 'util/locale';
import { ThemeWatcher } from 'comp/browser/theme-watcher';
import { AppSettingsModel } from 'models/app-settings-model';
import { Logger } from 'util/logger';
import { Launcher } from 'comp/launcher';
const appleThemes = {
macdark: 'setGenThemeMacDark'
};
const extraThemes = Features.isMac || Features.isiOS ? appleThemes : {};
const logger = new Logger('settings-manager');
const SettingsManager = {
neutralLocale: null,
activeLocale: 'en',
activeLocale: 'en-US',
activeTheme: null,
allLocales: {
'en': 'English',
'en-US': 'English',
'de-DE': 'Deutsch',
'fr-FR': 'Français'
},
allThemes: {
fb: 'setGenThemeFb',
db: 'setGenThemeDb',
dark: 'setGenThemeDark',
light: 'setGenThemeLight',
sd: 'setGenThemeSd',
sl: 'setGenThemeSl',
wh: 'setGenThemeWh',
fb: 'setGenThemeFb',
bl: 'setGenThemeBl',
db: 'setGenThemeDb',
lb: 'setGenThemeLb',
te: 'setGenThemeTe',
hc: 'setGenThemeHc',
...extraThemes
lt: 'setGenThemeLt',
dc: 'setGenThemeDc',
hc: 'setGenThemeHc'
},
// changing something here? don't forget about desktop/app.js
autoSwitchedThemes: [
{
name: 'setGenThemeDefault',
dark: 'dark',
light: 'light'
},
{
name: 'setGenThemeSol',
dark: 'sd',
light: 'sl'
},
{
name: 'setGenThemeBlue',
dark: 'fb',
light: 'bl'
},
{
name: 'setGenThemeBrown',
dark: 'db',
light: 'lb'
},
{
name: 'setGenThemeTerminal',
dark: 'te',
light: 'lt'
},
{
name: 'setGenThemeHighContrast',
dark: 'dc',
light: 'hc'
}
],
customLocales: {},
setBySettings(settings) {
this.setTheme(settings.theme);
this.setFontSize(settings.fontSize);
const locale = settings.locale;
init() {
Events.on('dark-mode-changed', () => this.darkModeChanged());
},
setBySettings() {
this.setTheme(AppSettingsModel.theme);
this.setFontSize(AppSettingsModel.fontSize);
const locale = AppSettingsModel.locale;
try {
if (locale) {
this.setLocale(settings.locale);
this.setLocale(AppSettingsModel.locale);
} else {
this.setLocale(this.getBrowserLocale());
}
@@ -46,7 +88,7 @@ const SettingsManager = {
},
getDefaultTheme() {
return Features.isMac ? 'macdark' : 'fb';
return 'dark';
},
setTheme(theme) {
@@ -61,18 +103,45 @@ const SettingsManager = {
document.body.classList.remove(cls);
}
}
if (AppSettingsModel.autoSwitchTheme) {
theme = this.selectDarkOrLightTheme(theme);
}
document.body.classList.add(this.getThemeClass(theme));
const metaThemeColor = document.head.querySelector('meta[name=theme-color]');
if (metaThemeColor) {
metaThemeColor.content = window.getComputedStyle(document.body).backgroundColor;
}
this.activeTheme = theme;
logger.debug('Theme changed', theme);
Events.emit('theme-applied');
},
getThemeClass(theme) {
return 'th-' + theme;
},
selectDarkOrLightTheme(theme) {
for (const config of this.autoSwitchedThemes) {
if (config.light === theme || config.dark === theme) {
return ThemeWatcher.dark ? config.dark : config.light;
}
}
return theme;
},
darkModeChanged() {
if (AppSettingsModel.autoSwitchTheme) {
for (const config of this.autoSwitchedThemes) {
if (config.light === this.activeTheme || config.dark === this.activeTheme) {
const newTheme = ThemeWatcher.dark ? config.dark : config.light;
logger.debug('Setting theme triggered by system settings change', newTheme);
this.setTheme(newTheme);
break;
}
}
}
},
setFontSize(fontSize) {
const defaultFontSize = Features.isMobile ? 14 : 12;
document.documentElement.style.fontSize = defaultFontSize + (fontSize || 0) * 2 + 'px';
@@ -83,7 +152,7 @@ const SettingsManager = {
return;
}
let localeValues;
if (loc !== 'en') {
if (loc !== 'en-US') {
if (this.customLocales[loc]) {
localeValues = this.customLocales[loc];
} else {
@@ -96,12 +165,26 @@ const SettingsManager = {
Object.assign(Locale, this.neutralLocale, localeValues);
this.activeLocale = loc;
Events.emit('set-locale', loc);
if (Launcher) {
const { ipcRenderer } = Launcher.electron();
const localeValuesForDesktopApp = {};
for (const [key, value] of Object.entries(Locale)) {
if (key.startsWith('sysMenu')) {
localeValuesForDesktopApp[key] = value;
}
}
ipcRenderer.invoke('setLocale', {
locale: loc,
...localeValuesForDesktopApp
});
}
},
getBrowserLocale() {
const language = (navigator.languages && navigator.languages[0]) || navigator.language;
if (language && language.lastIndexOf('en', 0) === 0) {
return 'en';
if (language && language.startsWith('en')) {
return 'en-US';
}
return language;
}
+13 -1
View File
@@ -17,6 +17,12 @@ const Alerts = {
return Locale.alertYes;
}
},
allow: {
result: 'yes',
get title() {
return Locale.alertAllow;
}
},
no: {
result: '',
get title() {
@@ -28,6 +34,12 @@ const Alerts = {
get title() {
return Locale.alertCancel;
}
},
deny: {
result: '',
get title() {
return Locale.alertDeny;
}
}
},
@@ -38,7 +50,7 @@ const Alerts = {
Alerts.alertDisplayed = true;
const view = new ModalView(config);
view.render();
view.on('result', (res, check) => {
view.once('result', (res, check) => {
if (res && config.success) {
config.success(res, check);
}
+18 -3
View File
@@ -26,13 +26,28 @@ const GDriveApps = {
const OneDriveApps = {
Local: {
id: 'b97c53d5-db5b-4124-aab9-d39195293815',
secret: 'V9b6:iJU]N7cImE1f_OLNjqZJDBnumR?'
id: 'b97c53d5-db5b-4124-aab9-d39195293815'
},
Production: {
id: 'bbc74d1b-3a9c-46e6-9da4-4c645e830923'
},
Desktop: {
id: 'bbc74d1b-3a9c-46e6-9da4-4c645e830923',
secret: 'aOMJaktJEAs_Tmh]fx4iQ[Zd3mp3KK7-'
}
};
export { DropboxApps, GDriveApps, OneDriveApps };
const MsTeamsApps = {
Local: {
id: '8fbe2245-13d5-446f-bedc-74c3b2e1f635'
},
Production: {
id: '8fbe2245-13d5-446f-bedc-74c3b2e1f635'
},
Desktop: {
id: '8fbe2245-13d5-446f-bedc-74c3b2e1f635',
secret: 'F02~HYaWs-~7MndJcVRtv9~h-50Brk_9ho'
}
};
export { DropboxApps, GDriveApps, OneDriveApps, MsTeamsApps };
+24 -4
View File
@@ -1,5 +1,6 @@
const DefaultAppSettings = {
theme: null, // UI theme
autoSwitchTheme: false, // automatically switch between light and dark theme
locale: null, // user interface language
expandGroups: true, // show entries from all subgroups
listViewWidth: null, // width of the entry list representation
@@ -8,14 +9,16 @@ const DefaultAppSettings = {
autoUpdate: 'install', // auto-update options: "install", "check", ""
clipboardSeconds: 0, // number of seconds after which the clipboard will be cleared
autoSave: true, // auto-save open files
autoSaveInterval: 0, // interval between performing automatic sync, minutes
rememberKeyFiles: false, // remember keyfiles selected on the Open screen
autoSaveInterval: 0, // interval between performing automatic sync, minutes, -1: on every change
rememberKeyFiles: 'path', // remember keyfiles selected on the Open screen
idleMinutes: 15, // app lock timeout after inactivity, minutes
minimizeOnClose: false, // minimise the app instead of closing
minimizeOnFieldCopy: false, // minimise the app on copy
tableView: false, // view entries as a table instead of list
colorfulIcons: false, // use colorful custom icons instead of grayscale
useMarkdown: true, // use Markdown in Notes field
directAutotype: true, // if only one matching entry is found, select that one automatically
autoTypeTitleFilterEnabled: true, // enable the title filtering in auto-type by default
titlebarStyle: 'default', // window titlebar style
lockOnMinimize: true, // lock the app when it's minimized
lockOnCopy: false, // lock the app after a password was copied
@@ -35,8 +38,18 @@ const DefaultAppSettings = {
allowIframes: false, // allow displaying the app in IFrames
useGroupIconForEntries: false, // automatically use group icon when creating new entries
enableUsb: true, // enable interaction with USB devices
nativeArgon2: true, // use native argon2 module
fieldLabelDblClickAutoType: false, // trigger auto-type by doubleclicking field label
auditPasswords: true, // enable password audit
auditPasswordEntropy: true, // show warnings for weak passwords
excludePinsFromAudit: true, // exclude PIN codes from audit
checkPasswordsOnHIBP: false, // check passwords on Have I Been Pwned
auditPasswordAge: 0, // show warnings about old passwords, number of years, 0 = disabled
deviceOwnerAuth: null, // Touch ID: null / 'memory' / 'file'
deviceOwnerAuthTimeoutMinutes: 0, // how often master password is required with Touch ID
disableOfflineStorage: false, // don't cache loaded files in offline storage
shortLivedStorageToken: false, // short-lived sessions in cloud storage providers
extensionFocusIfLocked: true, // focus KeeWeb if a browser extension tries to connect while KeeWeb is locked
extensionFocusIfEmpty: true, // show the entry selection screen if there's no match found by URL
yubiKeyShowIcon: true, // show an icon to open OTP codes from YubiKey
yubiKeyAutoOpen: false, // auto-load one-time codes when there are open files
@@ -66,6 +79,7 @@ const DefaultAppSettings = {
webdav: true, // enable WebDAV integration
webdavSaveMethod: 'move', // how to save files with WebDAV: "move" or "put"
webdavStatReload: false, // WebDAV: reload the file instead of relying on Last-Modified
gdrive: true, // enable Google Drive integration
gdriveClientId: null, // custom Google Drive client id
@@ -73,7 +87,13 @@ const DefaultAppSettings = {
onedrive: true, // enable OneDrive integration
onedriveClientId: null, // custom OneDrive client id
onedriveClientSecret: null // custom OneDrive client secret
onedriveClientSecret: null, // custom OneDrive client secret
onedriveTenantId: null, // custom OneDrive tenant id
msteams: false, // enable Microsoft Teams integration
msteamsClientId: null, // custom Microsoft Teams client id
msteamsClientSecret: null, // custom Microsoft Teams client secret
msteamsTenantId: null // custom Microsoft Teams tenant id
};
export { DefaultAppSettings };
+21 -21
View File
@@ -3,12 +3,12 @@ const IconMap = [
'globe',
'exclamation-triangle',
'server',
'thumb-tack',
'comments-o',
'thumbtack',
'comments',
'puzzle-piece',
'pencil-square-o',
'edit',
'plug',
'newspaper-o',
'address-card',
'paperclip',
'camera',
'wifi',
@@ -18,32 +18,32 @@ const IconMap = [
'certificate',
'bullseye',
'desktop',
'envelope-o',
'envelope',
'cog',
'clipboard',
'paper-plane-o',
'television',
'paper-plane',
'newspaper',
'bolt',
'inbox',
'floppy-o',
'hdd-o',
'dot-circle-o',
'expeditedssl',
'save',
'hdd',
'dot-circle',
'user-lock',
'terminal',
'print',
'map-signs',
'project-diagram',
'flag-checkered',
'wrench',
'laptop',
'archive',
'credit-card',
'windows',
'clock-o',
'clock',
'search',
'flask',
'gamepad',
'trash-o',
'sticky-note-o',
'trash',
'sticky-note',
'ban',
'question-circle',
'cube',
@@ -53,20 +53,20 @@ const IconMap = [
'unlock-alt',
'lock',
'check',
'pencil',
'picture-o',
'pencil-alt',
'image',
'book',
'list-alt',
'user-secret',
'cutlery',
'utensils',
'home',
'star-o',
'star',
'linux',
'map-pin',
'apple',
'wikipedia-w',
'usd',
'calendar',
'dollar-sign',
'signature',
'mobile'
];
+2
View File
@@ -0,0 +1,2 @@
export const KeeWebLogo =
'data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxyYWRpYWxHcmFkaWVudCBpZD0iYSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGN4PSI2NjMuMTMzIiBjeT0iMTM4LjYwMSIgcj0iODY0LjU2OCIgZng9IjY2My4xMzMiIGZ5PSIxMzguNjAxIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiM5MmJmZjUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzMDQxYzgiLz48L3JhZGlhbEdyYWRpZW50PjwvZGVmcz48cGF0aCBkPSJNOTIzIDM1Ny42MjhjMC05Ljc4MiAwLTE5LjU2NC0uMDU2LTI5LjM0OC0uMDUtOC4yNC0uMTQ0LTE2LjQ4LS4zNjgtMjQuNzE2YTM1OC43MjQgMzU4LjcyNCAwIDAwLTQuNzM2LTUzLjgxMSAxODEuNDk3IDE4MS40OTcgMCAwMC0xNi44NTgtNTEuMTMzIDE3Mi4wNjYgMTcyLjA2NiAwIDAwLTc1LjIzNi03NS4yIDE4MS43MDIgMTgxLjcwMiAwIDAwLTUxLjE4OC0xNi44NiAzNTkuNjI2IDM1OS42MjYgMCAwMC01My44Mi00LjczYy04LjI0Mi0uMjMtMTYuNDg2LS4zMTgtMjQuNzM4LS4zNjgtOS43OC0uMDYyLTE5LjU2OC0uMDYyLTI5LjM1Ni0uMDYyTDU1MyAxMDFoLTg1bC0xMTEuNjM4LjRjLTkuOCAwLTE5LjYxNCAwLTI5LjQyLjA1NC04LjI2Mi4wNS0xNi41Mi4xNDQtMjQuNzc4LjM2OGEzNjAuODA4IDM2MC44MDggMCAwMC01My45NDggNC43NDIgMTgyLjI3MiAxODIuMjcyIDAgMDAtNTEuMjU4IDE2Ljg0OCAxNzIuMjMgMTcyLjIzIDAgMDAtNzUuMzg2IDc1LjE4OCAxODEuMjM4IDE4MS4yMzggMCAwMC0xNi45IDUxLjE2IDM1OC4zMTIgMzU4LjMxMiAwIDAwLTQuNzQyIDUzLjhjLS4yMjIgOC4yNC0uMzE4IDE2LjQ4LS4zNjggMjQuNzItLjA2IDkuNzg0LS41NjIgMjEuOTM2LS41NjIgMzEuNzJ2MTk2bC41MDggMTEyLjQyOGMwIDkuOCAwIDE5LjYuMDU0IDI5LjM4OC4wNSA4LjI1Mi4xNDYgMTYuNTAyLjM2OCAyNC43NWEzNTkuMDQ2IDM1OS4wNDYgMCAwMDQuNzQ2IDUzLjg4NSAxODEuNjY4IDE4MS42NjggMCAwMDE2Ljg5IDUxLjIwMSAxNzIuMzU2IDE3Mi4zNTYgMCAwMDc1LjM4MiA3NS4zMDIgMTgyLjEwNSAxODIuMTA1IDAgMDA1MS4yODggMTYuODggMzYwLjIwNyAzNjAuMjA3IDAgMDA1My45MjQgNC43MzZjOC4yNTguMjI0IDE2LjUxOC4zMTggMjQuNzguMzY4IDkuOC4wNiAxOS42MTIuMDU2IDI5LjQyLjA1NmgzMTAuMjg0YzkuNzg4IDAgMTkuNTc2IDAgMjkuMzY0LS4wNTQgOC4yNDQtLjA1IDE2LjQ4OC0uMTQ0IDI0LjczLS4zNjhhMzU5LjEgMzU5LjEgMCAwMDUzLjg0LTQuNzQgMTgxLjUxOCAxODEuNTE4IDAgMDA1MS4xNi0xNi44NzIgMTcyLjE5NSAxNzIuMTk1IDAgMDA3NS4yNC03NS4yOTggMTgxLjk1IDE4MS45NSAwIDAwMTYuODY2LTUxLjIzIDM1OS44MTUgMzU5LjgxNSAwIDAwNC43MzItNTMuODY0Yy4yMjQtOC4yNS4zMTgtMTYuNS4zNjgtMjQuNzUuMDYtOS44LjA1Ni0xOS42LjA1Ni0yOS4zODh2LTMxMC44IiBmaWxsPSJ1cmwoI2EpIi8+PHBhdGggZD0iTTk5IDEwMWg4MjR2ODI0SDk5eiIgZmlsbD0ibm9uZSIvPjxnIGZpbGw9IiNmZmYiPjxwYXRoIGQ9Ik0zNzkuMTk2IDgxMy4yNzNjLTUuOTM5LS4wMS0xMi41My0yLjUwMi0xOS4zMjYtNi4wNjctMTQuNDk3LTcuNjA1LTI0LjQzOS0xNy4yMi0xNi4xMzktMzMuMDQyTDU0NS4zOCAzODkuNzYxYTE1MTguMzEgMTUxOC4zMSAwIDAxLTUuNzU4LTIuOTk4Yy03Mi43MDktMzguMTQyLTEwNC45NjctOTQuNTM1LTc4Ljg1Mi0xNDQuMzE4IDMwLjAxOC01Ny4yMjMgMTIyLjk4NC02Ny42MDggMjA3LjY0Ny0yMy4xOTYgODQuNjYzIDQ0LjQxMyAxMjguOTYxIDEyNi44MDUgOTguOTQzIDE4NC4wMjgtMjUuODE1IDQ5LjIxMS05My4yMTcgNTMuNTk5LTE2MC45OSAxOC4wNDctMS44NjctLjk4LTQuODk2LTIuNTUzLTguMzI0LTQuMzNMMzk2LjIzMSA4MDEuNzA1Yy00LjQwOSA4LjQwNS0xMC4zMDQgMTEuNTc5LTE3LjAzNSAxMS41Njh6bS0zNy4xMjctODIuMzUybC0zLjgxNi0yLjAwMi0uODUtLjQ0NS01MS42NTItMjcuMDk2di0uMDAybC0xMi40NTEtNi41MzFhNC4zMSA0LjMxIDAgMDEtMS44MTUtNS44MmwxNi4zNDYtMzEuMTZhNC4zMTIgNC4zMTIgMCAwMTUuNzg5LTEuODMybC4wMzMuMDE1IDI0LjkwMyAxMy4wNjVhNC4zMTEgNC4zMTEgMCAwMDUuODItMS44MTdsMTYuMzQ4LTMxLjE2YTQuMzEgNC4zMSAwIDAwLTEuODE3LTUuODJsLTEyLjQ1MS02LjUzMy0xMi40NTEtNi41MzJhNC4zMSA0LjMxIDAgMDEtMS44MTUtNS44MmwxNi4zNDYtMzEuMTYyYTQuMzEgNC4zMSAwIDAxNS44Mi0xLjgxNGwxMi40NTMgNi41MzEgMzIuNDExIDE3LjAwMiAyMC4wODkgMTAuNTM5LjAyNi4wMTQgMy43OTMgMS45OS0xNy4xNjggMzIuNzI2LTEuMTgyIDIuMjUyYS4wMDguMDA4IDAgMDEtLjAwMi4wMDRsLTI0LjM1NyA0Ni40My0yLjQ3NyA0LjcyMS0xMy44NjkgMjYuNDM5LS4wMS4wMTgtMS45OTQgMy44ek00MjMuNjkgNTc1LjMzbC0zLjgxOC0yLjAwNC0xOC4xMzUtOS41MTQtMTQuMzE4LTcuNTEyYTQuMzEgNC4zMSAwIDAxLTEuODE3LTUuODJsMTYuMzQ4LTMxLjE2YTQuMzExIDQuMzExIDAgMDE1LjgyLTEuODE3bDE0LjMxOSA3LjUxMiAxOC4xMzYgOS41MTQuMDI2LjAxMyAzLjc5MyAxLjk5MS0yLjAwNCAzLjgxOC04LjE3MiAxNS41OC04LjE3NCAxNS41OC0uMDEuMDE2LTEuOTk0IDMuODAzem0yNjYuODEzLTE4NC40MjZjMTAuNzUzLjAyIDE5LjE2My00LjMxMiAyNC43NS0xNC45NjMgMTcuMDI4LTMyLjQ2LTE0LjQ3Mi04Mi41MzgtNzAuMzU2LTExMS44NTQtNTUuODg0LTI5LjMxNi0xMTQuOTkxLTI2Ljc2OC0xMzIuMDE5IDUuNjkyLTE3LjAyOCAzMi40NTkgMzAuMzM0IDUzLjc3MiA4Ni4yMTkgODMuMDg4IDM3LjU0NyAxOS42OTYgNjkuMzg4IDM3Ljk5NCA5MS40MDYgMzguMDM3eiIvPjxwYXRoIGQ9Ik00MjMuNjkxIDU3NS4zMjlsMTkuMDkxIDEwLjAxNSAyMC4zNTMtMzguNzk3LTE5LjA5MS0xMC4wMTV6TTM0Mi4wNyA3MzAuOTIybDE5LjA5MSAxMC4wMTUgNjEuMDU3LTExNi4zOTItMTkuMDkxLTEwLjAxNXoiIGZpbGwtb3BhY2l0eT0iLjIiLz48L2c+PC9zdmc+';
+5
View File
@@ -0,0 +1,5 @@
const KnownAppVersions = {
KeePassXC: '2.6.4'
};
export { KnownAppVersions };
+20 -3
View File
@@ -6,17 +6,34 @@ const Links = {
BetaWebApp: 'https://beta.keeweb.info',
License: 'https://github.com/keeweb/keeweb/blob/master/LICENSE',
LicenseApache: 'https://opensource.org/licenses/Apache-2.0',
UpdateDesktop: 'https://github.com/keeweb/keeweb/releases/download/v{ver}/UpdateDesktop.zip',
LicenseLinkCCBY40: 'https://creativecommons.org/licenses/by/4.0/',
UpdateBasePath: 'https://github.com/keeweb/keeweb/releases/download/v{ver}/',
ReleaseNotes: 'https://github.com/keeweb/keeweb/blob/master/release-notes.md#release-notes',
SelfHostedDropbox: 'https://github.com/keeweb/keeweb#self-hosting',
Manifest: 'https://app.keeweb.info/manifest.appcache',
UpdateJson: 'https://app.keeweb.info/update.json',
AutoType: 'https://github.com/keeweb/keeweb/wiki/Auto-Type',
AutoTypeMacOS: 'https://github.com/keeweb/keeweb/wiki/Auto-Type#macos',
Translation: 'https://keeweb.oneskyapp.com/',
Donation: 'https://opencollective.com/keeweb#support',
Plugins: 'https://plugins.keeweb.info',
PluginDevelopStart: 'https://github.com/keeweb/keeweb/wiki/Plugins',
YubiKeyManual: 'https://github.com/keeweb/keeweb/wiki/YubiKey',
YubiKeyManagerInstall: 'https://github.com/Yubico/yubikey-manager#installation'
YubiKeyManagerInstall: 'https://github.com/Yubico/yubikey-manager#installation',
HaveIBeenPwned: 'https://haveibeenpwned.com',
HaveIBeenPwnedPrivacy: 'https://haveibeenpwned.com/Passwords',
ExtensionHelpForOtherBrowsers:
'https://github.com/keeweb/keeweb/wiki/Browser-AutoFill#other-browsers',
KWCForChrome:
'https://chrome.google.com/webstore/detail/keeweb-connect/pikpfmjfkekaeinceagbebpfkmkdlcjk',
KWCForFirefox: 'https://addons.mozilla.org/firefox/addon/keeweb-connect/',
KWCForEdge:
'https://microsoftedge.microsoft.com/addons/detail/keewebconnect/nmggpehkjmeaeocmaijenpejbepckinm',
KWCForSafari: 'https://apps.apple.com/app/keeweb-connect/id1565748094',
KPXCForChrome:
'https://chrome.google.com/webstore/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk',
KPXCForFirefox: 'https://addons.mozilla.org/firefox/addon/keepassxc-browser/',
KPXCForEdge:
'https://microsoftedge.microsoft.com/addons/detail/keepassxcbrowser/pdffhmdngciaglkoonimfcmckehcpafo'
};
export { Links };
+2 -1
View File
@@ -3,7 +3,8 @@ const RuntimeInfo = {
beta: !!'@@BETA',
buildDate: '@@DATE',
commit: '@@COMMIT',
devMode: '@@DEVMODE'
devMode: '@@DEVMODE',
appleTeamId: '@@APPLE_TEAM_ID'
};
export { RuntimeInfo };
+4 -1
View File
@@ -16,7 +16,10 @@ const Timeouts = {
ExternalDeviceReconnect: 3000,
ExternalDeviceAfterReconnect: 1000,
FieldLabelDoubleClick: 300,
NativeModuleHostRestartTime: 3000
NativeModuleHostRestartTime: 3000,
FastAnimation: 100,
AutoTypeCopyPaste: 300,
KeeWebConnectRequest: 60000
};
export { Timeouts };
+2 -2
View File
@@ -25,7 +25,7 @@ const Resizable = {
if (dragInfo.auto !== undefined) {
this.$el.css(dragInfo.prop, dragInfo.auto);
} else {
this.$el.css(dragInfo.prop, 'auto');
this.$el.css(dragInfo.prop, '');
}
this.fixSize(dragInfo);
this.emit('view-resize', null);
@@ -55,7 +55,7 @@ const Resizable = {
const propLower = prop.toLowerCase();
const min = this.getSizeProp('min' + prop);
const max = this.getSizeProp('max' + prop);
const auto = this.getSizeProp('auto' + prop) || 'auto';
const auto = this.getSizeProp('auto' + prop);
const startSize = this.$el[propLower]();
return { startSize, prop: propLower, min, max, auto };
},
+7 -3
View File
@@ -24,7 +24,7 @@ class View extends EventEmitter {
model = undefined;
options = {};
views = {};
hidden = false;
hidden = undefined;
removed = false;
modal = undefined;
eventListeners = {};
@@ -239,6 +239,10 @@ class View extends EventEmitter {
if (visible === undefined) {
visible = this.hidden;
}
if (this.hidden === !visible) {
this.debugLogger?.debug('Toggle: noop', visible);
return;
}
this.hidden = !visible;
if (this.modal) {
if (visible) {
@@ -247,7 +251,6 @@ class View extends EventEmitter {
FocusManager.setModal(null);
}
}
this.emit(visible ? 'show' : 'hide');
if (this.el) {
this.el.classList.toggle('show', !!visible);
this.el.classList.toggle('hide', !visible);
@@ -255,10 +258,11 @@ class View extends EventEmitter {
Tip.hideTips(this.el);
}
}
this.emit(visible ? 'show' : 'hide');
}
isHidden() {
return this.hidden;
return !!this.hidden;
}
isVisible() {
-1
View File
@@ -4,4 +4,3 @@ import 'hbs-helpers/ifeq';
import 'hbs-helpers/ifneq';
import 'hbs-helpers/ifemptyoreq';
import 'hbs-helpers/res';
import 'hbs-helpers/svg';
+5 -1
View File
@@ -12,10 +12,14 @@ Handlebars.registerHelper('res', function (key, options) {
return value;
});
Handlebars.registerHelper('Res', (key) => {
Handlebars.registerHelper('Res', (key, options) => {
let value = Locale[key];
if (value) {
value = value[0].toUpperCase() + value.substr(1);
const ix = value.indexOf('{}');
if (ix >= 0) {
value = value.replace('{}', options.fn(this));
}
}
return value;
});
-9
View File
@@ -1,9 +0,0 @@
import Handlebars from 'hbs';
Handlebars.registerHelper('svg', (name, cls) => {
const icon = require(`svg/${name}.svg`).default;
if (typeof cls === 'string') {
return `<svg class="${cls}"` + icon.substr(4);
}
return icon;
});
File diff suppressed because it is too large Load Diff
+172 -122
View File
@@ -1,50 +1,4 @@
{
"months": [
"Januar",
"Februar",
"März",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Dezember"
],
"monthsShort": [
"Jan",
"Feb",
"Mär",
"Apr",
"Mai",
"Jun",
"Jul",
"Aug",
"Sep",
"Okt",
"Nov",
"Dez"
],
"weekdays": [
"Sonntag",
"Montag",
"Dienstag",
"Mittwoch",
"Donnerstag",
"Freitag",
"Samstag"
],
"weekdaysShort": [
"So",
"Mo",
"Di",
"Mi",
"Do",
"Fr",
"Sa"
],
"retToApp": "zurück zur App",
"name": "Name",
"icon": "Icon",
@@ -56,10 +10,11 @@
"notes": "Notizen",
"entry": "Eintrag",
"group": "Gruppe",
"noTitle": "Keinen Titel",
"noTitle": "Kein Titel",
"or": "oder",
"history": "Verlauf",
"template": "Template",
"template": "Vorlage",
"templates": "Vorlagen",
"notImplemented": "Nicht implementiert",
"saveChanges": "Änderungen speichern",
"discardChanges": "Änderungen verwerfen",
@@ -72,6 +27,15 @@
"shiftKey": "Umschalt",
"altKey": "Alt",
"error": "Fehler",
"oneMinute": "Eine Minute",
"minutes": "{} Minuten",
"oneHour": "Eine Stunde",
"hours": "{} Stunden",
"oneDay": "Ein Tag",
"days": "{} Tage",
"oneWeek": "Eine Woche",
"oneMonth": "Ein Monat",
"oneYear": "Ein Jahr",
"cache": "Cache",
"file": "Datei",
"device": "Gerät",
@@ -86,13 +50,29 @@
"menuSetAbout": "Info",
"menuSetDevices": "Geräte",
"menuAlertNoTags": "Keine Tags",
"menuAlertNoTagsBody": "Neue Tags können durch das Bearbeiten von Einträgen (Abschnitt “Tags”) hinzugefügt werden.",
"menuEmptyTrash": "Leere Papierkorb",
"menuAlertNoTagsBody": "Neue Tags können durch das Bearbeiten von Einträgen (Abschnitt „Tags“) hinzugefügt werden.",
"menuEmptyTrash": "Papierkorb leeren",
"menuEmptyTrashAlert": "Papierkorb leeren?",
"menuEmptyTrashAlertBody": "Die Einträge können nicht wiederhergestellt werden",
"menuItemCollapsed": "Doppelklick zum Aufklappen",
"menuRestoreApp": "{} öffnen",
"menuQuitApp": "{} beenden",
"sysMenuAboutKeeWeb": "Über {}",
"sysMenuServices": "Dienste",
"sysMenuHide": "{} ausblenden",
"sysMenuHideOthers": "Andere ausblenden",
"sysMenuUnhide": "Alle anzeigen",
"sysMenuQuit": "{} beenden",
"sysMenuEdit": "Bearbeiten",
"sysMenuUndo": "Rückgängig",
"sysMenuRedo": "Wiederherstellen",
"sysMenuCut": "Ausschneiden",
"sysMenuCopy": "Kopieren",
"sysMenuPaste": "Einfügen",
"sysMenuSelectAll": "Alles auswählen",
"sysMenuWindow": "Fenster",
"sysMenuMinimize": "Minimieren",
"sysMenuClose": "Fenster schließen",
"alertYes": "Ja",
"alertNo": "Nein",
"alertOk": "OK",
@@ -109,21 +89,21 @@
"genLen": "Länge",
"genNewPass": "Neues Passwort",
"genPresetDefault": "Standardeinstellung",
"genPresetDerived": "unverändert",
"genPresetDerived": "Wie das alte Passwort",
"genPresetPronounceable": "aussprechbar",
"genPresetMed": "durchschnittlich lang",
"genPresetMed": "Durchschnittliche Länge",
"genPresetLong": "lang",
"genPresetPin4": "4-stellige PIN",
"genPresetMac": "MAC-Adresse",
"genPresetHash128": "128-Bit Hash",
"genPresetHash256": "256-Bit Hash",
"genPresetHash128": "128-Bit-Hash",
"genPresetHash256": "256-Bit-Hash",
"genHidePass": "Passwort ausblenden",
"genShowPass": "Passwort einblenden",
"grpTitle": "Gruppe",
"grpSearch": "Suche für Einträge in dieser Gruppe aktivieren",
"grpAutoType": "Auto-Type aktivieren",
"grpAutoTypeSeq": "Auto-Type Abfolge",
"grpAutoTypeSeqDefault": "Standard Auto-Type Abfolge verwenden",
"grpAutoTypeSeq": "Auto-Type-Sequenz",
"grpAutoTypeSeqDefault": "Standard-Auto-Type-Sequenz verwenden",
"grpTrash": "Gruppe und alle enthaltenen Einträge löschen",
"tagTitle": "Tag",
"tagTrash": "Diesen Tag von allen Einträgen entfernen",
@@ -131,14 +111,14 @@
"tagTrashQuestion": "Diesen Tag von allen Einträgen entfernen?",
"tagTrashQuestionBody": "Der Tag wird von allen Einträgen entfernt. Diese Aktion kann nicht rückgängig gemacht werden.",
"tagExists": "Tag existiert bereits",
"tagExistsBody": "Ein Tag mit diesem Namen ist bereits vorhanden. Bitte wählen Sie einen anderen Namen.",
"tagExistsBody": "Ein Tag mit diesem Namen ist bereits vorhanden. Bitte einen anderen Namen wählen.",
"tagBadName": "Ungültiger Name",
"tagBadNameBody": "Der Tag-Name darf nicht die folgenden Zeichen enthalten: {}. Bitte entfernen Sie sie.",
"tagBadNameBody": "Der Tag-Name darf folgende Zeichen nicht enthalten: {}. Bitte diese entfernen.",
"genPsTitle": "Generator-Voreinstellungen",
"genPsCreate": "Neue Voreinstellung",
"genPsDelete": "Voreinstellung löschen",
"genPsNew": "Voreinstellung",
"genPsEnabled": "In Voreinstellungs-Liste anzeigen",
"genPsEnabled": "In Voreinstellungsliste anzeigen",
"genPsDefault": "Standardmäßig ausgewählt",
"genPsDefaultLength": "Standardlänge",
"genPsUpper": "Lateinische Großbuchstaben",
@@ -151,16 +131,16 @@
"genPsInclude": "Zusätzliche Symbole angeben",
"genPsExample": "Beispiel für ein generiertes Passwort",
"genPsPattern": "Muster",
"genPsPatternHelp": "Mit Mustern können eigene Regeln zur Auswahl von Zeichen festgelegt werden. Beispielsweise werden mit 1-AA Passwörter generiert, die mit einer Ziffer starten, gefolgt von einem Bindestrich und zwei Großbuchstaben. Die folgenden Symbole stehen zur Verfügung:",
"genPsPatternHelp": "Mit Mustern können eigene Regeln zur Auswahl von Zeichen festgelegt werden. Beispielsweise werden mit 1-AA Passwörter generiert, die mit einer Ziffer starten, gefolgt von einem Bindestrich und zwei Großbuchstaben. Folgende Symbole stehen zur Verfügung:",
"genPsAllRanges": "Alle Zeichen",
"genPsIncluded": "Zusätzliche oben hinzugefügte Zeichen",
"keyChangeTitleRemote": "Hauptschlüssel geändert",
"keyChangeMessageRemote": "Der Hauptschlüssel für diese Datenbank wurde geändert. Bitte geben Sie das neue Passwort ein.",
"keyChangeMessageRemote": "Der Hauptschlüssel für diese Datenbank wurde geändert. Bitte das neue Passwort eingeben.",
"keyChangeTitleExpired": "Hauptschlüssel abgelaufen",
"keyChangeMessageExpired": "Der Hauptschlüssel für diese Datenkbank ist abgelaufen. Bitte geben Sie einen neuen Schlüssel ein.",
"keyChangeRepeatPassword": "Passwort (Wiederholung)",
"keyEnter": "Enter",
"iconFavTitle": "Website-Icon herunterladen und verwenden",
"iconFavTitle": "Website-Symbol herunterladen und verwenden",
"iconSelCustom": "Eigenes Icon auswählen",
"listEmptyTitle": "Leer",
"listEmptyAdd": "Zum Anlegen neuer Einträge den obigen {} Button benutzen",
@@ -168,9 +148,9 @@
"listNoWebsite": "keine Website",
"listNoUser": "kein Benutzer",
"listNoAttachments": "keine Anhänge",
"listAddTemplateHeader": "Templates",
"listAddTemplateBody1": "Templates ermöglichen es, Einträge mit nur einem Klick zu erstellen. Fügen Sie etwas zum Template-Eintrag hinzu und klicken Sie auf {}, um dieses Template zu benutzen.",
"listAddTemplateBody2": "Sie können Ihre Templates in der Gruppe “{}” finden.",
"listAddTemplateHeader": "Vorlagen",
"listAddTemplateBody1": "Vorlagen ermöglichen es, Einträge mit nur einem Klick zu erstellen. Fügen Sie etwas zum Vorlage-Eintrag hinzu und klicken Sie auf {}, um diese Vorlage zu verwenden.",
"listAddTemplateBody2": "Sie können Ihre Vorlagen in der Gruppe “{}” finden.",
"searchAddNew": "Hinzufügen",
"searchSort": "Sortierung",
"searchCreated": "Erstelldatum",
@@ -202,17 +182,17 @@
"openKeyFile": "Schlüsseldatei",
"openKeyFileDropbox": "(von Dropbox)",
"openDropHere": "Dateien hier ablegen",
"openFailedRead": "Konnte die Datei nicht lesen",
"openFailedRead": "Datei konnte nicht gelesen werden",
"openNothingFound": "Nichts gefunden",
"openNothingFoundBody": "Es konnten keine kompatiblen Dateien gefunden werden.",
"openSelectFile": "Datei auswählen",
"openSelectFileBody": "Wählen Sie eine Datei zum Öffnen aus",
"openPassFor": "Passwort für",
"openRemoveLastQuestion": "Lokale Datei löschen?",
"openRemoveLastQuestionBody": "Die Datei, die Sie löschen möchten, ist nur innerhalb der App gespeichert. Möchten Sie sie unwiederbringlich löschen?",
"openRemoveLastQuestionBody": "Die Datei, die Sie löschen möchten, ist nur innerhalb der App gespeichert. Möchten Sie diese unwiederbringlich löschen?",
"openRemoveLastQuestionModBody": "Die Datei, die Sie löschen möchten, beinhaltet lokale Änderungen. Möchten Sie diese Änderungen verwerfen und die Datei löschen?",
"openLocalFile": "Lokale Datei",
"openLocalFileBody": "Sie sind dabei, eine Datei zu öffnen, die innerhalb der App gespeichert wird. Wenn Sie an dieser Datei Änderungen vornehmen, werden diese nicht im Dateisystem gespeichert. Um die Datei mit den Änderungen zu erhalten, können Sie sie aus den Einstellungen heraus exportieren.",
"openLocalFileBody": "Sie sind dabei, eine Datei zu öffnen, die innerhalb der App gespeichert wird. Wenn Sie an dieser Datei Änderungen vornehmen, werden diese nicht im Dateisystem gespeichert. Um die Datei mit den Änderungen zu erhalten, können Sie diese aus den Einstellungen heraus exportieren.",
"openLocalFileDontShow": "Nicht wieder anzeigen",
"openWrongFile": "Inkompatibles Dateiformat",
"openWrongFileBody": "Dieses Dateiformat ist nicht kompatibel. Die App unterstützt KeePass-Datenbanken im kdbx-Format.",
@@ -221,10 +201,10 @@
"openUrl": "URL",
"openUrlDesc": "https://server/pfad/datei.kdbx, oder einfach datei.kdbx",
"openUser": "Benutzername",
"openUserDesc": "WebDAV Server Benutzername (falls erforderlich)",
"openUserDesc": "WebDAV-Server-Benutzername (falls erforderlich)",
"openUserPlaceholder": "kein Benutzername",
"openPass": "Passwort",
"openPassDesc": "WebDAV Server-Passwort (nicht das KDBX-Datei-Passwort)",
"openPassDesc": "WebDAV-Server-Passwort (nicht das KDBX-Datei-Passwort)",
"openPassPlaceholder": "kein Passwort",
"openConfigError": "Fehler: {}",
"openConfigErrorNotFound": "Datei nicht gefunden",
@@ -238,12 +218,13 @@
"openChalRespLoading": "Liste mit YubiKeys wird geladen",
"openChalRespSelectYubiKey": "Wählen Sie den YubiKey aus, den Sie benutzen wollen",
"openChalRespErrorEmpty": "Keine YubiKeys gefunden.",
"openChalRespErrorEmptyMac": "Verwenden Sie dieses Feature zum ersten Mal auf MacOS? KeeWeb muss in Ihren Sicherheitseinstellung im Input-Monitoring-Abschnitt hinzugefügt werden.",
"detAttDownload": "Umschalttaste + Klick auf den Anhang-Button zum Herunterladen oder",
"detAttDelToRemove": "Backspace zum entfernen",
"detAttSave": "Speichern unter...",
"detAttDelToRemove": "Backspace zum Entfernen",
"detAttSave": "Speichern unter …",
"detEmpty": "Ihre Passwörter werden hier angezeigt",
"detGroupRestore": "Ziehen Sie diese Gruppe in eine andere Gruppe als den Papierkorb, um sie wiederherzustellen.",
"detHistoryClickPoint": "Klicken Sie auf einen der Punkte im Verlauf um den entsprechenden Zustand des Eintrags anzuzeigen",
"detHistoryClickPoint": "Klicken Sie auf einen der Punkte im Verlauf, um den entsprechenden Zustand des Eintrags anzuzeigen",
"detHistoryReturn": "zurück zum Eintrag",
"detHistoryRevert": "Auf diese Version zurücksetzen",
"detHistoryDel": "Version löschen",
@@ -264,7 +245,7 @@
"detHistoryDiscardChangesAlertBody": "Ungespeicherte Änderungen gehen dabei unwiderruflich verloren.",
"detBackToList": "zurück zur Liste",
"detSetIconColor": "Farbe ändern",
"detSetIcon": "Icon ändern",
"detSetIcon": "Symbol ändern",
"detDropAttachments": "Anhänge hier ablegen",
"detDelEntry": "Löschen",
"detDelEntryPerm": "Dauerhaft löschen",
@@ -277,7 +258,7 @@
"detAttachments": "Anhänge",
"detDelFromTrash": "Aus dem Papierkorb löschen?",
"detDelFromTrashBody": "Es wird keine Möglichkeit zur Wiederherstellung geben.",
"detDelFromTrashBodyHint": "Um alle Dateien aus dem Papierkorb zu entfernen, klicken Sie auf das “Papierkorb” leeren-Icon im Papierkorb-Menüpunkt.",
"detDelFromTrashBodyHint": "Um alle Dateien aus dem Papierkorb zu entfernen, klicken Sie auf das „Papierkorb leeren“-Symbol im Papierkorb-Menüpunkt.",
"detDelToTrash": "Diesen Eintrag löschen?",
"detDelToTrashBody": "Der Eintrag wird in den Papierkorb verschoben.",
"detFieldCopied": "Kopiert",
@@ -299,12 +280,12 @@
"detAutoType": "Auto-Type",
"detAutoTypeSettings": "Auto-Type Einstellungen",
"detAutoTypeEnabled": "Auto-Type für diesen Eintrag aktivieren",
"detAutoTypeSequence": "Tastensequenz",
"detAutoTypeSequence": "Tastenfolge",
"detAutoTypeInput": "Eingabe",
"detAutoTypeShortcutsDesc": "{} oder {} während die App inaktiv ist",
"detAutoTypeObfuscation": "Echte Tastenanschläge mit zufälligen mischen",
"detAutoTypeWindow": "Fenster",
"detAutoTypeInputWindow": "Fenster-Titel",
"detAutoTypeInputWindow": "Fenstertitel",
"detSetupOtpAlert": "QR-Code einscannen",
"detSetupOtpAlertBody": "Bitte kopieren Sie den QR-Code, der auf der Autorisierungsseite angezeigt wird.",
"detSetupOtpAlertBody1": "1. Gehen Sie zur Autorisierungsseite",
@@ -317,25 +298,35 @@
"detSetupOtpAlertBodyWith": "mit {}",
"detOtpImageError": "Fehler beim Einlesen des Bildes",
"detOtpImageErrorBody": "Entschuldigung, wir konnten das Bildformat nicht lesen. Bitte kontaktieren Sie den Hersteller mit Einzelheiten zu diesem Fehler.",
"detOtpImageReading": "QR-Code wird eingelesen...",
"detOtpImageReading": "QR-Code wird eingelesen …",
"detOtpQrError": "QR-Code Lesefehler",
"detOtpQrErrorBody": "Der QR-Code konnte leider nicht eingelesen werden. Bitte versuchen Sie es noch einmal oder kontaktieren Sie den Hersteller mit Einzelheiten zum aufgetretenen Fehler.",
"detOtpQrWrong": "Fehlerhafter QR-Code",
"detOtpQrWrongBody": "Ihr QR-Code wurde erfolgreich eingelesen, enthält jedoch keinen Einmal-Code.",
"detOtpField": "Einmal-Code",
"detOtpClickToTouch": "Zum Erzeugen hier klicken",
"detOtpGenerating": "Wird erzeugt...",
"detOtpGenerating": "Wird erzeugt …",
"detOtpTouch": "Berühren Sie Ihren {}",
"detLockField": "Dieses Feld sperren, damit der Inhalt nicht durchsuchbar ist und angezeigt wird. Um ihn anzuzeigen, muss er angeklickt werden.",
"detUnlockField": "Dieses Feld entsperren. Dies führt dazu, dass der Inhalt direkt angezeigt wird und durchsuchbar ist.",
"detRevealField": "Anzeigen",
"detHideField": "Verbergen",
"detAutoTypeField": "Auto-Type",
"detIssuesHideTooltip": "Diese Warnung verstecken",
"detIssueWeakPassword": "Das Passwort ist schwach, es wird empfohlen es zu ändern.",
"detIssuePoorPassword": "Das Passwort ist schwach, es wird dringend empfohlen es zu ändern.",
"detIssuePwnedPassword": "Dieses Passwort wurde laut {} in einem Datenleck gefunden, es wird empfohlen das Passwort zu ändern.",
"detIssuePasswordCheckError": "Es gab einen Fehler bei der online-überprüfung des Passwort.",
"detIssueOldPassword": "Das Passwort ist alt.",
"detIssueCloseAlertHeader": "Verstecke Passwortmeldungen",
"detIssueCloseAlertBody": "Es gibt verschiedene Möglichkeiten diese Warnung zu verstecken:",
"detIssueCloseAlertEntry": "Nicht für diesen Eintrag anzeigen.",
"detIssueCloseAlertSettings": "Globale Einstellungen anpassen",
"autoTypeEntryFields": "Feld-Platzhalter",
"autoTypeModifiers": "Hilfstasten",
"autoTypeKeys": "Tasten",
"autoTypeLink": "mehr...",
"autoTypeError": "Auto-Type Fehler",
"autoTypeLink": "mehr …",
"autoTypeError": "Auto-Type-Fehler",
"autoTypeErrorGeneric": "Während der Ausführung von Auto-Type ist ein Fehler aufgetreten: {}",
"autoTypeErrorGlobal": "Um globale Tastenkürzel zu nutzen, wechseln Sie bitte zu der Anwendung, in der Sie das Passwort eingeben wollen.",
"autoTypeErrorNotInstalled": "{} ist nicht installiert",
@@ -343,7 +334,7 @@
"autoTypeMsgNoWindow": "Wir konnten den Titel des aktiven Fensters leider nicht auslesen. Fangen Sie einfach an zu tippen, um nach dem richtigen Eintrag zu suchen.",
"autoTypeMsgMatchedByWindow": "Passwort auswählen für {}",
"autoTypeNoMatches": "keine passenden Einträge",
"autoTypeSelectionHint": "Die Auto-Type Abfolge eintippen",
"autoTypeSelectionHint": "Die Auto-Type-Sequenz eintippen",
"autoTypeSelectionHintAction": "Nur das Passwort eintippen",
"autoTypeSelectionHintOpt": "Nur den Benutzernamen eintippen",
"autoTypeSelectionHintShift": "Andere Felder",
@@ -361,6 +352,8 @@
"appSaveError": "Fehler beim Speichern",
"appSaveErrorBody": "Automatisches Speichern fehlgeschlagen",
"appSaveErrorBodyMul": "Automatisches Speichern fehlgeschlagen:",
"appSaveErrorExitLoseChanges": "Schließen und alle Änderungen verlieren",
"appSaveErrorExitLoseChangesBody": "Du kannst entweder die App schließen und alle Änderungen verlieren, oder gehe in die Dateieinstellungen und exportiere die Daten",
"appSettingsError": "Fehler beim Laden der App",
"appSettingsErrorBody": "Es gab einen Fehler beim Laden der App-Einstellungen. Bitte überprüfen Sie die App-URL oder kontaktieren Sie Ihren Systemadministrator.",
"appNotSupportedError": "Ihr Browser unterstützt einige wichtige Funktionen nicht, die für diese App benötigt werden.",
@@ -375,12 +368,12 @@
"setGenNewVersion": "Eine neue Version der App ist verfügbar und wurde heruntergeladen",
"setGenReleaseNotes": "Versionshinweise anzeigen",
"setGenReloadToUpdate": "Seite aktualisieren, um das Update durchzuführen",
"setGenUpdateManual": "Eine neue Version ist verfügbar. Sie wird nach Updates suchen und sie automatisch installieren, ein automatisches Update von Ihrer Version ist aber nicht möglich.",
"setGenUpdateManual": "Eine neue Version ist verfügbar. Sie wird nach Updates suchen und diese automatisch installieren, ein automatisches Update Ihrer Version ist aber nicht möglich.",
"setGenDownloadUpdate": "Update herunterladen",
"setGenUpdateAuto": "Automatisch herunterladen und installieren",
"setGenUpdateCheck": "Auf Updates überprüfen aber nicht installieren",
"setGenUpdateCheck": "Auf Updates überprüfen, aber nicht installieren",
"setGenNoUpdate": "Nie nach Updates suchen",
"setGenUpdateChecking": "Suche nach Updates",
"setGenUpdateChecking": "Updates werden gesucht",
"setGenCheckUpdate": "Nach Updates suchen",
"setGenErrorChecking": "Updatesuche fehlgeschlagen",
"setGenLastCheckSuccess": "Letzte erfolgreiche Überprüfung am {}",
@@ -388,37 +381,49 @@
"setGenCheckedAt": "Überprüft am",
"setGenLatestVer": "Sie haben die aktuellste Version",
"setGenNewVer": "Neue Version {} verfügbar, veröffentlicht am",
"setGenDownloadingUpdate": "Update wird heruntergeladen...",
"setGenExtractingUpdate": "Update wird entpackt...",
"setGenDownloadingUpdate": "Update wird heruntergeladen …",
"setGenExtractingUpdate": "Update wird entpackt …",
"setGenCheckErr": "Beim Herunterladen der neuen Version ist ein Fehler aufgetreten",
"setGenNeverChecked": "Nie nach Updates gesucht",
"setGenRestartToUpdate": "App neu starten, um das Update durchzuführen",
"setGenDownloadAndRestart": "Update herunterladen und App neu starten",
"setGenAppearance": "Oberfläche",
"setGenTheme": "Theme",
"setGenThemeFb": "Flat blue",
"setGenThemeDb": "Dark brown",
"setGenThemeWh": "White",
"setGenThemeDefault": "Standard",
"setGenThemeDark": "Dunkel",
"setGenThemeLight": "Hell",
"setGenThemeBlue": "Flach blau",
"setGenThemeFb": "Mattblau",
"setGenThemeBl": "Hell blau",
"setGenThemeBrown": "Brownie",
"setGenThemeDb": "Dunkelbraun",
"setGenThemeLb": "Hell braun",
"setGenThemeTerminal": "Terminal",
"setGenThemeTe": "Terminal",
"setGenThemeHc": "High contrast",
"setGenThemeSd": "Solarized dark",
"setGenThemeSl": "Solarized light",
"setGenThemeMacDark": "macOS Dunkel",
"setGenThemeLt": "Hell Terminal",
"setGenThemeHighContrast": "Hoher Kontrast",
"setGenThemeHc": "Hoher Kontrast",
"setGenThemeDc": "Dunkel Kontrast",
"setGenThemeSol": "Solarized",
"setGenThemeSd": "Solarisiert dunkel",
"setGenThemeSl": "Solarisiert hell",
"setGenMoreThemes": "Mehr Themes",
"setGenAutoSwitchTheme": "Automatisch zwischen hellem und dunklem Thema umschalten, sofern möglich",
"setGenLocale": "Sprache",
"setGenLocOther": "Andere Sprachen sind als Plugins verfügbar.",
"setGenFontSize": "Schriftgröße",
"setGenFontSizeNormal": "Standard",
"setGenFontSizeLarge": "Groß",
"setGenFontSizeLargest": "Größte",
"setGenTitlebarStyle": "Fenster-Stil (erfordert Neustart des Programms)",
"setGenTitlebarStyle": "Fensterstil (erfordert Neustart des Programms)",
"setGenTitlebarStyleDefault": "Standard",
"setGenTitlebarStyleHidden": "Eigener Titel",
"setGenTitlebarStyleHiddenInset": "Eigener Titel, verschiebbares Fenster",
"setGenShowSubgroups": "Einträge aus allen Untergruppen anzeigen",
"setGenTableView": "Einträge in Tabellenansicht anzeigen",
"setGenColorfulIcons": "Eigene Icons in der Listenansicht farbig anzeigen",
"setGenColorfulIcons": "Eigene Symbole in der Listenansicht farbig anzeigen",
"setGenUseMarkdown": "Markdown in Notizen",
"setGenUseGroupIconForEntries": "Für neue Einträge automatisch das Gruppen-Icon verwenden",
"setGenUseGroupIconForEntries": "Für neue Einträge automatisch das Gruppensymbol verwenden",
"setGenDirectAutotype": "Wenn nur ein passender Eintrag gefunden wird, diesen automatisch für Auto-Type auswählen",
"setGenFunction": "Arbeitsweise",
"setGenAutoSyncOnClose": "Beim Schließen speichern und synchronisieren",
@@ -446,22 +451,38 @@
"setGenLockAutoType": "Bei Auto-Type",
"setGenLockOrSleep": "Bei Aktivierung von Bildschirmsperre oder Ruhezustand",
"setGenStorage": "Synchronisierung",
"setGenDisableOfflineStorage": "Geladene Dateien nicht in Offline-Speicher zwischenspeichern",
"setGenStorageLogout": "Abmelden",
"setGenShowAdvanced": "Erweiterte Einstellungen anzeigen",
"setGenDevTools": "Entwicklerwerkzeuge anzeigen",
"setGenTryBeta": "Beta-Version bis zum Schließen der App ausprobieren",
"setGenTryBeta": "Betaversion bis zum Schließen der App ausprobieren",
"setGenTryBetaWarning": "Ungespeicherte Dateien",
"setGenTryBetaWarningBody": "Bitte speichern Sie alle Dateien und Klicken Sie erneut auf diesen Button",
"setGenTryBetaWarningBody": "Bitte speichern Sie alle Dateien und klicken erneut auf diese Schaltfläche",
"setGenShowAppLogs": "App-Logs anzeigen",
"setGenReloadApp": "App neu laden",
"setGenFieldLabelDblClickAutoType": "Auto-Type durch anklicken von Beschriftungen aktivieren",
"setGenFieldLabelDblClickAutoType": "Auto-Type durch Anklicken von Beschriftungen aktivieren",
"setGenTouchId": "Fingerabdruck",
"setGenTouchIdDisabled": "Fingerabdruck nicht benutzen",
"setGenTouchIdMemory": "Nur mit Fingerabdruck entsperren, wenn KeeWeb aktiv ist",
"setGenTouchIdFile": "Benutze immer den Fingerabdruck anstatt das Master-Passwort",
"setGenTouchIdPass": "Benötige Master-Passwort nach",
"setGenAudit": "Audit",
"setGenAuditPasswords": "Warnungen für schwache Passwörter anzeigen",
"setGenAuditPasswordEntropy": "Passwortlänge und Zufälligkeit prüfen",
"setGenExcludePinsFromAudit": "Kurze Pin-Codes niemals prüfen (z. B. 12345)",
"setGenHelpHIBP": "KeeWeb kann überprüfen ob deine Passwörter bei einem früheren Datenleck veröffentlicht wurden. Deine Passwörter können nicht beim übertragen ausgelesen werden, aber die Anzahl der überprüften Passwörter kann möglicherweise entdeckt werden. {} kannst du mehr über den Datenschutz beim Nutzen dieses Dienstes erfahren. Wenn diese Option aktiviert ist überprüft KeeWeb deine Passwörter automatisch.",
"setGenHelpHIBPLink": "hier",
"setGenAuditPasswordAge": "Alte Passwörter",
"setGenAuditPasswordAgeOff": "Zeige keine Warnungen über alte Passwörter",
"setGenAuditPasswordAgeOneYear": "Zeige Warnungen für Passwörter die über ein Jahr alt sind",
"setGenAuditPasswordAgeYears": "Zeige Warnungen für Passwörter die über {} Jahre alt sind",
"setFilePath": "Dateipfad",
"setFileStorage": "Diese Datei wird von {} geladen.",
"setFileIntl": "Diese Datei ist im internen App-Speicher abgelegt",
"setFileLocalHint": "Sie wollen nahtlos mit lokalen Dateien arbeiten?",
"setFileDownloadApp": "Laden Sie die Desktop-App herunter",
"setFileSave": "Speichern",
"setFileSaveTo": "Speichern unter...",
"setFileSaveTo": "Speichern unter …",
"setFileClose": "Sperren",
"setFileSync": "Synchronisierung",
"setFileSyncVerb": "Synchronisieren",
@@ -475,7 +496,7 @@
"setFileConfirmPass": "Masterpasswort bestätigen",
"setFilePassChange": "Um das Passwort zu ändern, geben Sie es im Bestätigungsfeld an",
"setFilePassChanged": "Das Passwort wurde geändert. Lassen Sie das Feld leer, um das bisherige Passwort zu behalten.",
"setFilePassNotMatch": "Die Passwörter stimmen nicht überein, bitte geben Sie sie erneut ein",
"setFilePassNotMatch": "Die Passwörter stimmen nicht überein, bitte geben Sie diese erneut ein",
"setFileKeyFile": "Schlüsseldatei",
"setFileSelKeyFile": "Schlüsseldatei auswählen",
"setFileNames": "Namen",
@@ -492,7 +513,7 @@
"setFileBackupPath": "Sicherungspfad",
"setFileBackupTime": "Sicherungen anlegen",
"setFileBackupNow": "Jetzt sichern",
"setFileBackupNowWorking": "Sichern...",
"setFileBackupNowWorking": "Ausführung läuft …",
"setFileBackupError": "Sicherungsfehler",
"setFileBackupErrorDescription": "Fehler beim Schreiben der Sicherungsdatei",
"setFileBackupErrorIsDir": "Der Sicherungspfad ist ungültig",
@@ -512,9 +533,9 @@
"setFileUseGenKeyFile": "Generierte Schlüsseldatei benutzen",
"setFileUseOldKeyFile": "Alte Schlüsseldatei benutzen",
"setFileGenKeyFile": "Neue Schlüsseldatei generieren",
"setFileDontUseKeyFile": "Keine Schlüsseldatei benutzen",
"setFileDontUseKeyFile": "Keine Schlüsseldatei verwenden",
"setFileEmptyPass": "Leeres Passwort",
"setFileEmptyPassBody": "Eine Datenbank ohne Passwort zu speichern macht sie vollkommen ungeschützt. Möchten Sie sie trotzdem speichern?",
"setFileEmptyPassBody": "Eine Datenbank ohne Passwort zu speichern, macht diese vollkommen ungeschützt. Möchten Sie dies trotzdem tun?",
"setFileSaveError": "Fehler beim Speichern",
"setFileSaveErrorBody": "Schreibfehler beim Speichern",
"setFileAlreadyExists": "Existiert bereits",
@@ -529,14 +550,16 @@
"setFileDeviceIntro": "Einmal-Codes aus diesem {} werden in der App angezeigt.",
"setFileDeviceSettings": "Einstellungen",
"setFileYubiKey": "YubiKey",
"setFileDontUseYubiKey": "Keinen YubiKey benutzen",
"setFileDontUseYubiKey": "Keinen YubiKey verwenden",
"setFileRefreshYubiKeyList": "Liste aktualisieren",
"setFileYubiKeyHeader": "YubiKey",
"setFileYubiKeyBody": "Es ist gefährlich, einen YubiKey als Teil des Hauptschlüssels zu verwenden. Falls etwas schief geht, besteht möglicherweise kein Zugriff mehr auf die Passwörter. Haben Sie ein Backup Ihrer Datei erstellt, bevor diese Einstellung geändert wurde?",
"setFileYubiKeyErrorEmptyMac": "Scheinbar wurde ein YubiKey angeschlossen, er ist aber nicht sichtbar. KeeWeb muss in den Sicherheitseinstellungen im Abschnitt Input-Monitoring hinzugefügt werden, um auf YubiKeys zugreifen zu können.",
"setShTitle": "Tastenkürzel",
"setShShowAll": "alle Einträge anzeigen",
"setShColors": "mit Farben markierte Einträge anzeigen",
"setShTrash": "Papierkorb öffnen",
"setShFind": "Suchfeld aktivieren; oder: einfach anfangen, den Suchbegriff zu tippen",
"setShFind": "Suchen oder einfach beginnen zu tippen",
"setShClearSearch": "Suchfeld leeren",
"setShCopyPass": "Passwort bzw. ausgewähltes Feld kopieren",
"setShCopyUser": "Benutzername kopieren",
@@ -545,8 +568,8 @@
"setShPrev": "zum vorherigen Eintrag gehen",
"setShNext": "zum nächsten Eintrag gehen",
"setShCreateEntry": "Neuer Eintrag",
"setShOpen": "öffnen / neu",
"setShSave": "alle Dateien speichern",
"setShOpen": "Öffnen / Neu",
"setShSave": "Alle Dateien speichern",
"setShGen": "Passwort generieren",
"setShSet": "App-Einstellungen",
"setShCopyPassGlobal": "Passwort kopieren",
@@ -556,12 +579,12 @@
"setShRestoreApp": "{} öffnen",
"setShGlobal": "Diese systemweiten Tastenkürzel funktionieren auch, wenn die App im Hintergrund läuft. Klicken Sie auf einen Eintrag, um ein eigenes Kürzel festzulegen.",
"setShLock": "Datenbank sperren",
"setShEdit": "Drücken Sie eine Tastenkombination, um diese als Kürzel zu setzen",
"setShEdit": "Drücken Sie eine Tastenkombination, um diese als Kürzel zu festzulegen",
"setPlInstallTitle": "Neue Plugins installieren",
"setPlInstallDesc": "KeeWeb-Plugins fügen Funktionen, Themes und Sprachen zu KeeWeb hinzu. Plugins werden mit denselben Rechten ausgeführt wie KeeWeb selbst, sie können auf all Ihre Passwörter zugreifen oder diese ändern. Installieren Sie niemals Plugins, denen Sie nicht vertrauen.",
"setPlInstallLabel": "Plugin-URL",
"setPlInstallBtn": "Installieren",
"setPlInstallBtnProgress": "Installiere",
"setPlInstallBtnProgress": "Installation läuft",
"setPlUninstallBtn": "Deinstallieren",
"setPlDisableBtn": "Deaktivieren",
"setPlEnableBtn": "Aktivieren",
@@ -575,7 +598,7 @@
"setPlLoadTime": "geladen in {}",
"setPlLastUpdate": "Letzte Prüfung auf Updates",
"setPlLoadError": "Fehler beim Laden des Plugins",
"setPlGalleryLoading": "Plugins werden geladen, bitte warten Sie einen Moment",
"setPlGalleryLoading": "Plugins werden geladen, bitte einen Moment warten",
"setPlGalleryLoadError": "Fehler beim Laden der Plugins",
"setPlInstallUrlTitle": "Plugin von einer URL hinzufügen",
"setPlInstallUrlDesc": "Wenn das Plugin nicht in der Galerie verfügbar ist, können Sie es manuell von einer URL installieren",
@@ -587,19 +610,35 @@
"setPlTranslateLink": "übersetzen Sie die App in Ihre Sprache",
"setPlAutoUpdate": "Automatisch aktualisieren",
"setPlLoadGallery": "Plugin-Galerie laden",
"setDevicesTitle": "Geräte",
"setDevicesEnableUsb": "Interaktion mit USB-Geräten aktivieren",
"setDevicesYubiKeyIntro": "Es wird empfehlen {} zu lesen, bevor ein YubiKey verwendet wird.",
"setDevicesYubiKeyIntroLink": "dieses Dokument",
"setDevicesYubiKeyToolsDesc": "Um YubiKey in diesem Modus verwenden zu können, müssen Sie vorher das Tool {} installieren.",
"setDevicesYubiKeyToolsDesc2": "{}, um mehr Informationen über dieses Tool zu erhalten.",
"setDevicesYubiKeyToolsDescLink": "Hier klicken",
"setDevicesYubiKeyToolsStatusChecking": "Prüfung, ob {} installiert ist",
"setDevicesYubiKeyToolsStatusOk": "{} ist installiert",
"setDevicesYubiKeyToolsStatusError": "{} ist nicht installiert oder funktioniert nicht korrekt",
"setDevicesYubiKeyOtpTitle": "Einmal-Codes",
"setDevicesYubiKeyOtpDesc": "Ein YubiKey kann genutzt werden, um Einmal-Codes für verschiedene Dienste zu erzeugen.",
"setDevicesYubiKeyOtpShowIcon": "Das Symbol auf der Startseite anzeigen",
"setDevicesYubiKeyOtpAutoOpen": "Einmal-Codes automatisch laden, wenn Dateien geöffnet sind",
"setDevicesYubiKeyOtpMatchEntries": "Passende Einmal-Codes in den Einträgen anzeigen",
"setDevicesYubiKeyChalRespTitle": "Challenge-Response",
"setDevicesYubiKeyChalRespDesc": "Es ist auch möglich, einen YubiKey im Challenge-Response-Modus zu verwenden, um einen Teil des Private Schlüssels, der zum Verschlüsseln der Dateien verwendet wird, auf dem YubiKey zu speichern.",
"setDevicesYubiKeyChalRespShow": "Optionen zum Verwenden eines YubiKeys beim Öffnen von Dateien anzeigen",
"setDevicesYubiKeyStuckWorkaround": "YubiKey entfernen und neu verbinden, falls der Ladeprozess hängt",
"setDevicesYubiKeyRememberChalResp": "Challenge-Response-Einstellungen merken, während die App geöffnet ist",
"setAboutTitle": "Über",
"setAboutBuilt": "Diese App wurde mit den folgenden Werkzeugen erstellt",
"setAboutBuilt": "Diese App wurde mit folgenden Werkzeugen erstellt",
"setAboutLic": "Lizenz",
"setAboutLicComment": "Die App selbst und alle enthaltenen Komponenten, die nicht Public Domain sind, stehen unter der MIT-Lizenz.",
"setAboutFirst": "Dies ist eine Open-Source App, erstellt von {}",
"setAboutSecond": "und lizenziert unter {}.",
"setAboutSource": "Quellcode und Tickets befinden sich auf {}.",
"setHelpFormat": "Dateiformat",
"setHelpFormatBody": "Dies ist eine Portierung der {} App, programmiert mit Webtechnologien. Sie ist kompatibel mit Dateien im KeePass-Format (kdbx). Sie können solche Dateien (Passwort-Datenbanken) entweder mit KeePass oder mit dieser App erstellen. Das Dateiformat ist zu 100% kompatibel und sollte in beiden Anwendungen nutzbar sein.",
"setHelpFormatBody": "Dies ist eine Portierung der App {}, programmiert mit Webtechnologien. Sie ist kompatibel mit Dateien im KeePass-Format (kdbx). Sie können solche Dateien (Passwort-Datenbanken) entweder mit KeePass oder mit dieser App erstellen. Das Dateiformat ist zu 100 % kompatibel und sollte in beiden Anwendungen nutzbar sein.",
"setHelpProblems": "Probleme?",
"setHelpProblems1": "Falls etwas schief geht, {}",
"setHelpProblems2": "oder {}",
@@ -612,14 +651,15 @@
"setHelpUpdates": "Neuigkeiten",
"setHelpTwitter": "Twitter",
"dropboxSetupDesc": "Etwas Konfiguration ist notwendig, um Dropbox in einer selbst gehosteten Web-App zu verwenden. Bitte erstellen Sie eine eigene Dropbox-App und tragen Sie ihren App-Schlüssel unten ein.",
"dropboxAppKey": "Dropbox App-Schlüssel",
"dropboxAppKey": "Dropbox-App-Schlüssel",
"dropboxAppKeyDesc": "Kopieren Sie den Schlüssel aus Ihrer Dropbox-App (Entwicklereinstellungen)",
"dropboxAppSecret": "Dropbox App Secret",
"dropboxAppSecretDesc": "Das App Secret ist neben dem App Key zu finden",
"dropboxAppKeyHint": "Ihr App-Schlüssel",
"dropboxAppSecret": "Dropbox-App-Secret",
"dropboxAppSecretDesc": "Das App-Secret ist neben dem App-Schlüssel zu finden",
"dropboxFolder": "App-Ordner",
"dropboxFolderDesc": "Wenn Ihre App mit der gesamten Dropbox verlinkt ist (anstatt mit einem bestimmten Ordner), geben Sie hier den Pfad zum Ordner mit Ihren KDBX-Dateien an.",
"dropboxFolderSettingsDesc": "Wählen Sie einen Ordner in Ihrer Dropbox, in dem die Dateien gespeichert werden (standardmäßig das Stammverzeichnis)",
"dropboxFolderPlaceholder": "Standard-Ordner",
"dropboxFolderPlaceholder": "Standardordner",
"dropboxLink": "App verlinken mit",
"dropboxLinkApp": "App-Ordner (Apps/KeeWeb)",
"dropboxLinkFull": "Gesamte Dropbox oder beliebiger Ordner",
@@ -628,10 +668,10 @@
"webdavSaveMethod": "Speichermethode",
"webdavSaveMove": "Eine temporäre Datei hochladen und verschieben",
"webdavSavePut": "kdbx-Datei mittels PUT überschreiben",
"launcherSave": "Passwort-Datenbank speichern",
"launcherSave": "Passwortdatenbank speichern",
"launcherFileFilter": "KeePass-Dateien",
"authPopupRequired": "Pop-Ups blockiert",
"authPopupRequiredBody": "Bitte erlauben Sie Pop-Ups in Ihrem Browser.",
"authPopupRequired": "Pop-ups blockiert",
"authPopupRequiredBody": "Bitte erlauben Sie Pop-ups in Ihrem Browser.",
"exportFileInfo": "Dateiinformationen",
"exportHtmlName": "Name",
"exportHtmlDate": "Exportdatum",
@@ -641,5 +681,15 @@
"importCsvRun": "Importieren",
"importIgnoreField": "Ignorieren",
"importTo": "Einträge werden importiert nach:",
"importNewFile": "Neue Datei"
"importNewFile": "Neue Datei",
"yubiKeyStuckError": "Der YubiKey scheint nicht mehr zu funktionieren. Auto-Repair kann in den App-Einstellungen aktiviert werden.",
"yubiKeyNoKeyHeader": "YubiKey benötigt",
"yubiKeyNoKeyBody": "Bitte den YubiKey mit der Seriennummer {} anschließen",
"yubiKeySlot": "Anschluss",
"yubiKeyTouchRequestedHeader": "Bitte den YubiKey berühren",
"yubiKeyTouchRequestedBody": "Bitte den YubiKey mit der Seriennummer {} berühren",
"yubiKeyDisabledErrorHeader": "USB ist deaktiviert",
"yubiKeyDisabledErrorBody": "YubiKey ist zum Öffnen dieser Datei erforderlich. Bitte USB-Geräte in den Einstellungen aktivieren.",
"yubiKeyErrorWithCode": "YubiKey-Fehler, Code {}.",
"bioOpenAuthPrompt": "„{}“ öffnen"
}
+92 -62
View File
@@ -1,50 +1,4 @@
{
"months": [
"Janvier",
"Février",
"Mars",
"Avril",
"Mai",
"Juin",
"Juillet",
"Août",
"Septembre",
"Octobre",
"Novembre",
"Décembre"
],
"monthsShort": [
"Janv",
"Févr",
"Mars",
"Avr",
"Mai",
"Juin",
"Juil",
"Août",
"Sept",
"Oct",
"Nov",
"Déc"
],
"weekdays": [
"Dimanche",
"Lundi",
"Mardi",
"Mercredi",
"Jeudi",
"Vendredi",
"Samedi"
],
"weekdaysShort": [
"Dim",
"Lun",
"Mar",
"Mer",
"Jeu",
"Ven",
"Sam"
],
"retToApp": "retour vers l'appli",
"name": "nom",
"icon": "icône",
@@ -72,6 +26,15 @@
"shiftKey": "shift",
"altKey": "alt",
"error": "erreur",
"oneMinute": "une minute",
"minutes": "{} minutes",
"oneHour": "une heure",
"hours": "{} heures",
"oneDay": "un jour",
"days": "{} jours",
"oneWeek": "une semaine",
"oneMonth": "un mois",
"oneYear": "une année",
"cache": "cache",
"file": "fichier",
"device": "appareil",
@@ -88,11 +51,27 @@
"menuAlertNoTags": "Aucun tag",
"menuAlertNoTagsBody": "Vous pouvez ajouter de nouveaux tags en éditant les champs, dans la section Tags.",
"menuEmptyTrash": "Vider la Corbeille",
"menuEmptyTrashAlert": "Vider la Corbeille?",
"menuEmptyTrashAlert": "Vider la Corbeille ?",
"menuEmptyTrashAlertBody": "Vous ne pourrez plus récupérer ces éléments",
"menuItemCollapsed": "Double-cliquez pour déplier",
"menuRestoreApp": "Ouvrir {}",
"menuQuitApp": "Quitter {}",
"sysMenuAboutKeeWeb": "A propos {}",
"sysMenuServices": "Services",
"sysMenuHide": "Cacher {}",
"sysMenuHideOthers": "Cacher autres",
"sysMenuUnhide": "Montrer tous",
"sysMenuQuit": "Quitter {}",
"sysMenuEdit": "Editer",
"sysMenuUndo": "Annuler",
"sysMenuRedo": "Répéter",
"sysMenuCut": "Couper",
"sysMenuCopy": "Copier",
"sysMenuPaste": "Coller",
"sysMenuSelectAll": "Tout sélectionner",
"sysMenuWindow": "Fenêtre",
"sysMenuMinimize": "Réduire",
"sysMenuClose": "Fermer fenêtre",
"alertYes": "Oui",
"alertNo": "Non",
"alertOk": "OK",
@@ -109,7 +88,7 @@
"genLen": "Longueur",
"genNewPass": "Nouveau mot de passe",
"genPresetDefault": "réglages par défaut",
"genPresetDerived": "identique à l'ancien mot de passe",
"genPresetDerived": "similaire à l'ancien mot de passe",
"genPresetPronounceable": "prononçable",
"genPresetMed": "longueur moyenne",
"genPresetLong": "long",
@@ -133,7 +112,7 @@
"tagExists": "Ce tag existe déjà",
"tagExistsBody": "Un tag existe déjà avec ce nom. Merci de choisir un autre nom.",
"tagBadName": "Nom invalide",
"tagBadNameBody": "Un nom de tag ne peut pas contenir les caractères {}. Merci de les supprimer.",
"tagBadNameBody": "Les noms de Tag ne peuvent contenir les caractères {}. Veuillez les supprimer.",
"genPsTitle": "Préréglages du Générateur",
"genPsCreate": "Nouveau préréglage",
"genPsDelete": "Supprimer préréglage",
@@ -168,7 +147,7 @@
"listNoWebsite": "aucun site web",
"listNoUser": "aucun utilisateur",
"listNoAttachments": "aucune pièce-jointe",
"listAddTemplateHeader": "Templates",
"listAddTemplateHeader": "Modèles",
"listAddTemplateBody1": "Les modèles (templates) vous permettent de créer des nouvelles entrées en un clic. Ajouter quelque chose à l'entrée du modèle et ensuite cliquer de nouveau sur {} pour utiliser ce modèle.",
"listAddTemplateBody2": "Vous pouvez toujours retrouver vos modèles dans le groupe {}.",
"searchAddNew": "Ajouter Nouveau",
@@ -230,6 +209,7 @@
"openConfigErrorNotFound": "Aucun fichier trouvé",
"openError": "Erreur",
"openErrorDescription": "Une erreur est survenue à l'ouverture du fichier",
"openErrorDescriptionMaybeTouchIdChanged": "L'erreur ressemble à ce qu'y arrive habituellement quand la configuration de Touch ID a été changée. Par exemple, vous avez ajouté ou supprimé un doigt supplémentaire. Si c'est le cas, allez dans Paramètres, désactiver Touch ID et activer le de nouveau.",
"openErrorFileNotFound": "Fichier non trouvé",
"openListErrorBody": "Erreur au chargement de la liste des fichiers",
"openShowAllFiles": "Tous les fichiers",
@@ -283,7 +263,7 @@
"detDelToTrashBody": "L'entrée sera déplacée dans la corbeille.",
"detFieldCopied": "Copié",
"detFieldCopiedTime": "Copié pendant {} secondes",
"detCopyHint": "Vous pouvez copier la valeur du champ en cliquant sur son titre",
"detCopyHint": "Vous pouvez copier la valeur du champ en cliquant sur ton titre",
"detMore": "plus",
"detClickToAddField": "cliquez pour ajouter un nouveau champ",
"detMenuAddNewField": "Ajouter nouveau champ",
@@ -322,7 +302,7 @@
"detOtpQrError": "Erreur lecture du QR code",
"detOtpQrErrorBody": "Désolé, le QR code n'a pas pu être lu, essayez à nouveau ou contacter les auteurs de l'application avec le détail de l'erreur.",
"detOtpQrWrong": "QR code incorrect",
"detOtpQrWrongBody": "Votre QR code a été scanné avec succès mais il ne contient pas d'information pour un mot de passe à usage unique.",
"detOtpQrWrongBody": "Votre code QR code a été scanné avec succès mais il ne contient pas d'information pour un code à usage unique.",
"detOtpField": "Code à usage unique",
"detOtpClickToTouch": "Cliquer pour générer",
"detOtpGenerating": "Création...",
@@ -332,13 +312,24 @@
"detRevealField": "Révéler",
"detHideField": "Cacher",
"detAutoTypeField": "Saisie auto",
"detIssuesHideTooltip": "Cacher cet avertissement",
"detIssueWeakPassword": "Ce mot de passe est faible, nous vous recommandons de le changer",
"detIssuePoorPassword": "Ce mot de passe est très faible, nous vous recommandons très fort de le changer",
"detIssuePwnedPassword": "Ce mot de passe a été exposé à une faille selon {}, il est recommandé de le changer",
"detIssuePasswordCheckError": "Une erreur est survenue en vérifiant la force du mot de passe en ligne",
"detIssueOldPassword": "Ce mot de passe est vieux",
"detIssueCloseAlertHeader": "Cacher les problèmes de mots de passe",
"detIssueCloseAlertBody": "Il y a plusieurs façon de cacher ce message :",
"detIssueCloseAlertEntry": "Ne plus montrer pour cette entrée",
"detIssueCloseAlertSettings": "Modifier les paramètres globaux",
"autoTypeEntryFields": "Champs",
"autoTypeModifiers": "Touches modificatrices",
"autoTypeKeys": "Clés",
"autoTypeLink": "plus...",
"autoTypeError": "Erreur saisie auto",
"autoTypeErrorGeneric": "Il y a eu une erreur lors de la saisie automatique : {}",
"autoTypeErrorGlobal": "Pour utiliser un raccourci, sélectionner l'application là où vous voulez saisir le mot de passe",
"autoTypeErrorAccessibilityMacOS": "Nous avons essayé d'envoyer les frappes de caractères à cette application mais cela ne semble pas fonctionner parce qu'il manque des permissions. Cliquer ici pour en lire plus.",
"autoTypeErrorGlobal": "Pour utiliser un raccourci global, sélectionner l'application là où vous voulez saisir le mot de passe",
"autoTypeErrorNotInstalled": "{} n'est pas installé",
"autoTypeHeader": "Saisie auto : Selectionner",
"autoTypeMsgNoWindow": "Nous n'avons pas pu obtenir le titre de la fenêtre active, commencez à taper pour rechercher",
@@ -362,6 +353,8 @@
"appSaveError": "Sauver l'erreur",
"appSaveErrorBody": "Échec sauvegarde auto du fichier",
"appSaveErrorBodyMul": "Échec sauvegarde auto des fichiers",
"appSaveErrorExitLoseChanges": "Afsluiten en alle aanpassingen kwijtraken",
"appSaveErrorExitLoseChangesBody": "Je kunt de app afsluiten en alle wijzigingen kwijtraken, of je kunt de bestandsinstellingen gebruiken om gegevens te exporteren",
"appSettingsError": "Erreur lancement appli",
"appSettingsErrorBody": "Il y a eu une erreur lors du chargement des paramètres. Merci de revérifier l'URL de l'application ou contacter votre administrateur.",
"appNotSupportedError": "Votre navigateur n'est pas compatible avec certaines fonctionnalités importantes que nous utilisons.",
@@ -393,18 +386,30 @@
"setGenExtractingUpdate": "Décompression de la mise à jour...",
"setGenCheckErr": "Une erreur est intervenue durant le téléchargement de la mise à jour",
"setGenNeverChecked": "Ne jamais vérifier les mises à jour",
"setGenRestartToUpdate": "Redémarrer pour mettre à jour",
"setGenRestartToUpdate": "Redémarrer KeeWeb pour mettre à jour",
"setGenDownloadAndRestart": "Télécharger la mise à jour et redémarrer",
"setGenAppearance": "Apparence",
"setGenTheme": "Thème",
"setGenThemeFb": "Bleu plat",
"setGenThemeDefault": "Défaut",
"setGenThemeDark": "Sombre",
"setGenThemeLight": "Clair",
"setGenThemeBlue": "Bleu",
"setGenThemeFb": "Bleu foncé",
"setGenThemeBl": "Bleu clair",
"setGenThemeBrown": "Brownie",
"setGenThemeDb": "Marron foncé",
"setGenThemeWh": "Blanc",
"setGenThemeLb": "Brun clair",
"setGenThemeTerminal": "Terminal",
"setGenThemeTe": "Terminal",
"setGenThemeLt": "Terminal clair",
"setGenThemeHighContrast": "Contraste élevé",
"setGenThemeHc": "Contraste élevé",
"setGenThemeDc": "Contraste foncé",
"setGenThemeSol": "Solarisé",
"setGenThemeSd": "Solarized dark",
"setGenThemeSl": "Solarized light",
"setGenThemeMacDark": "Mode sombre de macOS",
"setGenMoreThemes": "Plus de thèmes",
"setGenAutoSwitchTheme": "Basculer automatiquement entre les thèmes clairs et foncés lorsque possible",
"setGenLocale": "Langue",
"setGenLocOther": "d'autres langues sont disponibles en tant que modules",
"setGenFontSize": "Taille de police",
@@ -441,12 +446,15 @@
"setGenClearSeconds": "Dans {} secondes",
"setGenClearMinute": "Dans une minute",
"setGenMinInstead": "Réduire l'application au lieu de fermer",
"setGenMinOnFieldCopy": "Réduire sur copie d'un champ",
"setGenLock": "Verrou Auto",
"setGenLockMinimize": "quand l'appli est en arrière-plan",
"setGenLockCopy": "sur copie de m.d.p.",
"setGenLockAutoType": "sur saisie auto",
"setGenLockOrSleep": "Quand l'ordinateur est verrouillé ou mis en sommeil",
"setGenStorage": "Stockage",
"setGenDisableOfflineStorage": "Ne pas mettre en cache des fichiers d'un stockage offline",
"setGenShortLivedStorageToken": "Utiliser des sessions de courte-durée avec les fournisseurs de stockage dans le cloud",
"setGenStorageLogout": "Déconnexion",
"setGenShowAdvanced": "Afficher les paramètres avancés",
"setGenDevTools": "Afficher les outils de développement",
@@ -456,6 +464,22 @@
"setGenShowAppLogs": "Voir les logs",
"setGenReloadApp": "Recharger l'application",
"setGenFieldLabelDblClickAutoType": "Remplissage auto par double clic sur les noms de champ",
"setGenTouchId": "Touch ID",
"setGenTouchIdDisabled": "Ne pas utiliser Touch ID",
"setGenTouchIdMemory": "Dévérouiller avec Touch ID uniquement quand KeeWeb est lancé",
"setGenTouchIdFile": "Toujours utiliser Touch ID à la place du mot de passe",
"setGenTouchIdPass": "Exige le mot de passe après",
"setGenAudit": "Audit",
"setGenAuditPasswords": "Voir des avertissements sur la force du mot de passe",
"setGenAuditPasswordEntropy": "Vérifier la longueur et l'aléas du mot de passe",
"setGenExcludePinsFromAudit": "Ne jamais vérifier les codes PIN courts, comme 123456",
"setGenCheckPasswordsOnHIBP": "Vérifier les mots de passe en utilisant un service en ligne {}",
"setGenHelpHIBP": "KeeWeb peut vérifier si vos mots de passes ont été précédemment exposés à une faille de sécurité en utilisant un service en ligne. Votre mot de passe ne peut pas être récupéré à partir de données envoyées en ligne, cependant le nombre de mots de passes vérifiés peut être exposé. Plus d'informations sur votre sécurité en utilisant ce service peuvent être trouvées {}. Si cette option est activée, KeeWeb vérifiera automatiquement vos mots de passes là.",
"setGenHelpHIBPLink": "ici",
"setGenAuditPasswordAge": "Vieux mots de passe",
"setGenAuditPasswordAgeOff": "Ne pas afficher d'avertissements à propos des vieux mots de passe",
"setGenAuditPasswordAgeOneYear": "Afficher des avertissements pour les mots de passe plus vieux qu'un an",
"setGenAuditPasswordAgeYears": "Afficher des avertissement pour les mots de passe plus vieux que {} ans",
"setFilePath": "Chemin",
"setFileStorage": "Le fichier est ouvert de {}.",
"setFileIntl": "Le fichier est conservé dans le stockage interne de l'application",
@@ -512,10 +536,10 @@
"setFileUseKeyFile": "Utiliser un fichier clé",
"setFileUseGenKeyFile": "Utiliser le fichier clé généré",
"setFileUseOldKeyFile": "Utiliser l'ancien fichier clé",
"setFileGenKeyFile": "Générer nouveau fichier clé",
"setFileGenKeyFile": "Générer un nouveau fichier clé",
"setFileDontUseKeyFile": "Ne pas utiliser de fichier clé",
"setFileEmptyPass": "Mot de passe vide",
"setFileEmptyPassBody": "Sauvegarder la base de données avec un mode de passe vide la laisse sans aucune protection. Voulez-vous vraiment faire cela ?",
"setFileEmptyPassBody": "Sauvegarder la base de données avec un mot de passe vide la laisse sans aucune protection. Voulez-vous vraiment faire cela ?",
"setFileSaveError": "Erreur sauvegarde",
"setFileSaveErrorBody": "Erreur sauvegarde dans le fichier",
"setFileAlreadyExists": "Existe déjà",
@@ -613,7 +637,7 @@
"setAboutTitle": "À propos",
"setAboutBuilt": "Cette appli est construite avec ces outils géniaux",
"setAboutLic": "Licence",
"setAboutLicComment": "Cette application et tous les composants qu'elle contient qui ne sont pas dans le domaine public sont sous licence MIT",
"setAboutLicComment": "Cette application et tous les composants qu'elle contient qui ne sont pas dans le domaine public sont sous licence MIT, sauf si autrement spécifié",
"setAboutFirst": "Ceci est une application open-source créée par {}",
"setAboutSecond": " et sous licence {}.",
"setAboutSource": "Le code source et les problèmes sont sur {}.",
@@ -647,7 +671,9 @@
"gdriveSharedWithMe": "Partagé avec moi",
"webdavSaveMethod": "Méthode de sauvegarde",
"webdavSaveMove": "Envoyer un fichier temporaire et le déplacer",
"webdavSavePut": "Ecraser le fichier kdbx avec PUT",
"webdavSavePut": "Écraser le fichier kdbx avec PUT",
"webdavNoLastModified": "L'entête HTTP \"Last-Modified\" est absent",
"webdavStatReload": "Toujours recharger le fichier au lieu de se fier à l'entête HTTP \"Last-Modified\"",
"launcherSave": "Sauvegarder base des mots de passe",
"launcherFileFilter": "Fichiers Keepass",
"authPopupRequired": "Les pop-up sont bloquées",
@@ -667,5 +693,9 @@
"yubiKeyNoKeyBody": "Merci d'insérer votre YubiKey avec le numéro de série {}",
"yubiKeySlot": "port",
"yubiKeyTouchRequestedHeader": "Touchez votre YubiKey",
"yubiKeyTouchRequestedBody": "Merci de toucher votre YubiKey avec le numéro de série {}"
"yubiKeyTouchRequestedBody": "Merci de toucher votre YubiKey avec le numéro de série {}",
"yubiKeyDisabledErrorHeader": "L'USB est désactivé",
"yubiKeyDisabledErrorBody": "Yubikey est nécessaire pour ouvrir ce fichier, merci d'activer les appareils USB dans les paramètres",
"yubiKeyErrorWithCode": "Erreur Yubikey code {}.",
"bioOpenAuthPrompt": "ouvrir \"{}\""
}
+276 -42
View File
@@ -4,19 +4,20 @@ import { SearchResultCollection } from 'collections/search-result-collection';
import { FileCollection } from 'collections/file-collection';
import { FileInfoCollection } from 'collections/file-info-collection';
import { RuntimeInfo } from 'const/runtime-info';
import { Launcher } from 'comp/launcher';
import { UsbListener } from 'comp/app/usb-listener';
import { NativeModules } from 'comp/launcher/native-modules';
import { Timeouts } from 'const/timeouts';
import { AppSettingsModel } from 'models/app-settings-model';
import { EntryModel } from 'models/entry-model';
import { FileInfoModel } from 'models/file-info-model';
import { FileModel } from 'models/file-model';
import { GroupModel } from 'models/group-model';
import { YubiKeyOtpModel } from 'models/external/yubikey-otp-model';
import { YubiKeyOtpModel } from 'models/otp-device/yubikey-otp-model';
import { MenuModel } from 'models/menu/menu-model';
import { PluginManager } from 'plugins/plugin-manager';
import { Features } from 'util/features';
import { DateFormat } from 'util/formatting/date-format';
import { DateFormat } from 'comp/i18n/date-format';
import { Launcher } from 'comp/launcher';
import { UrlFormat } from 'util/formatting/url-format';
import { IdGenerator } from 'util/generators/id-generator';
import { Locale } from 'util/locale';
@@ -37,6 +38,10 @@ class AppModel {
isBeta = RuntimeInfo.beta;
advancedSearch = null;
attachedYubiKeysCount = 0;
memoryPasswordStorage = {};
fileUnlockPromise = null;
hardwareDecryptInProgress = false;
mainWindowBlurTimer = null;
constructor() {
Events.on('refresh', this.refresh.bind(this));
@@ -47,6 +52,11 @@ class AppModel {
Events.on('select-entry', this.selectEntry.bind(this));
Events.on('unset-keyfile', this.unsetKeyFile.bind(this));
Events.on('usb-devices-changed', this.usbDevicesChanged.bind(this));
Events.on('main-window-blur', this.mainWindowBlur.bind(this));
Events.on('main-window-focus', this.mainWindowFocus.bind(this));
Events.on('main-window-will-close', this.mainWindowWillClose.bind(this));
Events.on('hardware-decrypt-started', this.hardwareDecryptStarted.bind(this));
Events.on('hardware-decrypt-finished', this.hardwareDecryptFinished.bind(this));
this.appLogger = new Logger('app');
AppModel.instance = this;
@@ -166,10 +176,29 @@ class AppModel {
page: 'file',
file
});
this.refresh();
file.on('reload', this.reloadFile.bind(this));
file.on('change', () => Events.emit('file-changed', file));
file.on('change', () => {
Events.emit('file-changed', file);
});
file.on('ejected', () => this.closeFile(file));
file.on('change:dirty', (file, dirty) => {
if (dirty && this.settings.autoSaveInterval === -1) {
this.syncFile(file);
}
});
Events.emit('file-opened');
if (this.fileUnlockPromise) {
this.appLogger.info('Running pending file unlock operation');
this.fileUnlockPromise.resolve(file);
this.fileUnlockPromise = null;
Events.emit('unlock-message-changed', null);
}
return true;
}
@@ -231,6 +260,9 @@ class AppModel {
}
closeAllFiles() {
if (!this.files.hasOpenFiles()) {
return;
}
for (const file of this.files) {
file.close();
this.fileClosed(file);
@@ -243,6 +275,7 @@ class AppModel {
this.tags.splice(0, this.tags.length);
this.filter = {};
this.menu.select({ item: this.menu.allItemsItem });
Events.emit('all-files-closed');
}
closeFile(file) {
@@ -253,6 +286,7 @@ class AppModel {
this.menu.groupsSection.removeByFile(file);
this.menu.filesSection.removeByFile(file);
this.menu.select({ item: this.menu.allItemsSection.items[0] });
Events.emit('one-file-closed');
}
emptyTrash() {
@@ -294,7 +328,7 @@ class AppModel {
}
getEntries() {
const entries = this.getEntriesByFilter(this.filter);
const entries = this.getEntriesByFilter(this.filter, this.files);
entries.sortEntries(this.sort, this.filter);
if (this.filter.trash) {
this.addTrashGroups(entries);
@@ -302,16 +336,16 @@ class AppModel {
return entries;
}
getEntriesByFilter(filter) {
getEntriesByFilter(filter, files) {
const preparedFilter = this.prepareFilter(filter);
const entries = new SearchResultCollection();
const devicesToMatchOtpEntries = this.files.filter((file) => file.external);
const devicesToMatchOtpEntries = files.filter((file) => file.backend === 'otp-device');
const matchedOtpEntrySet = this.settings.yubiKeyMatchEntries ? new Set() : undefined;
this.files
.filter((file) => !file.external)
files
.filter((file) => file.backend !== 'otp-device')
.forEach((file) => {
file.forEachEntry(preparedFilter, (entry) => {
if (matchedOtpEntrySet) {
@@ -426,7 +460,7 @@ class AppModel {
createNewEntry(args) {
const sel = this.getFirstSelectedGroupForCreation();
if (args && args.template) {
if (args?.template) {
if (sel.file !== args.template.file) {
sel.file = args.template.file;
sel.group = args.template.file.groups[0];
@@ -442,11 +476,21 @@ class AppModel {
}
}
createNewEntryWithFields(group, fields) {
return EntryModel.newEntryWithFields(group, fields);
}
createNewGroup() {
const sel = this.getFirstSelectedGroupForCreation();
return GroupModel.newGroup(sel.group, sel.file);
}
createNewGroupWithName(group, file, name) {
const newGroup = GroupModel.newGroup(group, file);
newGroup.setName(name);
return newGroup;
}
createNewTemplateEntry() {
const file = this.getFirstSelectedGroupForCreation().file;
const group = file.getEntryTemplatesGroup() || file.createEntryTemplatesGroup();
@@ -465,7 +509,7 @@ class AppModel {
}
}
createNewFile(name) {
createNewFile(name, callback) {
if (!name) {
for (let i = 0; ; i++) {
name = Locale.openNewFile + (i || '');
@@ -475,9 +519,10 @@ class AppModel {
}
}
const newFile = new FileModel({ id: IdGenerator.uuid() });
newFile.create(name);
this.addFile(newFile);
return newFile;
newFile.create(name, () => {
this.addFile(newFile);
callback?.(newFile);
});
}
openFile(params, callback) {
@@ -525,7 +570,8 @@ class AppModel {
fileInfo &&
fileInfo.openDate &&
fileInfo.rev === params.rev &&
fileInfo.storage !== 'file'
fileInfo.storage !== 'file' &&
!this.settings.disableOfflineStorage
) {
logger.info('Open file from cache because it is latest');
this.openFileFromCache(
@@ -546,7 +592,12 @@ class AppModel {
},
fileInfo
);
} else if (!fileInfo || !fileInfo.openDate || params.storage === 'file') {
} else if (
!fileInfo ||
!fileInfo.openDate ||
params.storage === 'file' ||
this.settings.disableOfflineStorage
) {
this.openFileFromStorage(params, callback, fileInfo, logger);
} else {
logger.info('Open file from cache, will sync after load', params.storage);
@@ -594,7 +645,7 @@ class AppModel {
logger.info('Load from storage');
storage.load(params.path, params.opts, (err, data, stat) => {
if (err) {
if (fileInfo && fileInfo.openDate) {
if (fileInfo && fileInfo.openDate && !this.settings.disableOfflineStorage) {
logger.info('Open file from cache because of storage load error', err);
this.openFileFromCache(params, callback, fileInfo);
} else {
@@ -618,7 +669,8 @@ class AppModel {
!noCache &&
fileInfo &&
storage.name !== 'file' &&
(err || (stat && stat.rev === cacheRev))
(err || (stat && stat.rev === cacheRev)) &&
!this.settings.disableOfflineStorage
) {
logger.info(
'Open file from cache because ' + (err ? 'stat error' : 'it is latest'),
@@ -645,7 +697,7 @@ class AppModel {
let needLoadKeyFile = false;
if (!params.keyFileData && fileInfo && fileInfo.keyFileName) {
params.keyFileName = fileInfo.keyFileName;
if (this.settings.rememberKeyFiles === 'data') {
if (this.settings.rememberKeyFiles === 'data' && fileInfo.keyFileHash) {
params.keyFileData = FileModel.createKeyFileWithHash(fileInfo.keyFileHash);
} else if (this.settings.rememberKeyFiles === 'path' && fileInfo.keyFilePath) {
params.keyFilePath = fileInfo.keyFilePath;
@@ -663,10 +715,13 @@ class AppModel {
path: params.path,
keyFileName: params.keyFileName,
keyFilePath: params.keyFilePath,
backup: (fileInfo && fileInfo.backup) || null,
fingerprint: (fileInfo && fileInfo.fingerprint) || null,
backup: fileInfo?.backup || null,
chalResp: params.chalResp
});
if (params.encryptedPassword) {
file.encryptedPassword = fileInfo.encryptedPassword;
file.encryptedPasswordDate = fileInfo?.encryptedPasswordDate || new Date();
}
const openComplete = (err) => {
if (err) {
return callback(err);
@@ -685,7 +740,7 @@ class AppModel {
if (fileInfo) {
file.syncDate = fileInfo.syncDate;
}
if (updateCacheOnSuccess) {
if (updateCacheOnSuccess && !this.settings.disableOfflineStorage) {
logger.info('Save loaded file to cache');
Storage.cache.save(file.id, null, params.fileData);
}
@@ -755,7 +810,6 @@ class AppModel {
syncDate: file.syncDate || dt,
openDate: dt,
backup: file.backup,
fingerprint: file.fingerprint,
chalResp: file.chalResp
});
switch (this.settings.rememberKeyFiles) {
@@ -771,6 +825,14 @@ class AppModel {
keyFilePath: file.keyFilePath || null
});
}
if (this.settings.deviceOwnerAuth === 'file' && file.encryptedPassword) {
const maxDate = new Date(file.encryptedPasswordDate);
maxDate.setMinutes(maxDate.getMinutes() + this.settings.deviceOwnerAuthTimeoutMinutes);
if (maxDate > new Date()) {
fileInfo.encryptedPassword = file.encryptedPassword;
fileInfo.encryptedPasswordDate = file.encryptedPasswordDate;
}
}
this.fileInfos.remove(file.id);
this.fileInfos.unshift(fileInfo);
this.fileInfos.save();
@@ -808,14 +870,17 @@ class AppModel {
if (data && backup && backup.enabled && backup.pending) {
this.scheduleBackupFile(file, data);
}
if (params) {
this.saveFileFingerprint(file, params.password);
}
if (this.settings.yubiKeyAutoOpen) {
if (this.attachedYubiKeysCount > 0 && !this.files.some((f) => f.external)) {
if (
this.attachedYubiKeysCount > 0 &&
!this.files.some((f) => f.backend === 'otp-device')
) {
this.tryOpenOtpDeviceInBackground();
}
}
if (this.settings.deviceOwnerAuth) {
this.saveEncryptedPassword(file, params);
}
}
fileClosed(file) {
@@ -858,7 +923,7 @@ class AppModel {
path = Storage[storage].getPathForName(file.name);
}
const optionsForLogging = { ...options };
if (optionsForLogging && optionsForLogging.opts && optionsForLogging.opts.password) {
if (optionsForLogging.opts && optionsForLogging.opts.password) {
optionsForLogging.opts = { ...optionsForLogging.opts };
optionsForLogging.opts.password = '***';
}
@@ -965,6 +1030,10 @@ class AppModel {
logger.info('Updated sync date, saving modified file');
saveToCacheAndStorage();
} else if (file.dirty) {
if (this.settings.disableOfflineStorage) {
logger.info('File is dirty and cache is disabled');
return complete(err);
}
logger.info('Saving not modified dirty file to cache');
Storage.cache.save(fileInfo.id, null, data, (err) => {
if (err) {
@@ -1027,6 +1096,9 @@ class AppModel {
} else if (!file.dirty) {
logger.info('Saving to storage, skip cache because not dirty');
saveToStorage(data);
} else if (this.settings.disableOfflineStorage) {
logger.info('Saving to storage because cache is disabled');
saveToStorage(data);
} else {
logger.info('Saving to cache');
Storage.cache.save(fileInfo.id, null, data, (err) => {
@@ -1050,6 +1122,10 @@ class AppModel {
logger.info('File does not exist in storage, creating');
saveToCacheAndStorage();
} else if (file.dirty) {
if (this.settings.disableOfflineStorage) {
logger.info('Stat error, dirty, cache is disabled', err || 'no error');
return complete(err);
}
logger.info('Stat error, dirty, save to cache', err || 'no error');
file.getData((data, e) => {
if (e) {
@@ -1087,6 +1163,14 @@ class AppModel {
}
}
deleteAllCachedFiles() {
for (const fileInfo of this.fileInfos) {
if (fileInfo.storage && !fileInfo.modified) {
Storage.cache.remove(fileInfo.id);
}
}
}
clearStoredKeyFiles() {
for (const fileInfo of this.fileInfos) {
fileInfo.set({
@@ -1224,18 +1308,6 @@ class AppModel {
}
}
saveFileFingerprint(file, password) {
if (Launcher && Launcher.fingerprints && !file.fingerprint) {
const fileInfo = this.fileInfos.get(file.id);
Launcher.fingerprints.register(file.id, password, (token) => {
if (token) {
fileInfo.fingerprint = token;
this.fileInfos.save();
}
});
}
}
usbDevicesChanged() {
const attachedYubiKeysCount = this.attachedYubiKeysCount;
@@ -1246,7 +1318,9 @@ class AppModel {
}
const isNewYubiKey = UsbListener.attachedYubiKeys > attachedYubiKeysCount;
const hasOpenFiles = this.files.some((file) => file.active && !file.external);
const hasOpenFiles = this.files.some(
(file) => file.active && file.backend !== 'otp-device'
);
if (isNewYubiKey && hasOpenFiles && !this.openingOtpDevice) {
this.tryOpenOtpDeviceInBackground();
@@ -1278,7 +1352,7 @@ class AppModel {
return null;
}
for (const file of this.files) {
if (file.external) {
if (file.backend === 'otp-device') {
const matchingEntry = file.getMatchingEntry(entry);
if (matchingEntry) {
return matchingEntry;
@@ -1286,6 +1360,166 @@ class AppModel {
}
}
}
saveEncryptedPassword(file, params) {
if (!this.settings.deviceOwnerAuth || params.encryptedPassword) {
return;
}
NativeModules.hardwareEncrypt(params.password)
.then((encryptedPassword) => {
encryptedPassword = encryptedPassword.toBase64();
const fileInfo = this.fileInfos.get(file.id);
const encryptedPasswordDate = new Date();
file.encryptedPassword = encryptedPassword;
file.encryptedPasswordDate = encryptedPasswordDate;
if (this.settings.deviceOwnerAuth === 'file') {
fileInfo.encryptedPassword = encryptedPassword;
fileInfo.encryptedPasswordDate = encryptedPasswordDate;
this.fileInfos.save();
} else if (this.settings.deviceOwnerAuth === 'memory') {
this.memoryPasswordStorage[file.id] = {
value: encryptedPassword,
date: encryptedPasswordDate
};
}
})
.catch((e) => {
file.encryptedPassword = null;
file.encryptedPasswordDate = null;
delete this.memoryPasswordStorage[file.id];
this.appLogger.error('Error encrypting password', e);
});
}
getMemoryPassword(fileId) {
return this.memoryPasswordStorage[fileId];
}
checkEncryptedPasswordsStorage() {
if (this.settings.deviceOwnerAuth === 'file') {
let changed = false;
for (const fileInfo of this.fileInfos) {
if (this.memoryPasswordStorage[fileInfo.id]) {
fileInfo.encryptedPassword = this.memoryPasswordStorage[fileInfo.id].value;
fileInfo.encryptedPasswordDate = this.memoryPasswordStorage[fileInfo.id].date;
changed = true;
}
}
if (changed) {
this.fileInfos.save();
}
for (const file of this.files) {
if (this.memoryPasswordStorage[file.id]) {
file.encryptedPassword = this.memoryPasswordStorage[file.id].value;
file.encryptedPasswordDate = this.memoryPasswordStorage[file.id].date;
}
}
} else if (this.settings.deviceOwnerAuth === 'memory') {
let changed = false;
for (const fileInfo of this.fileInfos) {
if (fileInfo.encryptedPassword) {
this.memoryPasswordStorage[fileInfo.id] = {
value: fileInfo.encryptedPassword,
date: fileInfo.encryptedPasswordDate
};
fileInfo.encryptedPassword = null;
fileInfo.encryptedPasswordDate = null;
changed = true;
}
}
if (changed) {
this.fileInfos.save();
}
} else {
let changed = false;
for (const fileInfo of this.fileInfos) {
if (fileInfo.encryptedPassword) {
fileInfo.encryptedPassword = null;
fileInfo.encryptedPasswordDate = null;
changed = true;
}
}
if (changed) {
this.fileInfos.save();
}
for (const file of this.files) {
if (file.encryptedPassword) {
file.encryptedPassword = null;
file.encryptedPasswordDate = null;
}
}
this.memoryPasswordStorage = {};
}
}
unlockAnyFile(unlockRes, timeout) {
this.rejectPendingFileUnlockPromise('Replaced with a new operation');
Events.emit('show-open-view');
return new Promise((resolve, reject) => {
this.fileUnlockPromise = { resolve, reject, unlockRes };
if (timeout) {
const timer = setTimeout(
() => this.rejectPendingFileUnlockPromise('Timeout'),
timeout
);
this.fileUnlockPromise.resolve = (res) => {
clearTimeout(timer);
resolve(res);
};
this.fileUnlockPromise.reject = (err) => {
clearTimeout(timer);
reject(err);
};
}
this.appLogger.info('Pending file unlock operation is set');
Events.emit('unlock-message-changed', unlockRes);
});
}
get unlockMessageRes() {
return this.fileUnlockPromise?.unlockRes;
}
rejectPendingFileUnlockPromise(reason) {
if (this.fileUnlockPromise) {
this.appLogger.info('Cancel pending file unlock operation', reason);
this.fileUnlockPromise.reject(new Error(reason));
this.fileUnlockPromise = null;
Events.emit('unlock-message-changed', null);
}
}
mainWindowBlur() {
if (!this.hardwareDecryptInProgress) {
this.mainWindowBlurTimer = setTimeout(() => {
// macOS emits focus-blur-focus event in a row when triggering auto-type from minimized state
delete this.mainWindowBlurTimer;
this.rejectPendingFileUnlockPromise('Main window blur');
}, Timeouts.AutoTypeWindowFocusAfterBlur);
}
}
mainWindowFocus() {
if (this.mainWindowBlurTimer) {
clearTimeout(this.mainWindowBlurTimer);
this.mainWindowBlurTimer = null;
}
}
mainWindowWillClose() {
this.rejectPendingFileUnlockPromise('Main window will close');
}
hardwareDecryptStarted() {
this.hardwareDecryptInProgress = true;
}
hardwareDecryptFinished() {
this.hardwareDecryptInProgress = false;
if (!Launcher.isAppFocused()) {
this.rejectPendingFileUnlockPromise('App is not focused after hardware decrypt');
}
}
}
export { AppModel };
+9
View File
@@ -21,6 +21,15 @@ class AppSettingsModel extends Model {
if (data.rememberKeyFiles === true) {
data.rememberKeyFiles = 'data';
}
if (data.locale === 'en') {
data.locale = 'en-US';
}
if (data.theme === 'macdark') {
data.theme = 'dark';
}
if (data.theme === 'wh') {
data.theme = 'light';
}
}
save() {

Some files were not shown because too many files have changed in this diff Show More