Compare commits

...
1019 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 819b10e1f6 fixed version
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-09 21:02:08 +02:00
antelle 0c170766e5 Merge branch 'release-1.15' 2020-06-09 20:47:36 +02:00
antelle f7a0025b97 bump version 2020-06-09 20:47:07 +02:00
antelle 22776e9088 fix #1409: auto-type wrong character issues on Linux 2020-06-09 20:39:09 +02:00
antelle d23d393c39 - fixed duplicated YubiKeys displayed in file settings 2020-06-09 19:50:35 +02:00
antelle bbf6e796f8 updated electron version 2020-06-09 19:46:37 +02:00
antelle 1c3c194b3a possibility to debug startup with --start-logging 2020-06-09 19:36:18 +02:00
antelle 0cc633c5c1 added a possibility to log messages on startup 2020-06-09 19:35:17 +02:00
antelle 9a8f1ad508 fix #1530: running from directories with hash symbols 2020-06-09 19:26:59 +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 d7ee936b27 fix #1530: recursive creation of the portable directory 2020-06-08 19:13:34 +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
antelle 5081df4f2f Merge branch 'master' into release-1.15 2020-06-07 12:33:10 +02:00
antelle b91dbdaa8f purging cloudflare cache after publishing a release 2020-06-07 12:32:22 +02:00
antelle c313de9f93 release date
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-07 11:04:57 +02:00
antelle 09b00f3137 Merge branch 'release-1.15' 2020-06-07 11:03:39 +02:00
antelle 8342336e8d bump version 2020-06-07 09:48:23 +02:00
antelle fe245dc95e fix #1528: OTP generation for stored values 2020-06-07 09:47:36 +02:00
antelle 3001afd9db Merge branch 'develop'
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-06 16:50:23 +02:00
antelle 48b5c8d4e7 up deps 2020-06-06 16:48:48 +02:00
antelle 21dc4e758f bump version 2020-06-06 16:47:32 +02:00
antelle 5c5fc0271e languages 2020-06-06 16:43:13 +02:00
antelle 082c6bee93 moved native modules to a native module host 2020-06-06 13:30:35 +02:00
antelle c1d354a6fb added an error when usb is disabled 2020-06-05 20:37:13 +02:00
antelle eab683ec71 removed all mentions of cordova
It never got enough love by contributors and it seems to be abandoned. Better to
implement this API in a separate repo.
2020-06-05 16:45:37 +02:00
antelle cd2b87b317 fixed refreshing the yubikey status 2020-06-05 16:41:54 +02:00
antelle 4abe63c00f fixed ykman path on macos 2020-06-05 16:41:38 +02:00
antelle 7e4e61e005 fixed ykman path on macos 2020-06-04 21:33:42 +02:00
antelle db38e015f8 option to skip code signing on macos 2020-06-04 20:13:15 +02:00
antelle ff20d2a554 up kdbxweb 2020-06-04 18:49:33 +02:00
antelle cb7df205bb up deps 2020-06-03 22:42:39 +02:00
antelle dfe2c42172 utils sh 2020-06-03 21:31:32 +02:00
antelle 4854c0c100 removed an unnecessary task 2020-06-03 21:28:31 +02:00
antelle 33bdf124c2 up languages 2020-06-03 21:27:17 +02:00
antelle 2d752ef144 fix #917: option to install without a desktop icon on Windows 2020-06-03 18:11:38 +02:00
antelle d258bce037 de 2020-06-03 11:31:40 +02:00
antelle 21ff37d82e workflow 2020-06-03 09:58:57 +02:00
antelle 312b08ca99 workflow 2020-06-03 09:58:39 +02:00
antelle a47f0d0ae5 up 2020-06-03 09:51:47 +02:00
antelle c1a6268c2d added some logging 2020-06-03 08:33:26 +02:00
antelle 65b6fe2e2e removed an old task 2020-06-02 22:30:21 +02:00
antelle 0f29e94272 default format for new files is now KDBX4 2020-06-02 21:57:41 +02:00
antelle dd9b6a1943 disabled codeql-analysis 2020-06-02 19:33:06 +02:00
antelle a45bc13248 override language detection 2020-06-02 19:26:59 +02:00
antelle b7b5caac9d trying out codeql-analysis 2020-06-02 19:22:27 +02:00
antelle 0eb5ebab0d wording 2020-06-02 19:14:48 +02:00
antelle f34b1011c8 more contributions 2020-06-02 17:48:09 +02:00
antelle 33049c21e7 contribution links 2020-06-02 17:39:03 +02:00
antelle 6c12822279 contribution links 2020-06-02 17:37:20 +02:00
antelle 8a3ff150a6 added contributions 2020-06-02 17:36:13 +02:00
antelle de4bfdf9e0 reformatted build.yaml 2020-06-02 14:16:06 +02:00
antelle 461a45dc51 packaging native dependencies separately 2020-06-02 13:58:25 +02:00
antelle 2b24d716a9 up deps 2020-06-02 13:00:17 +02:00
antelle 36b6fd80b8 added a comment to the workflow 2020-06-02 12:56:27 +02:00
antelle f8cbf51842 skipping code signing on windows 2020-06-02 12:43:05 +02:00
antelle 17fbf9188d option to skip code signing on Windows 2020-06-02 12:41:30 +02:00
antelle ec5bacd951 updated the workflow 2020-06-02 12:29:55 +02:00
antelle 56c8649040 up yk-api 2020-06-02 12:23:22 +02:00
antelle 435eb5779f fixed some portable installation issues on macos 2020-06-02 10:47:20 +02:00
antelle a83e04fe4d reverted config changes 2020-06-01 23:54:55 +02:00
antelle bdbff334a5 syntax for old node.js 2020-06-01 23:30:33 +02:00
antelle f70eb35f69 syntax for old node.js 2020-06-01 23:27:48 +02:00
antelle f1a5aba485 refactored codesignconfig 2020-06-01 22:54:38 +02:00
antelle f8e454dab3 splitted macos app signing and notarization 2020-06-01 22:51:42 +02:00
antelle 9988454af1 fixed app exit while the app is not ready 2020-06-01 16:59:32 +02:00
antelle e0b65bbebf up modules; prettier 2020-06-01 16:53:51 +02:00
antelle 09fd37820b bumped min electron version 2020-06-01 16:46:21 +02:00
antelle 360194c627 loading native modules from updated desktop archives 2020-06-01 16:45:18 +02:00
antelle 21768b1b59 testing builds 2020-06-01 15:55:52 +02:00
antelle 325d521f49 fix #448: minimized login option support on macOS 2020-06-01 15:02:19 +02:00
antelle 2deb241ff2 fix #830: minimize instead of close option on macOS 2020-06-01 14:42:06 +02:00
antelle a2194435e6 fix #1460: auto-type on double-clicking field labels 2020-06-01 10:25:16 +02:00
antelle 448e173f7e added a warning about Input Monitoring for mac 2020-06-01 07:30:00 +02:00
antelle 4b52280549 fixed sticky tips 2020-05-31 23:31:14 +02:00
antelle 9e09f7b7e7 fixed double yubikey alert when opening files from storages 2020-05-31 22:28:38 +02:00
antelle 83293d909e up 2020-05-31 20:26:48 +02:00
antelle 176553a674 fixed challenge-response calculation with padding 2020-05-31 16:35:36 +02:00
antelle a4b22b6c88 silenced the deprecation warning 2020-05-31 14:25:50 +02:00
antelle fb6bcdf5c9 added a possibility to refresh the list of yubikeys 2020-05-31 14:23:37 +02:00
antelle 776c75d823 grammar 2020-05-31 14:13:32 +02:00
antelle 143f7c6a7a yubikey enrollment 2020-05-31 14:13:08 +02:00
antelle b520c58ef4 cancelChalResp 2020-05-31 13:06:36 +02:00
antelle 3c47adbf32 fixed argon2 in browsers 2020-05-31 13:03:59 +02:00
antelle 835acff637 up 2020-05-31 13:00:35 +02:00
antelle a08e3c7d27 option to remember challenge-response codes 2020-05-31 10:15:27 +02:00
antelle d9258745d0 fixed file saving errors 2020-05-30 23:28:33 +02:00
antelle 70bd4f1c4e fixed main window close/closed events confusion 2020-05-30 17:46:13 +02:00
antelle ab3860cdc9 fixed errors during auto-save 2020-05-30 17:40:18 +02:00
antelle 9185ef9a55 showing main window for yubikey alerts 2020-05-30 17:12:48 +02:00
antelle e6826b176e yubikey ui alerts 2020-05-30 16:02:02 +02:00
antelle f5bb29e61a fixed open args 2020-05-30 14:28:06 +02:00
antelle d0d013e046 clearing cache on file close 2020-05-30 12:45:56 +02:00
antelle 5002c2ce67 better building instructions 2020-05-30 12:06:53 +02:00
antelle 8e3b218a74 readme 2020-05-30 12:02:04 +02:00
antelle 51bd80faf4 fixed saved yubikeys 2020-05-30 10:29:02 +02:00
antelle b4fe0af53a extracted challenge-response calculator 2020-05-30 08:19:47 +02:00
antelle 2ba9a03e2c using yubikeys for opening files 2020-05-30 08:10:19 +02:00
antelle 7394754b6f fixed an error when files were closed during sync 2020-05-30 07:47:52 +02:00
antelle b41b291e60 added desktop module description 2020-05-30 07:39:31 +02:00
antelle 0f582e3248 refreshing the list of yubikeys on the start screen 2020-05-29 23:24:05 +02:00
antelle b90e84be42 loading yubikeys using a native module 2020-05-29 23:15:10 +02:00
antelle ef627c9530 updated keeweb-native-modules 2020-05-29 21:49:33 +02:00
antelle b3f021a577 fixed eslint 2020-05-28 18:34:32 +02:00
antelle 96263c11ec escaping more characters in xml, see #1520 2020-05-28 10:54:52 +02:00
antelle b2a6e1180c reverted renderer process native modules, it crashes 2020-05-28 10:48:09 +02:00
antelle 817b44b152 fixed an error during loading the list of yubikeys 2020-05-26 23:26:26 +02:00
antelle 701bb4caa4 loading compatible modules in renderer process 2020-05-26 23:24:00 +02:00
antelle a1b8682316 moved the stuck yubikey flag to the otp section 2020-05-25 19:40:04 +02:00
antelle 8c386dbe57 challenge-response calculation function 2020-05-24 21:06:53 +02:00
antelle d8e49d993a added a text about used yubikey 2020-05-24 20:31:03 +02:00
antelle 93310eb029 listing YubiKeys on the open view 2020-05-24 20:02:49 +02:00
antelle f215c26a63 removed plugin cache invalidation with timestamps 2020-05-24 19:07:01 +02:00
antelle b5fddeb627 added a missing check 2020-05-24 18:42:14 +02:00
antelle 81d4c5c403 extracted YubiKey module 2020-05-24 18:39:56 +02:00
antelle bf7dec2437 fixed writing unencrypted configs 2020-05-24 13:46:03 +02:00
antelle efa9b5f9a7 fixed the yubikey challenge-response icon on the open screen 2020-05-23 19:26:54 +02:00
antelle 1f862a37e9 fixed yubikey icon on the open screen 2020-05-23 19:07:45 +02:00
antelle e8a491dba5 disabled sandbox mode in package.json/electron 2020-05-23 17:08:55 +02:00
antelle aa899b8096 fix #1409: fixed auto-typing Tab character on Debian 2020-05-23 17:07:59 +02:00
antelle 8de55c450a fixed sticky tooltips in settings 2020-05-22 22:44:50 +02:00
antelle 1fafa68793 restructured settings code a bit 2020-05-22 22:28:12 +02:00
antelle e69219697a serializing settings in renderer 2020-05-22 22:17:36 +02:00
Dimitri Witkowski 08a0f6e587 Merge pull request #1514 from b3nj5m1n/add_check_support_windowid
Added check for system supporting window id
2020-05-22 21:48:06 +02:00
b3nj5m1n 7a26d80255 Removed log from debugging 2020-05-22 21:10:52 +02:00
b3nj4m1n 4171848655 Added check for system supporting window id 2020-05-22 21:05:30 +02:00
antelle a7193fd783 fixed config writing 2020-05-22 20:55:05 +02:00
antelle ccce95d4fe fixed configs for portable installations 2020-05-22 20:54:47 +02:00
antelle b9f37c22d9 config encryption with a key from keychain 2020-05-22 20:46:03 +02:00
Dimitri Witkowski 3b3c95e57c Merge pull request #1513 from b3nj5m1n/fix_app_dock
Added check for platform before calling app.dock
2020-05-22 18:58:50 +02:00
b3nj4m1n fadea36232 Added check for platform before calling app.dock 2020-05-22 18:47:26 +02:00
antelle 99b687244c release notes: fix #1509: fixed auto-type for tiling mode in Linux 2020-05-22 12:34:31 +02:00
antelle 813b26270f extracted a constant 2020-05-22 12:30:01 +02:00
antelle b715507dd0 Merge branch 'b3nj5m1n-autotype-tilingwm' into develop 2020-05-22 12:25:11 +02:00
b3nj4m1n 0cb502ca67 Fixed possible error for undefined window id 2020-05-22 12:10:27 +02:00
antelle e5073eb65f default theme on macOS 2020-05-22 11:44:49 +02:00
antelle c3b55cd7fa better portable app checks 2020-05-22 10:56:08 +02:00
antelle e9ef1bd698 fixed remote events 2020-05-22 10:44:59 +02:00
antelle 045fe99158 made dropbox secret hidden 2020-05-22 10:15:21 +02:00
antelle dad95033c0 added a possibility to clear a storage provider field in app settings 2020-05-22 10:14:43 +02:00
b3nj4m1n 02e4d52db6 windowId gets passed in a different place 2020-05-21 21:36:26 +02:00
b3nj4m1n 1f83f34e55 Addressed requested changes 2020-05-21 19:41:19 +02:00
b3nj4m1n 1ffb45433c Fixed auto-type on tiling wm 2020-05-21 17:10:32 +02:00
antelle b6780dff03 ignored tags for yubikey entries 2020-05-16 10:03:02 +02:00
antelle 5b91dcb272 fix #1440: fixed auto-type for maximized windows 2020-05-15 22:41:39 +02:00
antelle 099f1ca9d0 calling openDevTools from window.webContents 2020-05-15 21:54:34 +02:00
antelle c73f81a80a dev desktop tasks 2020-05-15 21:38:33 +02:00
antelle 177b150c95 Merge branch 'master' into develop
# Conflicts:
#	app/templates/export/entry.hbs
#	release-notes.md
2020-05-15 20:52:16 +02:00
antelle 489eb5598c Merge branch 'release-1.14'
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-05-15 20:37:24 +02:00
antelle ad571c1e27 fix eslint 2020-05-15 20:36:59 +02:00
antelle 9827b871fc Merge branch 'release-1.14' 2020-05-15 20:26:25 +02:00
antelle 004436e868 bump version 2020-05-15 20:25:57 +02:00
antelle 008b615903 improved exporting files with markdown notes 2020-05-15 20:25:22 +02:00
antelle 451b465e4f disabled markdown in export 2020-05-15 20:17:40 +02:00
antelle c91e5de961 readme formatting 2020-05-15 20:14:55 +02:00
antelle 8251423d95 up deps 2020-05-15 20:00:18 +02:00
antelle 65f66ac60c fix #1503: arm64 Windows installer 2020-05-14 22:59:11 +02:00
antelle 94caa1fca3 fix #1503: building windows arm64 2020-05-14 20:52:02 +02:00
antelle f7d1708550 removed pcsclite 2020-05-14 20:47:11 +02:00
antelle b888f9da32 ignored unnecessary native modules 2020-05-14 20:36:14 +02:00
antelle 4d678184f3 option to create a portable installation 2020-05-14 20:11:41 +02:00
antelle a2bf157b01 fixed portable executable dir 2020-05-14 18:55:11 +02:00
antelle 1b279119cb auto-opening yubikeys with other files 2020-05-14 18:31:26 +02:00
antelle 2075e82381 increased the yubikey reconnect timeout 2020-05-13 22:29:12 +02:00
antelle 9f60ca8ece fix #1506: replaced incorrectly added client secret config properties 2020-05-13 22:25:33 +02:00
antelle d6867fb62e added native modules links 2020-05-13 21:25:43 +02:00
antelle 07e918965b fix #620: clearing middle-click clipboard in Linux 2020-05-13 21:20:26 +02:00
antelle 24bcc758e7 release notes 2020-05-13 20:26:25 +02:00
antelle 7ae64b0552 fixed native argon2 2020-05-13 20:25:45 +02:00
antelle 38e46f6d85 building native modules into asar 2020-05-13 20:25:33 +02:00
antelle 530316c25f native modules for all available architectures 2020-05-13 19:15:56 +02:00
antelle cc750df077 moved native modules to the main process 2020-05-13 17:44:33 +02:00
antelle 3c85694fc2 removed temp paths from profile 2020-05-13 17:03:23 +02:00
antelle 955a1346ff removed old cookie handling 2020-05-13 16:37:35 +02:00
antelle 8e4566fe5f moved app options 2020-05-12 23:46:19 +02:00
antelle 20623e4c42 disabled unnecessary caching 2020-05-12 23:10:26 +02:00
antelle cac31d3350 added a yubikey selection icon to the open screen 2020-05-11 19:29:39 +02:00
antelle 746b56ac57 release notes 2020-05-11 19:11:14 +02:00
antelle bc140b3fb5 auto-opening yubikeys 2020-05-11 18:49:21 +02:00
antelle 5d15411d3d fixed sequential otp generation 2020-05-11 17:44:18 +02:00
antelle c6ce694dfe displaying matching otp codes in entries 2020-05-11 17:24:06 +02:00
antelle 16d303d966 fixed password field after idle period 2020-05-10 19:44:29 +02:00
antelle f422dbd3db fixed variable name 2020-05-10 16:48:58 +02:00
antelle 5125a44170 added enter to the default totp command 2020-05-10 11:42:13 +02:00
antelle 6e7f2c570f fixed dev app icon 2020-05-10 09:49:09 +02:00
antelle db31e65cdf fixed entry creation on a yubikey 2020-05-09 23:55:57 +02:00
antelle 3cd9bf6505 html => empty 2020-05-09 20:15:46 +02:00
antelle 3b05f6267e escaping fields autocomplete 2020-05-09 17:00:39 +02:00
antelle 3e5b0bff4f renamed content to contentHtml 2020-05-09 16:39:04 +02:00
antelle c9cb15d7b7 removed shortcuts formatting 2020-05-09 16:35:11 +02:00
antelle 89415c86a4 escaping details shortcuts 2020-05-09 16:18:39 +02:00
antelle 4cd515b19f put export csp back 2020-05-09 16:16:27 +02:00
antelle 730e2c5ba5 removed export csp 2020-05-09 16:14:56 +02:00
antelle 3735a6689c escaping attachments 2020-05-09 16:14:43 +02:00
antelle 15c81d8f35 partials support 2020-05-09 16:14:20 +02:00
antelle 154c780281 disabled markdown in export 2020-05-09 15:25:11 +02:00
antelle ec9422d22d added csp for exported html 2020-05-09 14:39:18 +02:00
antelle 101e8eaa24 fixed sort options 2020-05-09 10:59:23 +02:00
antelle 6a31bba69c using optional chaining 2020-05-09 10:45:12 +02:00
antelle 96ee68dee7 fixed yubikey totp auto-type 2020-05-09 10:37:34 +02:00
antelle 5a94c81b11 fixed yubikey username auto-type 2020-05-09 08:57:57 +02:00
antelle cf8aa6f9a4 copying otp from yubikeys 2020-05-08 23:33:57 +02:00
antelle 20e201795e options for yubikey username field 2020-05-08 23:16:13 +02:00
antelle e95902d2bb wording: one-time codes 2020-05-08 23:15:38 +02:00
antelle 79a3b39248 disabled KEEWEB_HTML_PATH in prod builds 2020-05-08 16:20:23 +02:00
antelle c08a0eb8d1 fixed the details view 2020-05-07 23:10:35 +02:00
antelle a280258d4f cleaned up openProcess 2020-05-07 23:10:23 +02:00
antelle 7985a7a522 added a better message when a yubikey is stuck 2020-05-07 22:07:58 +02:00
antelle 6ba2e16a91 fixed yubikey reconnection 2020-05-07 21:22:34 +02:00
antelle f34ebe2fd3 code cleanup 2020-05-07 19:00:18 +02:00
antelle ab3d4a056f fixed the yubikey workaround 2020-05-07 18:58:17 +02:00
antelle c88a7c000b extracted a method 2020-05-06 23:12:49 +02:00
antelle 53b700aa70 fixed yubikey serial detection 2020-05-06 23:06:22 +02:00
antelle 66c219f6d1 locale 2020-05-06 21:08:37 +02:00
antelle 06dd153955 fixed stuck yubikeys 2020-05-06 21:06:19 +02:00
antelle 88a0e63894 disabled auto-open by default 2020-05-06 19:49:21 +02:00
antelle cac7e53ea8 fixed work with several yubikeys 2020-05-06 19:30:30 +02:00
antelle 3e390388eb removed a workaround for Edge 2020-05-06 18:44:00 +02:00
antelle 2a1b01c0aa displaying last open file after detaching a yubikey 2020-05-06 18:43:04 +02:00
antelle 5a535bb07a Added a yubikey workaround 2020-05-06 18:37:37 +02:00
antelle 235ceae228 restarting the yubikey application if it's stuck 2020-05-05 22:36:23 +02:00
antelle 46710aead4 watching yubikeys 2020-05-05 21:26:41 +02:00
antelle 277d443d4c Merge branch 'usb' into develop 2020-05-05 20:58:20 +02:00
antelle 770c25a273 fixed open files 2020-05-05 20:57:26 +02:00
antelle c935de3ec1 message 2020-05-05 20:51:01 +02:00
antelle 8350123839 devices 2020-05-05 20:50:11 +02:00
antelle 02adb81ae0 settings 2020-05-05 20:47:15 +02:00
antelle cf26427e68 fix otp cancel 2020-05-05 20:14:32 +02:00
antelle 6e357e93ec props 2020-05-05 20:03:38 +02:00
antelle 1c8f3932fb search fix 2020-05-05 18:11:58 +02:00
antelle 75671a0a8a canUseNativeModules 2020-05-05 18:01:16 +02:00
antelle 58296dd896 disabled yubikey on win32 2020-05-05 17:57:01 +02:00
antelle 2fff34dc61 grammar 2020-05-05 17:49:19 +02:00
antelle 851ade7e24 fixed drag 2020-05-05 17:46:07 +02:00
antelle 13585f5317 splitted detail fields 2020-05-05 17:28:20 +02:00
antelle 5d0ab36f2c fixed details 2020-05-05 17:05:20 +02:00
antelle be46cf9a25 lint fix 2020-05-05 16:37:25 +02:00
antelle ffdc438091 removed app-model prepare 2020-05-05 16:34:39 +02:00
antelle 52741cf46a search tests 2020-05-05 16:23:17 +02:00
antelle 9263caed59 search 2020-05-05 15:08:19 +02:00
antelle aa5a9fc6d2 Merge branch 'master' into usb
# Conflicts:
#	app/scripts/app.js
#	package.json
#	release-notes.md
2020-05-04 19:46:07 +02:00
antelle 316518d63b Merge branch 'master' into develop
# Conflicts:
#	package.json
#	release-notes.md
2020-05-04 19:45:03 +02:00
antelle 03c44d76c9 Merge branch 'release-1.14'
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-05-04 19:21:54 +02:00
antelle 346d6cb119 fixed dist html file location 2020-05-04 19:21:29 +02:00
antelle dae3d57d1e Merge branch 'release-1.14' 2020-05-04 19:12:47 +02:00
antelle aa65cef103 bump version 2020-05-04 19:12:08 +02:00
antelle e19c923538 up deps 2020-05-04 19:11:20 +02:00
antelle ec292705b3 distinct redirect URIs for storage providers 2020-05-04 19:06:05 +02:00
antelle d36588d148 ignored the whole file in prettier 2020-05-02 19:54:47 +02:00
antelle 32c171d05b Merge branch 'develop' into usb 2020-05-02 14:05:47 +02:00
antelle f031be4ce2 Merge branch 'master' into develop
# Conflicts:
#	app/scripts/storage/storage-base.js
#	release-notes.md
2020-05-02 14:04:53 +02:00
antelle d3ebcabec4 Merge branch 'master' into release-1.14 2020-05-02 14:03:04 +02:00
antelle 7c89ebc047 fixed a typo 2020-05-01 18:37:27 +02:00
antelle 8d1eafe2bc Merge branch 'develop' into usb 2020-04-29 19:24:47 +02:00
antelle 510f982579 Merge branch 'master' into develop 2020-04-29 18:59:43 +02:00
antelle 63e6be9867 plugin ids 2020-04-29 18:48:39 +02:00
antelle 0a2957449f added blob: to script-src 2020-04-29 18:41:50 +02:00
antelle ee2ba047ab removed eval's 2020-04-29 18:41:16 +02:00
antelle a8590c1b90 icon 2020-04-29 18:17:26 +02:00
antelle 76fa1f6c9f merge 2020-04-23 20:50:53 +02:00
antelle dd68aa342a Merge branch 'develop' into usb
# Conflicts:
#	app/scripts/const/default-app-settings.js
#	app/scripts/views/open-view.js
2020-04-23 20:26:52 +02:00
antelle 21d614aa97 escaping dangerous places 2020-04-23 20:09:37 +02:00
antelle f4e4bbbb8f escaping dangerous places 2020-04-23 20:06:10 +02:00
antelle d30c9d9d90 escaping more dangerous places 2020-04-23 19:55:52 +02:00
antelle e0884a4eec escaping dangerous places 2020-04-23 19:24:39 +02:00
antelle 446c7f3ab5 fixed webdav pattern 2020-04-23 19:17:05 +02:00
antelle 48a6189fe7 dropdown hint option 2020-04-23 19:12:15 +02:00
antelle e568d06b02 fix #1350: clearing master password after auto lock period 2020-04-23 18:47:46 +02:00
antelle 243c962667 #1471: WebDAV url validation 2020-04-23 18:39:59 +02:00
antelle 908510ff0b fix #1342: hint that the data will be stored in unencrypted form after exporting 2020-04-23 18:36:26 +02:00
antelle d6aab0c470 fix #1463: copying the original url instead of adding https: 2020-04-23 18:30:17 +02:00
antelle 8d2640d99d fix #1400: auto-apply tag when creating new entry in tag view 2020-04-23 18:20:46 +02:00
antelle d45748cc32 fix #557, fix #513: Argon2 speed improvements in desktop apps 2020-04-23 18:12:16 +02:00
antelle eb577d61d5 fix 2020-04-19 20:55:38 +02:00
antelle f79bb90b69 tooling check 2020-04-19 20:42:55 +02:00
antelle 4591268063 settings 2020-04-19 18:52:21 +02:00
antelle 7e849934ff Merge branch 'develop' into usb 2020-04-19 16:11:11 +02:00
antelle 92fb322f7b more network logging 2020-04-18 15:48:47 +02:00
antelle abc154738c translated a dropbox message 2020-04-18 15:48:29 +02:00
antelle 63850f75af Merge branch 'master' into develop 2020-04-18 15:23:40 +02:00
antelle 159104a50a merge 2020-04-18 11:04:06 +02:00
antelle 66b2b77791 Merge branch 'develop' into usb
# Conflicts:
#	app/scripts/models/app-settings-model.js
#	desktop/app.js
2020-04-18 11:03:06 +02:00
antelle 36ade29e46 Merge branch 'master' into develop 2020-04-18 10:52:41 +02:00
antelle 57b813196f Merge branch 'release-1.14' into develop 2020-04-18 09:04:49 +02:00
antelle 8be588afdf Merge branch 'develop' into usb 2020-04-15 19:52:54 +02:00
antelle dfdbd8b092 devtools fix 2020-04-15 19:37:03 +02:00
antelle 4fad58b1e9 Merge branch 'develop' into usb
# Conflicts:
#	app/scripts/models/app-settings-model.js
#	app/templates/open.hbs
2020-04-15 18:23:52 +02:00
antelle 035a4485b7 usb 101 2020-04-15 16:50:01 +02:00
523 changed files with 23099 additions and 44092 deletions
+3
View File
@@ -6,6 +6,9 @@
"useBuiltIns": true
}],
"@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 -1
View File
@@ -15,5 +15,5 @@ trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.nsh]
[{*.nsh,*.yml,*.yaml,*.json}]
indent_size = 2
+5 -4
View File
@@ -1,5 +1,5 @@
{
"plugins": ["prettier", "import"],
"plugins": ["prettier", "import", "babel"],
"extends": ["standard", "eslint:recommended", "plugin:prettier/recommended"],
"rules": {
"semi": ["error", "always"],
@@ -15,7 +15,7 @@
"no-useless-escape": "off",
"no-var": "error",
"prefer-const": "error",
"no-unused-expressions": "error",
"no-unused-expressions": "off",
"strict": ["error", "never"],
"no-mixed-operators": "off",
"prefer-promise-reject-errors": "off",
@@ -48,8 +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"
"import/no-default-export": "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();
+418 -488
View File
@@ -1,496 +1,426 @@
name: Build
on:
push:
tags: [ '*' ]
push:
tags: [ '*' ]
jobs:
web:
runs-on: ubuntu-latest
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-tag@v2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- uses: actions/checkout@v2
with:
repository: keeweb/keeweb
# ref: develop
- name: Install npm modules
run: npm ci
- name: Test
run: npm test
- name: Grunt
run: grunt
- name: Upload artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.html
path: dist
web:
runs-on: ubuntu-latest
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-git-tag@v3.0.2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- 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
run: npm test
- name: Grunt
run: grunt
- name: Upload artifact
uses: actions/upload-artifact@v1
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
needs:
- web
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-tag@v2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- uses: actions/checkout@v2
with:
repository: keeweb/keeweb
# ref: develop
- name: Download artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.html
path: dist
- name: Write secrets
env:
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
KEEWEB_SIGN: ${{ secrets.KEEWEB_SIGN }}
run: |
mkdir keys
echo "$PRIVATE_KEY" > keys/private-key.pem
echo "$KEEWEB_SIGN" > keys/keeweb-sign.json
- name: Build in Docker
uses: ./.github/actions/linux-build
- name: Upload AppImage artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.AppImage
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.AppImage
- name: Upload snap artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.snap
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.snap
- name: Upload deb artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.deb
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.deb
- name: Upload zip artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.zip
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.zip
- name: Upload rpm artifact
uses: actions/upload-artifact@v1
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
linux:
runs-on: ubuntu-latest
needs:
- web
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-git-tag@v3.0.2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- name: Checkout
uses: actions/checkout@v2
with:
repository: keeweb/keeweb
ref: ${{ github.repository == 'keeweb/keeweb' && github.sha || 'develop' }}
- name: Download artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.html
path: dist
- name: Write secrets
env:
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
KEEWEB_SIGN: ${{ secrets.KEEWEB_SIGN }}
run: |
mkdir keys
echo "$PRIVATE_KEY" > keys/private-key.pem
echo "$KEEWEB_SIGN" > keys/keeweb-sign.json
- name: Build in Docker
uses: ./.github/actions/linux-build
- name: Upload AppImage artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.AppImage
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.AppImage
- name: Upload snap artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.snap
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.snap
- name: Upload deb artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.deb
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.deb
- name: Upload zip artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.zip
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.zip
- name: Upload rpm artifact
uses: actions/upload-artifact@v1
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
darwin:
runs-on: macos-latest
needs:
- web
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-tag@v2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- uses: actions/checkout@v2
with:
repository: keeweb/keeweb
# ref: develop
- name: Download artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.html
path: dist
- 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
env:
CODESIGN: ${{ secrets.CODESIGN }}
APPLE_DEPLOY_PASSWORD: ${{ secrets.APPLE_DEPLOY_PASSWORD }}
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
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
with:
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
- name: Grunt
run: grunt desktop-darwin
- name: Upload 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
darwin:
runs-on: macos-latest
needs:
- web
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-git-tag@v3.0.2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- name: Checkout
uses: actions/checkout@v2
with:
repository: keeweb/keeweb
ref: ${{ github.repository == 'keeweb/keeweb' && github.sha || 'develop' }}
- name: Download artifact
uses: actions/download-artifact@v1
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 grunt
run: sudo npm i -g grunt-cli
- name: Write secrets
env:
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"
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 --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.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
needs:
- web
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-tag@v2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- uses: actions/checkout@v2
with:
repository: 'keeweb/keeweb'
# ref: 'develop'
- name: Download artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.html
path: dist
- 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
env:
CODESIGN: ${{ secrets.CODESIGN }}
MS_CODESIGN_PRIVATE_KEY_CLIENT: ${{ secrets.MS_CODESIGN_PRIVATE_KEY_CLIENT }}
MS_CODESIGN_PUBLIC_KEY_CLIENT: ${{ secrets.MS_CODESIGN_PUBLIC_KEY_CLIENT }}
MS_CODESIGN_PUBLIC_KEY_SERVER: ${{ secrets.MS_CODESIGN_PUBLIC_KEY_SERVER }}
run: |
mkdir keys
echo $Env:CODESIGN > keys/codesign.json
mkdir keys/code-signing
echo $Env:MS_CODESIGN_PRIVATE_KEY_CLIENT > keys/code-signing/private-key-client.pem
echo $Env:MS_CODESIGN_PUBLIC_KEY_CLIENT > keys/code-signing/public-key-client.pem
echo $Env:MS_CODESIGN_PUBLIC_KEY_SERVER > keys/code-signing/public-key-server.pem
- name: Grunt
run: grunt desktop-win32
- name: Upload ia32 exe artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.exe
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.exe
- name: Upload ia32 zip artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.zip
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.zip
- name: Upload x64 exe artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.exe
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.exe
- name: Upload x64 zip artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.zip
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.zip
win32:
runs-on: windows-latest
needs:
- web
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-git-tag@v3.0.2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- name: Checkout
uses: actions/checkout@v2
with:
repository: keeweb/keeweb
ref: ${{ github.repository == 'keeweb/keeweb' && github.sha || 'develop' }}
- name: Download artifact
uses: actions/download-artifact@v1
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 grunt
run: npm i -g grunt-cli
- name: Write secrets
env:
CODESIGN: ${{ secrets.CODESIGN }}
MS_CODESIGN_PRIVATE_KEY_CLIENT: ${{ secrets.MS_CODESIGN_PRIVATE_KEY_CLIENT }}
MS_CODESIGN_PUBLIC_KEY_CLIENT: ${{ secrets.MS_CODESIGN_PUBLIC_KEY_CLIENT }}
MS_CODESIGN_PUBLIC_KEY_SERVER: ${{ secrets.MS_CODESIGN_PUBLIC_KEY_SERVER }}
run: |
mkdir keys
echo $Env:CODESIGN > keys/codesign.json
mkdir keys/code-signing
echo $Env:MS_CODESIGN_PRIVATE_KEY_CLIENT > keys/code-signing/private-key-client.pem
echo $Env:MS_CODESIGN_PUBLIC_KEY_CLIENT > keys/code-signing/public-key-client.pem
echo $Env:MS_CODESIGN_PUBLIC_KEY_SERVER > keys/code-signing/public-key-server.pem
- name: Grunt
run: grunt desktop-win32 ${{ github.repository != 'keeweb/keeweb' && '--skip-sign' || '' }}
- name: Upload ia32 exe artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.exe
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.exe
- name: Upload ia32 zip artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.zip
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.zip
- name: Upload x64 exe artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.exe
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.exe
- name: Upload x64 zip artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.zip
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.zip
- name: Upload arm64 exe artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.exe
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.exe
- name: Upload arm64 zip artifact
uses: actions/upload-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.zip
path: dist/desktop/KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.zip
publish:
runs-on: ubuntu-latest
needs:
- linux
- darwin
- win32
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-tag@v2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- uses: GoogleCloudPlatform/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
with:
repository: keeweb/keeweb
path: keeweb
# ref: develop
- name: Install npm modules
working-directory: keeweb
run: npm ci
- name: Download html artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.html
path: html
- name: Download linux.AppImage artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.AppImage
path: assets
- name: Download linux.snap artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.snap
path: assets
- name: Download linux.deb artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.deb
path: assets
- name: Download linux.zip artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.zip
path: assets
- name: Download linux.rpm artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x86_64.rpm
path: assets
- name: Download darwin.dmg artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.mac.dmg
path: assets
- name: Download win32.ia32.exe artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.exe
path: assets
- name: Download win32.ia32.zip artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.zip
path: assets
- name: Download win32.x64.exe artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.exe
path: assets
- name: Download win32.x64.zip artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.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 .
- name: Copy assets to dist
run: mkdir -p keeweb/dist/desktop && cp assets/* keeweb/dist/desktop
- name: Write secrets
env:
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
KEEWEB_SIGN: ${{ secrets.KEEWEB_SIGN }}
working-directory: keeweb
run: |
mkdir keys
echo "$PRIVATE_KEY" > keys/private-key.pem
echo "$KEEWEB_SIGN" > keys/keeweb-sign.json
- name: Grunt
working-directory: keeweb
run: grunt finish-release
- name: Copy signatures to assets
run: cp keeweb/dist/desktop/Verify.sign.sha256 assets
- name: Copy checksums to assets
run: cp keeweb/dist/desktop/Verify.sha256 assets
- name: Login to DockerHub Registry
env:
DOCKERHUB_ACCESS_TOKEN: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }}
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
run: echo $DOCKERHUB_ACCESS_TOKEN | docker login -u $DOCKERHUB_USERNAME --password-stdin
- name: Copy dist to the Docker context
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 push antelle/keeweb
- name: Create a GitHub release
id: create_release
uses: actions/create-release@latest
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 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 }}
- name: Checkout gh-pages
uses: actions/checkout@v2
with:
ref: gh-pages
path: gh-pages
fetch-depth: 0
- name: Commit dist to gh-pages
working-directory: gh-pages
run: |
git rm -r '*'
cp -r ../html/* .
mkdir -p .github/workflows
echo $GITHUB_SHA > build.txt
date >> build.txt
git add .
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git commit -am v${{ steps.get_tag.outputs.tag }}
- name: Push gh-pages
uses: keeweb/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: gh-pages
directory: gh-pages
- name: Restore git mtime
working-directory: gh-pages
run: python3 ../keeweb/.github/actions/scripts/git-restore-mtime.py
- name: Sync the website
run: gsutil -m rsync -r -d -x "^\." gh-pages gs://app.keeweb.info/
publish:
runs-on: ubuntu-latest
needs:
- linux
- darwin
- win32
steps:
- name: Get current git tag
id: get_tag
uses: keeweb/get-git-tag@v3.0.2
with:
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
tagRegexGroup: 1
- 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
- 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
- name: Download html artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.html
path: html
- name: Download linux.AppImage artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.AppImage
path: assets
- name: Download linux.snap artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.snap
path: assets
- name: Download linux.deb artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.deb
path: assets
- name: Download linux.zip artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x64.zip
path: assets
- name: Download linux.rpm artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.linux.x86_64.rpm
path: assets
- name: Download darwin.x64.dmg artifact
uses: actions/download-artifact@v1
with:
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
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.exe
path: assets
- name: Download win32.ia32.zip artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.ia32.zip
path: assets
- name: Download win32.x64.exe artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.exe
path: assets
- name: Download win32.x64.zip artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.x64.zip
path: assets
- name: Download win32.arm64.exe artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.exe
path: assets
- name: Download win32.arm64.zip artifact
uses: actions/download-artifact@v1
with:
name: KeeWeb-${{ steps.get_tag.outputs.tag }}.win.arm64.zip
path: assets
- name: Zip html
working-directory: html
run: zip -vr ../assets/KeeWeb-${{ steps.get_tag.outputs.tag }}.html.zip .
- name: Copy assets to dist
run: mkdir -p keeweb/dist/desktop && cp assets/* keeweb/dist/desktop
- name: Write secrets
env:
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
KEEWEB_SIGN: ${{ secrets.KEEWEB_SIGN }}
working-directory: keeweb
run: |
mkdir keys
echo "$PRIVATE_KEY" > keys/private-key.pem
echo "$KEEWEB_SIGN" > keys/keeweb-sign.json
- name: Grunt
working-directory: keeweb
run: grunt finish-release
- name: Copy signatures to assets
run: cp keeweb/dist/desktop/Verify.sign.sha256 assets
- name: Copy checksums to assets
run: cp keeweb/dist/desktop/Verify.sha256 assets
- name: Login to DockerHub Registry
env:
DOCKERHUB_ACCESS_TOKEN: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }}
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
run: echo $DOCKERHUB_ACCESS_TOKEN | docker login -u $DOCKERHUB_USERNAME --password-stdin
- name: Copy dist to the Docker context
run: cp -r html keeweb/package/docker/dist
- name: Build the Docker image
working-directory: keeweb
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 --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: 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:
ref: gh-pages
path: gh-pages
fetch-depth: 0
- name: Commit dist to gh-pages
working-directory: gh-pages
run: |
git rm -r '*'
cp -r ../html/* .
mkdir -p .github/workflows
echo $GITHUB_SHA > build.txt
date >> build.txt
git add .
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git commit -am v${{ steps.get_tag.outputs.tag }}
- name: Push gh-pages
uses: keeweb/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: gh-pages
directory: gh-pages
- name: Restore git mtime
working-directory: gh-pages
run: python3 ../keeweb/.github/actions/scripts/git-restore-mtime.py
- name: Sync the website
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: |
curl -sS -X POST "https://api.cloudflare.com/client/v4/zones/dbda8e7e454d23edb08d4bc3b3f6f872/purge_cache" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'
-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/
+339 -212
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');
@@ -10,7 +11,7 @@ const pkg = require('./package.json');
debug.enable('electron-notarize');
module.exports = function(grunt) {
module.exports = function (grunt) {
require('time-grunt')(grunt);
require('load-grunt-tasks')(grunt);
@@ -24,36 +25,72 @@ module.exports = function(grunt) {
const dt = date.toISOString().replace(/T.*/, '');
const year = date.getFullYear();
const minElectronVersionForUpdate = '8.0.0';
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 getCodeSingConfig = () => require('./keys/codesign.json');
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'
};
grunt.initConfig({
gitinfo: {
branch: {
current: {
SHA: 'Current HEAD SHA',
shortSHA: 'Current HEAD short SHA',
name: 'Current branch name',
lastCommitTime: 'Last commit time'
}
const windowsAppVersionString = {
CompanyName: 'KeeWeb',
FileDescription: pkg.description,
OriginalFilename: 'KeeWeb.exe',
ProductName: 'KeeWeb',
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: {} },
clean: {
dist: ['dist', 'tmp'],
desktop: ['tmp/desktop', 'dist/desktop'],
cordova: ['tmp/cordova', 'dist/cordova']
desktop: ['tmp/desktop', 'dist/desktop']
},
copy: {
html: {
@@ -61,19 +98,11 @@ module.exports = function(grunt) {
dest: 'tmp/index.html',
nonull: true
},
'html-dist': {
src: 'tmp/app.html',
dest: 'dist/index.html',
nonull: true
},
'404-dist': {
src: 'app/404.html',
dest: 'dist/404.html',
nonull: true
},
favicon: {
src: 'app/favicon.png',
dest: 'tmp/favicon.png',
'content-dist': {
cwd: 'app/content/',
src: '**',
dest: 'dist/',
expand: true,
nonull: true
},
icons: {
@@ -104,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',
@@ -123,27 +145,23 @@ module.exports = function(grunt) {
expand: true,
nonull: true
},
'desktop-update': {
cwd: 'tmp/desktop/keeweb-linux-x64/resources/',
src: 'app.asar',
dest: 'tmp/desktop/update/',
'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',
expand: true,
nonull: true
nonull: true,
options: { mode: true }
},
'desktop-update-helper': {
src: ['helper/darwin/KeeWebHelper', 'helper/win32/KeeWebHelper.exe'],
dest: 'tmp/desktop/update/',
nonull: true
},
'desktop-windows-helper-ia32': {
src: 'helper/win32/KeeWebHelper.exe',
dest: 'tmp/desktop/KeeWeb-win32-ia32/Resources/',
nonull: true
},
'desktop-windows-helper-x64': {
src: 'helper/win32/KeeWebHelper.exe',
dest: 'tmp/desktop/KeeWeb-win32-x64/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',
@@ -155,6 +173,41 @@ module.exports = function(grunt) {
dest: `dist/desktop/KeeWeb-${pkg.version}.win.ia32.exe`,
nonull: true
},
'desktop-win32-dist-arm64': {
src: 'tmp/desktop/KeeWeb.win.arm64.exe',
dest: `dist/desktop/KeeWeb-${pkg.version}.win.arm64.exe`,
nonull: true
},
'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/',
nonull: true
},
'native-modules-win32-ia32': {
src: 'node_modules/@keeweb/keeweb-native-modules/*-win32-ia32.node',
dest: 'tmp/desktop/KeeWeb-win32-ia32/resources/',
nonull: true
},
'native-modules-win32-arm64': {
src: 'node_modules/@keeweb/keeweb-native-modules/*-win32-arm64.node',
dest: 'tmp/desktop/KeeWeb-win32-arm64/resources/',
nonull: true
},
'native-modules-linux-x64': {
src: 'node_modules/@keeweb/keeweb-native-modules/*-linux-x64.node',
dest: 'tmp/desktop/keeweb-linux-x64/resources/',
nonull: true
},
'electron-builder-dist-linux-rpm': {
src: `tmp/desktop/electron-builder/KeeWeb-${pkg.version}.x86_64.rpm`,
dest: `dist/desktop/KeeWeb-${pkg.version}.linux.x86_64.rpm`,
@@ -169,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: {
@@ -176,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: {
@@ -189,12 +289,12 @@ module.exports = function(grunt) {
algo: 'sha512',
expected: {
style: 1,
script: 3
script: 1
}
},
app: {
src: 'tmp/app.html',
dest: 'tmp/app.html'
dest: 'dist/index.html'
}
},
htmlmin: {
@@ -209,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 }] },
@@ -245,17 +345,6 @@ module.exports = function(grunt) {
]
},
files: { 'tmp/desktop/app/main.js': 'desktop/main.js' }
},
'cordova-html': {
options: {
replacements: [
{
pattern: '<script',
replacement: '<script src="cordova.js"></script><script'
}
]
},
files: { 'tmp/cordova/app/index.html': 'dist/index.html' }
}
},
webpack: {
@@ -270,7 +359,10 @@ module.exports = function(grunt) {
sha: 'dev'
}),
publicPath: '/',
contentBase: path.resolve(__dirname, 'tmp'),
contentBase: [
path.resolve(__dirname, 'tmp'),
path.resolve(__dirname, 'app/content')
],
progress: false
},
js: {
@@ -288,93 +380,61 @@ 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',
osxSign: {
get identity() {
return getCodeSingConfig().identities.app;
},
hardenedRuntime: true,
entitlements: 'package/osx/entitlements.mac.plist',
'entitlements-inherit': 'package/osx/entitlements.mac.plist',
'gatekeeper-assess': false
},
osxNotarize: {
get appleId() {
return getCodeSingConfig().appleId;
},
appleIdPassword: '@keychain:AC_PASSWORD',
get ascProvider() {
return getCodeSingConfig().teamId;
}
},
afterCopy: [
(buildPath, electronVersion, platform, arch, callback) => {
if (path.basename(buildPath) !== 'app') {
throw new Error('Bad build path: ' + buildPath);
}
const resPath = path.dirname(buildPath);
if (path.basename(resPath) !== 'Resources') {
throw new Error('Bad Resources path: ' + resPath);
}
const helperTargetPath = path.join(
resPath,
'helper/darwin/KeeWebHelper'
);
const helperSourcePath = path.join(
__dirname,
'helper/darwin/KeeWebHelper'
);
fs.copySync(helperSourcePath, helperTargetPath);
const contentsPath = path.dirname(resPath);
if (path.basename(contentsPath) !== 'Contents') {
throw new Error('Bad Contents path: ' + contentsPath);
}
const installerSourcePath = path.join(
__dirname,
'package/osx/KeeWeb Installer.app'
);
const installerTargetPath = path.join(
contentsPath,
'Installer/KeeWeb Installer.app'
);
fs.copySync(installerSourcePath, installerTargetPath);
callback();
}
]
extendInfo: 'package/osx/extend.plist'
}
},
win32: {
'darwin-arm64': {
options: {
platform: 'darwin',
arch: 'arm64',
icon: 'graphics/icon.icns',
appBundleId: 'net.antelle.keeweb',
appCategoryType: 'public.app-category.productivity',
extendInfo: 'package/osx/extend.plist'
}
},
'win32-x64': {
options: {
platform: 'win32',
arch: ['ia32', 'x64'],
arch: 'x64',
icon: 'graphics/icon.ico',
buildVersion: pkg.version,
'version-string': {
CompanyName: 'KeeWeb',
FileDescription: pkg.description,
OriginalFilename: 'KeeWeb.exe',
ProductName: 'KeeWeb',
InternalName: 'KeeWeb'
}
'version-string': windowsAppVersionString
}
},
'win32-ia32': {
options: {
platform: 'win32',
arch: 'ia32',
icon: 'graphics/icon.ico',
buildVersion: pkg.version,
'version-string': windowsAppVersionString
}
},
'win32-arm64': {
options: {
platform: 'win32',
arch: 'arm64',
icon: 'graphics/icon.ico',
buildVersion: pkg.version,
'version-string': windowsAppVersionString
}
}
},
@@ -403,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 }]
@@ -431,6 +502,10 @@ module.exports = function(grunt) {
options: { archive: `dist/desktop/KeeWeb-${pkg.version}.win.ia32.zip` },
files: [{ cwd: 'tmp/desktop/KeeWeb-win32-ia32', src: '**', expand: true }]
},
'win32-arm64': {
options: { archive: `dist/desktop/KeeWeb-${pkg.version}.win.arm64.zip` },
files: [{ cwd: 'tmp/desktop/KeeWeb-win32-arm64', src: '**', expand: true }]
},
'linux-x64': {
options: { archive: `dist/desktop/KeeWeb-${pkg.version}.linux.x64.zip` },
files: [
@@ -440,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
}
},
@@ -498,6 +559,20 @@ module.exports = function(grunt) {
arch: 'ia32',
output: 'tmp/desktop/KeeWeb-win32-ia32/uninst.exe'
}
},
'win32-arm64': {
options: {
installScript: 'package/nsis/main.nsi',
arch: 'arm64',
output: 'tmp/desktop/KeeWeb.win.arm64.exe'
}
},
'win32-un-arm64': {
options: {
installScript: 'package/nsis/main-un.nsi',
arch: 'arm64',
output: 'tmp/desktop/KeeWeb-win32-arm64/uninst.exe'
}
}
},
chmod: {
@@ -517,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': {
@@ -529,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'
}
@@ -552,49 +625,66 @@ module.exports = function(grunt) {
]
}
},
'sign-archive': {
'desktop-update': {
'osx-sign': {
options: {
get identity() {
return getCodeSignConfig().identities.app;
},
hardenedRuntime: true,
entitlements: 'package/osx/entitlements.plist',
'entitlements-inherit': 'package/osx/entitlements-inherit.plist',
'gatekeeper-assess': false
},
'desktop-x64': {
options: {
file: 'dist/desktop/UpdateDesktop.zip',
signature: zipCommentPlaceholder
}
'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'
}
},
'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'
notarize: {
options: {
appBundleId: 'net.antelle.keeweb',
get appleId() {
return getCodeSignConfig().appleId;
},
appleIdPassword: '@keychain:AC_PASSWORD',
get ascProvider() {
return getCodeSignConfig().teamId;
}
},
'desktop-x64': {
src: 'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app'
},
'desktop-arm64': {
src: 'tmp/desktop/KeeWeb-darwin-arm64/KeeWeb.app'
}
},
'sign-exe': {
options: {
url: pkg.homepage,
get windows() {
return getCodeSingConfig().windows;
return getCodeSignConfig().windows;
},
get certHash() {
return getCodeSingConfig().microsoftCertHash;
return getCodeSignConfig().microsoftCertHash;
}
},
'win32-build-x64': {
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',
@@ -607,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',
@@ -615,6 +707,20 @@ module.exports = function(grunt) {
}
}
},
'win32-build-arm64': {
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',
'tmp/desktop/KeeWeb-win32-arm64/libGLESv2.dll':
'ANGLE libGLESv2 Dynamic Link Library'
}
}
},
'win32-uninst-x64': {
options: {
files: {
@@ -629,6 +735,13 @@ module.exports = function(grunt) {
}
}
},
'win32-uninst-arm64': {
options: {
files: {
'tmp/desktop/KeeWeb-win32-arm64/uninst.exe': 'KeeWeb Uninstaller'
}
}
},
'win32-installer-x64': {
options: {
files: {
@@ -642,6 +755,13 @@ module.exports = function(grunt) {
'tmp/desktop/KeeWeb.win.ia32.exe': 'KeeWeb Setup'
}
}
},
'win32-installer-arm64': {
options: {
files: {
'tmp/desktop/KeeWeb.win.arm64.exe': 'KeeWeb Setup'
}
}
}
},
'sign-dist': {
@@ -650,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-*']
}
}
},
@@ -662,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
+29 -12
View File
@@ -5,7 +5,7 @@ The app can run either in browser, or as a desktop app.
![screenshot](img/screenshot.png)
# Quick Links
## Quick Links
Apps: [Web](https://app.keeweb.info/), [Desktop](https://github.com/keeweb/keeweb/releases/latest)
Timeline: [Release Notes](release-notes.md), [TODO](https://github.com/keeweb/keeweb/wiki/TODO)
@@ -14,11 +14,11 @@ Website: [keeweb.info](https://keeweb.info)
Twitter: [kee_web](https://twitter.com/kee_web)
Donate: [OpenCollective](https://opencollective.com/keeweb#support), [GitHub](https://github.com/sponsors/antelle)
# Status
## Status
The app is quite stable now. Basic stuff, as well as more advanced operations, should be rather reliable.
# Self-hosting
## Self-hosting
Everything you need to host this app on your server is any static file server. The app is a single HTML file + a service worker (optionally; for offline access).
You can download the latest distribution files from [gh-pages](https://github.com/keeweb/keeweb/archive/gh-pages.zip) branch.
@@ -33,17 +33,18 @@ docker run --name keeweb -d -p 443:443 -p 80:80 -v $EXT_DIR:/etc/nginx/external/
To make Dropbox work in your self-hosted app, [go to this Wiki page](https://github.com/keeweb/keeweb/wiki/Dropbox-and-GDrive).
# Building
## Building
The easiest way to clone all KeeWeb repos is:
```bash
curl https://raw.githubusercontent.com/keeweb/keeweb/develop/dev-env.sh | bash -
```
The app can be built with grunt: `grunt` (html file will be in `dist/`).
Desktop apps are built with `grunt desktop`. This works only in macOS as it builds dmg; requires wine.
Also, a hardware token is required.
To run Electron app without building an installer, build the app with `grunt` and start it this way:
The app can be built with grunt: `grunt` (html files will be in `dist/`).
Desktop apps are built with `grunt desktop`. This requires some magic and currently works only on CI,
you can find more details in [the GitHub Actions workflow](.github/workflows/build.yaml).
To run the desktop (electron) app without building an installer, build the app with `grunt` and start it this way:
```bash
npm run dev
npm run electron
@@ -54,7 +55,15 @@ For debug build:
1. run `npm run dev`
2. open `http://localhost:8085`
# Contributing
To build desktop apps, use these goals, the result can be found in `tmp`:
```
npm run dev-desktop-macos
npm run dev-desktop-windows
npm run dev-desktop-linux
```
## Contributing
Please read contribution guidelines [for pull requests](.github/PULL_REQUEST_TEMPLATE.md).
Here's a [list of issues](https://github.com/keeweb/keeweb/labels/help%20wanted) where your help would be very welcome.
@@ -62,12 +71,12 @@ Also you can help by [translating KeeWeb](https://keeweb.oneskyapp.com) to your
Other ways of contribution can be found [on this page](CONTRIBUTING.md).
### Important notes for pull requests
#### Important notes for pull requests
- please branch from `develop`, not `master`
- don't edit translation files except base.json, they will be replaced
# Donations
## Donations
KeeWeb is not free to develop. It takes time, requires paid code signing certificates and domains.
You can help the project or say "thank you" with this button:
@@ -77,7 +86,15 @@ You can also sponsor the developer directly [on GitHub](https://github.com/spons
Please note: donation does not imply any type of service contract.
## Thank you
# License
Notable contributions to KeeWeb:
- 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)
## License
[MIT](https://github.com/keeweb/keeweb/blob/master/LICENSE)
-27
View File
@@ -1,27 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Not Found</title>
<style>
body {
font-family: -apple-system, "BlinkMacSystemFont", "Raleway", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
font-feature-settings: "liga" 0;
text-align: center;
background: #F5F5F5;
font-weight: 300;
}
h1 {
font-size: 3em;
margin-top: 10vh;
}
p {
line-height: 1.5em;
}
</style>
</head>
<body>
<h1>404 Not Found</h1>
<p>Sorry, the page you're looking for was not found.</p>
</body>
</html>
+28
View File
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Not Found</title>
<style>
body {
font-family: -apple-system, 'BlinkMacSystemFont', 'Raleway', 'Helvetica Neue',
'Helvetica', 'Arial', sans-serif;
font-feature-settings: 'liga' 0;
text-align: center;
background: #f5f5f5;
font-weight: 300;
}
h1 {
font-size: 3em;
margin-top: 10vh;
}
p {
line-height: 1.5em;
}
</style>
</head>
<body>
<h1>404 Not Found</h1>
<p>Sorry, the page you're looking for was not found.</p>
</body>
</html>
+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: 'dropbox', search: location.search },
window.location.origin
);
window.close();
</script>
</body>
</html>
+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: 'gdrive', search: location.search },
window.location.origin
);
window.close();
</script>
</body>
</html>
+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>
+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: 'onedrive', 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

-3
View File
@@ -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

+40 -22
View File
@@ -5,10 +5,12 @@ import { AppRightsChecker } from 'comp/app/app-rights-checker';
import { ExportApi } from 'comp/app/export-api';
import { SingleInstanceChecker } from 'comp/app/single-instance-checker';
import { Updater } from 'comp/app/updater';
import { AuthReceiver } from 'comp/browser/auth-receiver';
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';
@@ -25,6 +27,8 @@ import { KdbxwebInit } from 'util/kdbxweb/kdbxweb-init';
import { Locale } from 'util/locale';
import { AppView } from 'views/app-view';
import 'hbs-helpers';
import { AutoType } from './auto-type';
import { Storage } from './storage';
StartProfiler.milestone('loading modules');
@@ -33,11 +37,6 @@ const ready = (Launcher && Launcher.ready) || $;
ready(() => {
StartProfiler.milestone('document ready');
if (AuthReceiver.receive()) {
return;
}
StartProfiler.milestone('checking auth');
const appModel = new AppModel();
StartProfiler.milestone('creating app model');
@@ -46,9 +45,11 @@ ready(() => {
.then(initModules)
.then(loadRemoteConfig)
.then(ensureCanRun)
.then(initStorage)
.then(initUsbListener)
.then(showApp)
.then(postInit)
.catch(e => {
.catch((e) => {
appModel.appLogger.error('Error starting app', e);
});
@@ -59,10 +60,11 @@ ready(() => {
);
}
return FeatureTester.test()
.catch(e => {
.catch((e) => {
Alerts.error({
header: Locale.appSettingsError,
body: Locale.appNotSupportedError + '<br/><br/>' + e,
body: Locale.appNotSupportedError,
pre: e,
buttons: [],
esc: false,
enter: false,
@@ -88,10 +90,12 @@ ready(() => {
function initModules() {
KeyHandler.init();
IdleTracker.init();
PopupNotifier.init();
KdbxwebInit.init();
FocusDetector.init();
AutoType.init();
ThemeWatcher.init();
SettingsManager.init();
window.kw = ExportApi;
return PluginManager.init().then(() => {
StartProfiler.milestone('initializing modules');
@@ -112,15 +116,15 @@ 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 => {
.catch((e) => {
if (!appModel.settings.cacheConfigSettings) {
showSettingsLoadError();
throw e;
@@ -133,6 +137,18 @@ ready(() => {
});
}
function initStorage() {
for (const prv of Object.values(Storage)) {
prv.init();
}
StartProfiler.milestone('initializing storage');
}
function initUsbListener() {
UsbListener.init();
StartProfiler.milestone('starting usb');
}
function showApp() {
return Promise.resolve().then(() => {
const skipHttpsWarning =
@@ -140,14 +156,14 @@ ready(() => {
const protocolIsInsecure = ['https:', 'file:', 'app:'].indexOf(location.protocol) < 0;
const hostIsInsecure = location.hostname !== 'localhost';
if (protocolIsInsecure && hostIsInsecure && !skipHttpsWarning) {
return new Promise(resolve => {
return new Promise((resolve) => {
Alerts.error({
header: Locale.appSecWarn,
icon: 'user-secret',
esc: false,
enter: false,
click: false,
body: Locale.appSecWarnBody1 + '<br/><br/>' + Locale.appSecWarnBody2,
body: Locale.appSecWarnBody1 + '\n\n' + Locale.appSecWarnBody2,
buttons: [{ result: '', title: Locale.appSecWarnBtn, error: true }],
complete: () => {
showView();
@@ -157,21 +173,23 @@ ready(() => {
});
} else {
showView();
return new Promise((resolve) => requestAnimationFrame(resolve));
}
});
}
function postInit() {
Updater.init();
SingleInstanceChecker.init();
AppRightsChecker.init();
setTimeout(() => PluginManager.runAutoUpdate(), Timeouts.AutoUpdatePluginsAfterStart);
setTimeout(() => {
Updater.init();
SingleInstanceChecker.init();
AppRightsChecker.init();
IdleTracker.init();
BrowserExtensionConnector.init(appModel);
PluginManager.runAutoUpdate();
}, Timeouts.AutoUpdatePluginsAfterStart);
}
function showView() {
appModel.prepare();
StartProfiler.milestone('preparing app model');
new AppView(appModel).render();
StartProfiler.milestone('first view rendering');
@@ -1,14 +0,0 @@
import { Launcher } from 'comp/launcher';
const AutoTypeEmitterFactory = {
create(callback) {
if (Launcher && Launcher.autoTypeSupported) {
const { AutoTypeEmitter } = require('./emitter/auto-type-emitter-' +
Launcher.platform());
return new AutoTypeEmitter(callback);
}
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 };
+15 -15
View File
@@ -11,7 +11,7 @@ const MaxSteps = 1000;
const MaxCopy = 2;
const FakeCharAlphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz123456789O0oIl';
const AutoTypeObfuscator = function(chars) {
const AutoTypeObfuscator = function (chars) {
this.chars = chars;
this.inputChars = [];
this.inputCursor = 0;
@@ -21,7 +21,7 @@ const AutoTypeObfuscator = function(chars) {
this.copyCount = 0;
};
AutoTypeObfuscator.prototype.obfuscate = function() {
AutoTypeObfuscator.prototype.obfuscate = function () {
while (!this.finished()) {
this.step();
if (this.stepCount++ > MaxSteps) {
@@ -35,16 +35,16 @@ AutoTypeObfuscator.prototype.obfuscate = function() {
return this.ops;
};
AutoTypeObfuscator.prototype.finished = function() {
AutoTypeObfuscator.prototype.finished = function () {
return (
this.chars.length === this.inputChars.length &&
this.chars.every(function(ch, ix) {
this.chars.every(function (ch, ix) {
return this.inputChars[ix].ch === ch;
}, this)
);
};
AutoTypeObfuscator.prototype.step = function() {
AutoTypeObfuscator.prototype.step = function () {
const isFake = this.stepCount < MaxFakeOps && Math.random() > this.stepCount / MaxFakeOps;
if (isFake) {
this.stepFake();
@@ -52,11 +52,11 @@ AutoTypeObfuscator.prototype.step = function() {
this.stepReal();
}
if (logger.getLevel() >= Logger.Level.Debug) {
logger.debug('value', this.inputChars.map(ic => ic.ch).join(''));
logger.debug('value', this.inputChars.map((ic) => ic.ch).join(''));
}
};
AutoTypeObfuscator.prototype.stepFake = function() {
AutoTypeObfuscator.prototype.stepFake = function () {
const pos = Math.floor(Math.random() * (this.inputChars.length + 1));
const ch = FakeCharAlphabet[Math.floor(Math.random() * FakeCharAlphabet.length)];
logger.info('step.fake', pos, ch);
@@ -85,7 +85,7 @@ AutoTypeObfuscator.prototype.stepFake = function() {
}
};
AutoTypeObfuscator.prototype.stepReal = function() {
AutoTypeObfuscator.prototype.stepReal = function () {
const possibleActions = [];
const inputRealPositions = [];
let i;
@@ -134,7 +134,7 @@ AutoTypeObfuscator.prototype.stepReal = function() {
}
};
AutoTypeObfuscator.prototype.moveToPos = function(pos) {
AutoTypeObfuscator.prototype.moveToPos = function (pos) {
logger.debug('moveToPos', pos);
while (this.inputCursor > pos) {
this.moveLeft();
@@ -144,21 +144,21 @@ AutoTypeObfuscator.prototype.moveToPos = function(pos) {
}
};
AutoTypeObfuscator.prototype.moveLeft = function() {
AutoTypeObfuscator.prototype.moveLeft = function () {
logger.debug('moveLeft');
this.ops.push({ type: 'key', value: 'left' });
this.inputCursor--;
this.inputSel = 0;
};
AutoTypeObfuscator.prototype.moveRight = function() {
AutoTypeObfuscator.prototype.moveRight = function () {
logger.debug('moveRight');
this.ops.push({ type: 'key', value: 'right' });
this.inputCursor++;
this.inputSel = 0;
};
AutoTypeObfuscator.prototype.inputChar = function(ch) {
AutoTypeObfuscator.prototype.inputChar = function (ch) {
logger.debug('inputChar', ch);
this.ops.push({ type: 'text', value: ch });
this.inputChars.splice(this.inputCursor, this.inputSel, { ch });
@@ -166,7 +166,7 @@ AutoTypeObfuscator.prototype.inputChar = function(ch) {
this.inputSel = 0;
};
AutoTypeObfuscator.prototype.copyPaste = function(ch) {
AutoTypeObfuscator.prototype.copyPaste = function (ch) {
logger.debug('copyPaste', ch);
this.ops.push({ type: 'cmd', value: 'copyPaste', arg: ch });
this.inputChars.splice(this.inputCursor, this.inputSel, { ch });
@@ -174,7 +174,7 @@ AutoTypeObfuscator.prototype.copyPaste = function(ch) {
this.inputSel = 0;
};
AutoTypeObfuscator.prototype.selectText = function(backward, count) {
AutoTypeObfuscator.prototype.selectText = function (backward, count) {
logger.debug('selectText', backward ? 'left' : 'right', count);
const ops = [];
for (let i = 0; i < count; i++) {
@@ -192,7 +192,7 @@ AutoTypeObfuscator.prototype.selectText = function(backward, count) {
this.inputSel = count;
};
AutoTypeObfuscator.prototype.deleteText = function(backward) {
AutoTypeObfuscator.prototype.deleteText = function (backward) {
logger.debug('deleteText', backward ? 'left' : 'right');
this.ops.push({ type: 'key', value: backward ? 'bs' : 'del' });
if (this.inputSel) {
+11 -11
View File
@@ -1,6 +1,6 @@
import { AutoTypeRunner } from 'auto-type/auto-type-runner';
const AutoTypeParser = function(sequence) {
const AutoTypeParser = function (sequence) {
this.sequence = sequence;
this.ix = 0;
this.states = [];
@@ -8,7 +8,7 @@ const AutoTypeParser = function(sequence) {
AutoTypeParser.opRegex = /^(.*?)(?:([\s:=])[\s:=]*(.*))?$/;
AutoTypeParser.prototype.parse = function() {
AutoTypeParser.prototype.parse = function () {
const len = this.sequence.length;
this.pushState();
while (this.ix < len) {
@@ -45,14 +45,14 @@ AutoTypeParser.prototype.parse = function() {
return new AutoTypeRunner(this.state().ops);
};
AutoTypeParser.prototype.pushState = function() {
AutoTypeParser.prototype.pushState = function () {
this.states.unshift({
modifiers: null,
ops: []
});
};
AutoTypeParser.prototype.popState = function() {
AutoTypeParser.prototype.popState = function () {
if (this.states.length <= 1) {
throw 'Unexpected ")" at index ' + this.ix;
}
@@ -60,11 +60,11 @@ AutoTypeParser.prototype.popState = function() {
this.addState(state);
};
AutoTypeParser.prototype.state = function() {
AutoTypeParser.prototype.state = function () {
return this.states[0];
};
AutoTypeParser.prototype.readOp = function() {
AutoTypeParser.prototype.readOp = function () {
const toIx = this.sequence.indexOf('}', this.ix + 2);
if (toIx < 0) {
throw 'Mismatched "{" at index ' + this.ix;
@@ -79,7 +79,7 @@ AutoTypeParser.prototype.readOp = function() {
this.addOp(op, sep, arg);
};
AutoTypeParser.prototype.readModifier = function(modifier) {
AutoTypeParser.prototype.readModifier = function (modifier) {
const state = this.state();
if (!state.modifiers) {
state.modifiers = {};
@@ -91,14 +91,14 @@ AutoTypeParser.prototype.readModifier = function(modifier) {
state.modifiers[modifier] = true;
};
AutoTypeParser.prototype.resetModifiers = function() {
AutoTypeParser.prototype.resetModifiers = function () {
const state = this.state();
const modifiers = state.modifiers;
state.modifiers = null;
return modifiers;
};
AutoTypeParser.prototype.addState = function(state) {
AutoTypeParser.prototype.addState = function (state) {
this.state().ops.push({
type: 'group',
value: state.ops,
@@ -106,7 +106,7 @@ AutoTypeParser.prototype.addState = function(state) {
});
};
AutoTypeParser.prototype.addChar = function(ch) {
AutoTypeParser.prototype.addChar = function (ch) {
this.state().ops.push({
type: 'text',
value: ch,
@@ -114,7 +114,7 @@ AutoTypeParser.prototype.addChar = function(ch) {
});
};
AutoTypeParser.prototype.addOp = function(op, sep, arg) {
AutoTypeParser.prototype.addOp = function (op, sep, arg) {
this.state().ops.push({
type: 'op',
value: op,
+37 -31
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,10 +6,10 @@ 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) {
const AutoTypeRunner = function (ops) {
this.ops = ops;
this.pendingResolvesCount = 0;
this.entry = null;
@@ -142,8 +142,9 @@ AutoTypeRunner.Substitutions = {
}
};
AutoTypeRunner.prototype.resolve = function(entry, callback) {
AutoTypeRunner.prototype.resolve = function (entry, context, callback) {
this.entry = entry;
this.context = context;
try {
this.resolveOps(this.ops);
if (!this.pendingResolvesCount) {
@@ -156,7 +157,7 @@ AutoTypeRunner.prototype.resolve = function(entry, callback) {
}
};
AutoTypeRunner.prototype.resolveOps = function(ops) {
AutoTypeRunner.prototype.resolveOps = function (ops) {
for (let i = 0, len = ops.length; i < len; i++) {
const op = ops[i];
if (op.type === 'group') {
@@ -167,7 +168,7 @@ AutoTypeRunner.prototype.resolveOps = function(ops) {
}
};
AutoTypeRunner.prototype.resolveOp = function(op) {
AutoTypeRunner.prototype.resolveOp = function (op) {
if (op.value.length === 1 && !op.sep) {
// {x}
op.type = 'text';
@@ -203,6 +204,11 @@ AutoTypeRunner.prototype.resolveOp = function(op) {
}
return;
}
if (this.context?.resolved?.[lowerValue]) {
op.type = 'text';
op.value = this.context.resolved[lowerValue];
return;
}
const substitution = AutoTypeRunner.Substitutions[lowerValue];
if (substitution) {
// {title}
@@ -218,7 +224,7 @@ AutoTypeRunner.prototype.resolveOp = function(op) {
}
};
AutoTypeRunner.prototype.tryParseCommand = function(op) {
AutoTypeRunner.prototype.tryParseCommand = function (op) {
switch (op.value.toLowerCase()) {
case 'clearfield':
// {CLEARFIELD}
@@ -233,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;
@@ -257,7 +263,7 @@ AutoTypeRunner.prototype.tryParseCommand = function(op) {
}
};
AutoTypeRunner.prototype.getEntryFieldKeys = function(field, op) {
AutoTypeRunner.prototype.getEntryFieldKeys = function (field, op) {
if (!field || !this.entry) {
return '';
}
@@ -268,7 +274,7 @@ AutoTypeRunner.prototype.getEntryFieldKeys = function(field, op) {
if (value.isProtected) {
op.type = 'group';
const ops = [];
value.forEachChar(ch => {
value.forEachChar((ch) => {
if (ch === 10 || ch === 13) {
ops.push({ type: 'key', value: 'enter' });
} else {
@@ -283,7 +289,7 @@ AutoTypeRunner.prototype.getEntryFieldKeys = function(field, op) {
}
op.type = 'group';
const partsOps = [];
parts.forEach(part => {
parts.forEach((part) => {
if (partsOps.length) {
partsOps.push({ type: 'key', value: 'enter' });
}
@@ -295,11 +301,11 @@ AutoTypeRunner.prototype.getEntryFieldKeys = function(field, op) {
}
};
AutoTypeRunner.prototype.getEntryGroupName = function() {
AutoTypeRunner.prototype.getEntryGroupName = function () {
return this.entry && this.entry.group.title;
};
AutoTypeRunner.prototype.dt = function(part) {
AutoTypeRunner.prototype.dt = function (part) {
switch (part) {
case 'simple':
return (
@@ -327,7 +333,7 @@ AutoTypeRunner.prototype.dt = function(part) {
}
};
AutoTypeRunner.prototype.udt = function(part) {
AutoTypeRunner.prototype.udt = function (part) {
switch (part) {
case 'simple':
return (
@@ -355,7 +361,7 @@ AutoTypeRunner.prototype.udt = function(part) {
}
};
AutoTypeRunner.prototype.getOtp = function(op) {
AutoTypeRunner.prototype.getOtp = function (op) {
if (!this.entry) {
return '';
}
@@ -363,13 +369,13 @@ AutoTypeRunner.prototype.getOtp = function(op) {
if (!this.entry.otpGenerator) {
return '';
}
this.entry.otpGenerator.next(otp => {
this.pendingResolved(op, otp, otp ? undefined : 'OTP error');
this.entry.otpGenerator.next((err, otp) => {
this.pendingResolved(op, otp, err);
});
return AutoTypeRunner.PendingResolve;
};
AutoTypeRunner.prototype.pendingResolved = function(op, value, error) {
AutoTypeRunner.prototype.pendingResolved = function (op, value, error) {
const wasPending = op.value === AutoTypeRunner.PendingResolve;
if (value) {
op.value = value;
@@ -384,11 +390,11 @@ AutoTypeRunner.prototype.pendingResolved = function(op, value, error) {
}
};
AutoTypeRunner.prototype.obfuscate = function() {
AutoTypeRunner.prototype.obfuscate = function () {
this.obfuscateOps(this.ops);
};
AutoTypeRunner.prototype.obfuscateOps = function(ops) {
AutoTypeRunner.prototype.obfuscateOps = function (ops) {
for (let i = 0, len = ops.length; i < len; i++) {
const op = ops[i];
if (op.mod) {
@@ -397,7 +403,7 @@ AutoTypeRunner.prototype.obfuscateOps = function(ops) {
if (op.type === 'text') {
this.obfuscateOp(op);
} else if (op.type === 'group') {
const onlyText = op.value.every(grOp => grOp.type === 'text' && !grOp.mod);
const onlyText = op.value.every((grOp) => grOp.type === 'text' && !grOp.mod);
if (onlyText) {
this.obfuscateOp(op);
} else {
@@ -407,7 +413,7 @@ AutoTypeRunner.prototype.obfuscateOps = function(ops) {
}
};
AutoTypeRunner.prototype.obfuscateOp = function(op) {
AutoTypeRunner.prototype.obfuscateOp = function (op) {
let letters = [];
if (op.type === 'text') {
if (!op.value || op.value.length <= 1) {
@@ -415,7 +421,7 @@ AutoTypeRunner.prototype.obfuscateOp = function(op) {
}
letters = op.value.split('');
} else {
op.value.forEach(grOp => letters.push(...grOp.value.split('')));
op.value.forEach((grOp) => letters.push(...grOp.value.split('')));
}
if (letters.length <= 1) {
return;
@@ -425,8 +431,8 @@ AutoTypeRunner.prototype.obfuscateOp = function(op) {
op.type = 'group';
};
AutoTypeRunner.prototype.run = function(callback) {
this.emitter = AutoTypeEmitterFactory.create(this.emitNext.bind(this));
AutoTypeRunner.prototype.run = function (callback, windowId) {
this.emitter = new AutoTypeEmitter(this.emitNext.bind(this), windowId);
this.emitterState = {
callback,
stack: [],
@@ -436,10 +442,10 @@ AutoTypeRunner.prototype.run = function(callback) {
activeMod: {},
finished: null
};
this.emitNext();
this.emitter.begin();
};
AutoTypeRunner.prototype.emitNext = function(err) {
AutoTypeRunner.prototype.emitNext = function (err) {
if (err) {
this.emitterState.finished = true;
this.emitterState.callback(err);
@@ -512,8 +518,8 @@ AutoTypeRunner.prototype.emitNext = function(err) {
}
};
AutoTypeRunner.prototype.setEmitterMod = function(addedMod) {
Object.keys(addedMod).forEach(function(mod) {
AutoTypeRunner.prototype.setEmitterMod = function (addedMod) {
Object.keys(addedMod).forEach(function (mod) {
if (addedMod[mod] && !this.emitterState.activeMod[mod]) {
emitterLogger.debug('mod', mod, true);
this.emitter.setMod(mod, true);
@@ -522,8 +528,8 @@ AutoTypeRunner.prototype.setEmitterMod = function(addedMod) {
}, this);
};
AutoTypeRunner.prototype.resetEmitterMod = function(targetState) {
Object.keys(this.emitterState.activeMod).forEach(function(mod) {
AutoTypeRunner.prototype.resetEmitterMod = function (targetState) {
Object.keys(this.emitterState.activeMod).forEach(function (mod) {
if (this.emitterState.activeMod[mod] && !targetState[mod]) {
emitterLogger.debug('mod', mod, false);
this.emitter.setMod(mod, false);
@@ -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,147 +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) {
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('keyup ctrl alt shift t');
Object.keys(this.mod).forEach(mod => {
this.pendingScript.push('keydown ' + ModMap[mod]);
});
text.split('').forEach(char => {
this.pendingScript.push('key U' + char.charCodeAt(0).toString(16));
});
Object.keys(this.mod).forEach(mod => {
this.pendingScript.push('keyup ' + ModMap[mod]);
});
this.waitComplete();
};
AutoTypeEmitter.prototype.key = function(key) {
if (typeof key !== 'number') {
if (!KeyMap[key]) {
return this.callback('Bad key: ' + key);
}
key = KeyMap[key].toString(16);
}
this.pendingScript.push('key --clearmodifiers ' + this.modString() + key);
this.callback();
};
AutoTypeEmitter.prototype.copyPaste = function(text) {
this.pendingScript.push('sleep 0.5');
Launcher.setClipboardText(text);
this.pendingScript.push('key --clearmodifiers 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) {
Launcher.spawn({
cmd: 'xdotool',
args: ['-'],
data: script,
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 };
+95 -62
View File
@@ -1,39 +1,38 @@
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(appModel) {
init() {
if (!this.enabled) {
return;
}
this.appModel = appModel;
Events.on('auto-type', e => this.handleEvent(e));
Events.on('main-window-blur', e => this.resetPendingEvent(e));
Events.on('main-window-will-close', e => this.resetPendingEvent(e));
Events.on('closed-open-view', e => this.processPendingEvent(e));
Events.on('auto-type', (e) => this.handleEvent(e));
},
handleEvent(e) {
const entry = (e && e.entry) || null;
const sequence = (e && e.sequence) || null;
const context = (e && e.context) || null;
logger.debug('Auto type event', entry);
if (this.running) {
logger.debug('Already running, skipping event');
@@ -41,7 +40,7 @@ const AutoType = {
}
if (entry) {
this.hideWindow(() => {
this.runAndHandleResult({ entry, sequence });
this.runAndHandleResult({ entry, sequence, context });
});
} else {
if (this.selectEntryView) {
@@ -58,12 +57,19 @@ const AutoType = {
}
},
runAndHandleResult(result) {
this.run(result, err => {
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
});
}
});
@@ -73,16 +79,17 @@ const AutoType = {
}
},
run(result, callback) {
run(result, windowId, callback) {
this.running = true;
const sequence = result.sequence || result.entry.getEffectiveAutoTypeSeq();
const context = result.context;
logger.debug('Start', sequence);
const ts = logger.ts();
try {
const parser = new AutoTypeParser(sequence);
const runner = parser.parse();
logger.debug('Parsed', this.printOps(runner.ops));
runner.resolve(result.entry, err => {
runner.resolve(result.entry, context, (err) => {
if (err) {
this.running = false;
logger.error('Resolve error', err);
@@ -99,7 +106,7 @@ const AutoType = {
}
logger.debug('Obfuscated');
}
runner.run(err => {
runner.run((err) => {
this.running = false;
if (err) {
logger.error('Run error', err);
@@ -107,7 +114,7 @@ const AutoType = {
}
logger.debug('Complete', logger.ts(ts));
return callback && callback();
});
}, windowId);
});
} catch (ex) {
this.running = false;
@@ -120,7 +127,7 @@ const AutoType = {
try {
const parser = new AutoTypeParser(sequence);
const runner = parser.parse();
runner.resolve(entry, callback);
runner.resolve(entry, null, callback);
} catch (ex) {
return callback(ex);
}
@@ -157,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;
}
@@ -185,14 +191,14 @@ const AutoType = {
logger.debug('Error during active window check, something is wrong', err);
return callback(false);
}
if (activeWindowInfo.id !== windowInfo.id) {
if (activeWindowInfo.id !== windowInfo.id && !this.supportsEventsWithWindowId) {
logger.info(
`Active window doesn't match: ID is different. ` +
`Expected ${windowInfo.id}, got ${activeWindowInfo.id}`
);
return callback(false, activeWindowInfo);
}
if (activeWindowInfo.url !== windowInfo.url) {
if (activeWindowInfo.url !== windowInfo.url && !this.supportsEventsWithWindowId) {
logger.info(
`Active window doesn't match: url is different. ` +
`Expected "${windowInfo.url}", got "${activeWindowInfo.url}"`
@@ -205,74 +211,101 @@ const AutoType = {
},
selectEntryAndRun() {
this.getActiveWindowInfo((e, windowInfo) => {
const filter = new AutoTypeFilter(windowInfo, this.appModel);
this.getActiveWindowInfo(async (e, windowInfo) => {
const filter = new SelectEntryFilter(
windowInfo,
AppModel.instance,
AppModel.instance.files,
{
autoType: true
}
);
const evt = { filter, windowInfo };
if (!this.appModel.files.hasOpenFiles()) {
this.pendingEvent = evt;
if (!AppModel.instance.files.hasOpenFiles()) {
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] });
this.runAndHandleResult({ entry: entries[0] }, evt.windowInfo.id);
});
return;
}
this.focusMainWindow();
evt.filter.ignoreWindowInfo = true;
this.selectEntryView = new AutoTypeSelectView({ filter: evt.filter });
this.selectEntryView.on('result', result => {
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);
this.runAndHandleResult(result, evt.windowInfo.id);
}
});
}
});
});
this.selectEntryView.render();
this.selectEntryView.on('show-open-files', () => {
this.selectEntryView.on('show-open-files', async () => {
this.selectEntryView.hide();
Events.emit('open-file');
});
},
resetPendingEvent() {
if (this.pendingEvent) {
this.pendingEvent = null;
logger.debug('auto-type event cancelled');
}
},
processPendingEvent() {
if (this.selectEntryView) {
try {
await AppModel.instance.unlockAnyFile('autoTypeUnlockMessage');
} catch {
this.selectEntryView.emit('result', undefined);
return;
}
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();
});
}
};
+6 -6
View File
@@ -1,23 +1,23 @@
import { Collection } from 'framework/collection';
import { FileModel } from 'models/file-model';
import { Model } from 'framework/model';
class FileCollection extends Collection {
static model = FileModel;
static model = Model;
hasOpenFiles() {
return this.some(file => file.active);
return this.some((file) => file.active);
}
hasUnsavedFiles() {
return this.some(file => file.modified);
return this.some((file) => file.modified);
}
hasDirtyFiles() {
return this.some(file => file.dirty);
return this.some((file) => file.dirty);
}
getByName(name) {
return this.find(file => file.name.toLowerCase() === name.toLowerCase());
return this.find((file) => file.name.toLowerCase() === name.toLowerCase());
}
}
@@ -6,7 +6,7 @@ class FileInfoCollection extends Collection {
static model = FileInfoModel;
load() {
return SettingsStore.load('file-info').then(data => {
return SettingsStore.load('file-info').then((data) => {
if (data) {
for (const item of data) {
this.push(new FileInfoModel(item));
@@ -20,7 +20,7 @@ class FileInfoCollection extends Collection {
}
getMatch(storage, name, path) {
return this.find(fi => {
return this.find((fi) => {
return (
(fi.storage || '') === (storage || '') &&
(fi.name || '') === (name || '') &&
@@ -30,7 +30,7 @@ class FileInfoCollection extends Collection {
}
getByName(name) {
return this.find(file => file.name.toLowerCase() === name.toLowerCase());
return this.find((file) => file.name.toLowerCase() === name.toLowerCase());
}
}
+10 -9
View File
@@ -17,7 +17,7 @@ const AppRightsChecker = {
if (!Launcher.getAppPath().startsWith(this.AppPath)) {
return;
}
this.needRunInstaller(needRun => {
this.needRunInstaller((needRun) => {
if (needRun) {
this.showAlert();
this.runInstaller();
@@ -26,7 +26,7 @@ const AppRightsChecker = {
},
needRunInstaller(callback) {
Launcher.statFile(this.AppPath, stat => {
Launcher.statFile(this.AppPath, (stat) => {
const folderIsRoot = stat && stat.uid === 0;
callback(!folderIsRoot);
});
@@ -38,15 +38,15 @@ const AppRightsChecker = {
icon: 'lock',
header: Locale.appRightsAlert,
body:
Locale.appRightsAlertBody1.replace('{}', `<code>${this.AppPath}</code>`) +
'<br/>' +
Locale.appRightsAlertBody2 +
`: <pre>${command}</pre>`,
Locale.appRightsAlertBody1.replace('{}', this.AppPath) +
'\n' +
Locale.appRightsAlertBody2,
pre: command,
buttons: [
{ result: 'skip', title: Locale.alertDoNotAsk, error: true },
Alerts.buttons.ok
],
success: result => {
success: (result) => {
if (result === 'skip') {
this.dontAskAnymore();
}
@@ -58,8 +58,9 @@ const AppRightsChecker = {
runInstaller() {
Launcher.spawn({
cmd: this.AppPath + '/Contents/Installer/KeeWeb Installer.app/Contents/MacOS/applet',
args: ['--install'],
complete: () => {
this.needRunInstaller(needRun => {
this.needRunInstaller((needRun) => {
if (this.alert && !needRun) {
this.alert.closeWithResult('cancel');
}
@@ -69,7 +70,7 @@ const AppRightsChecker = {
},
dontAskAnymore() {
this.needRunInstaller(needRun => {
this.needRunInstaller((needRun) => {
if (needRun) {
AppSettingsModel.skipFolderRightsWarning = true;
}
@@ -0,0 +1,194 @@
import { Events } from 'framework/events';
import { Logger } from 'util/logger';
import { YubiKey } from 'comp/app/yubikey';
import { UsbListener } from 'comp/app/usb-listener';
import { Alerts } from 'comp/ui/alerts';
import { Locale } from 'util/locale';
import { Timeouts } from 'const/timeouts';
import { Launcher } from 'comp/launcher';
import { AppSettingsModel } from 'models/app-settings-model';
const logger = new Logger('chal-resp');
const ChalRespCalculator = {
cache: {},
getCacheKey(params) {
return `${params.vid}:${params.pid}:${params.serial}:${params.slot}`;
},
build(params) {
if (!params) {
return null;
}
return (challenge) => {
return new Promise((resolve, reject) => {
challenge = Buffer.from(challenge);
const hexChallenge = challenge.toString('hex');
const cacheKey = this.getCacheKey(params);
const respFromCache = this.cache[cacheKey]?.[hexChallenge];
if (respFromCache) {
logger.debug('Found ChalResp in cache');
return resolve(Buffer.from(respFromCache, 'hex'));
}
if (!AppSettingsModel.enableUsb) {
logger.debug('USB is disabled');
Alerts.error({
header: Locale.yubiKeyDisabledErrorHeader,
body: Locale.yubiKeyDisabledErrorBody,
complete() {
const err = new Error(Locale.yubiKeyDisabledErrorHeader);
err.userCanceled = true;
err.ykError = true;
reject(err);
}
});
return;
}
logger.debug('Calculating ChalResp using a YubiKey', params);
this._calc(params, challenge, (err, response) => {
if (err) {
return reject(err);
}
resolve(response);
});
});
};
},
_calc(params, challenge, callback) {
let touchAlert = null;
let userCanceled = false;
YubiKey.calculateChalResp(params, challenge, (err, response) => {
if (userCanceled) {
userCanceled = false;
return;
}
if (touchAlert) {
touchAlert.closeWithoutResult();
touchAlert = null;
}
if (err) {
if (err.noKey) {
logger.info('YubiKey ChalResp: no key');
this._showNoKeyAlert(params.serial, (err) => {
if (err) {
return callback(err);
}
this._calc(params, challenge, callback);
});
return;
} else if (err.touchRequested) {
logger.info('YubiKey ChalResp: touch requested');
touchAlert = this._showTouchAlert(params.serial, (err) => {
touchAlert = null;
userCanceled = true;
logger.info('YubiKey ChalResp canceled');
YubiKey.cancelChalResp();
callback(err);
});
return;
} else {
logger.error('YubiKey ChalResp error', err);
}
return callback(err);
}
const cacheKey = this.getCacheKey(params);
if (!this.cache[cacheKey]) {
this.cache[cacheKey] = {};
}
const hexChallenge = challenge.toString('hex');
this.cache[cacheKey][hexChallenge] = response.toString('hex');
logger.info('Calculated YubiKey ChalResp');
callback(null, response);
});
},
_showNoKeyAlert(serial, callback) {
Launcher.showMainWindow();
let noKeyAlert = null;
let deviceEnumerationTimer;
const onUsbDevicesChanged = () => {
if (!UsbListener.attachedYubiKeys) {
return;
}
deviceEnumerationTimer = setTimeout(() => {
YubiKey.list((err, list) => {
if (err) {
logger.error('YubiKey list error', err);
return;
}
const isAttached = list.some((yk) => yk.serial === serial);
logger.info(isAttached ? 'YubiKey found' : 'YubiKey not found');
if (isAttached) {
Events.off('usb-devices-changed', onUsbDevicesChanged);
if (noKeyAlert) {
noKeyAlert.closeWithoutResult();
}
callback();
}
});
}, Timeouts.ExternalDeviceAfterReconnect);
};
Events.on('usb-devices-changed', onUsbDevicesChanged);
noKeyAlert = Alerts.alert({
header: Locale.yubiKeyNoKeyHeader,
body: Locale.yubiKeyNoKeyBody.replace('{}', serial),
buttons: [Alerts.buttons.cancel],
icon: 'usb-token',
cancel: () => {
logger.info('No key alert closed');
clearTimeout(deviceEnumerationTimer);
Events.off('usb-devices-changed', onUsbDevicesChanged);
const err = new Error('User canceled the YubiKey no key prompt');
err.userCanceled = true;
err.ykError = true;
return callback(err);
}
});
},
_showTouchAlert(serial, callback) {
Launcher.showMainWindow();
return Alerts.alert({
header: Locale.yubiKeyTouchRequestedHeader,
body: Locale.yubiKeyTouchRequestedBody.replace('{}', serial),
buttons: [Alerts.buttons.cancel],
icon: 'usb-token',
cancel: () => {
logger.info('Touch alert closed');
const err = new Error('User canceled the YubiKey touch prompt');
err.userCanceled = true;
err.ykError = true;
return callback(err);
}
});
},
clearCache(params) {
delete this.cache[this.getCacheKey(params)];
}
};
export { ChalRespCalculator };
+8 -8
View File
@@ -3,19 +3,19 @@ import { UrlFormat } from 'util/formatting/url-format';
const ChooserAppKey = 'qp7ctun6qt5n9d6';
const DropboxChooser = function(callback) {
const DropboxChooser = function (callback) {
this.cb = callback;
this.onMessage = this.onMessage.bind(this);
};
DropboxChooser.prototype.callback = function(err, res) {
DropboxChooser.prototype.callback = function (err, res) {
if (this.cb) {
this.cb(err, res);
}
this.cb = null;
};
DropboxChooser.prototype.choose = function() {
DropboxChooser.prototype.choose = function () {
const windowFeatures = 'width=640,height=552,left=357,top=100,resizable=yes,location=yes';
const url = this.buildUrl();
this.popup = window.open(url, 'dropbox', windowFeatures);
@@ -26,7 +26,7 @@ DropboxChooser.prototype.choose = function() {
this.closeInt = setInterval(this.checkClose.bind(this), 200);
};
DropboxChooser.prototype.buildUrl = function() {
DropboxChooser.prototype.buildUrl = function () {
return UrlFormat.makeUrl('https://www.dropbox.com/chooser', {
origin: window.location.protocol + '//' + window.location.host,
'app_key': AppSettingsModel.dropboxAppKey || ChooserAppKey,
@@ -40,7 +40,7 @@ DropboxChooser.prototype.buildUrl = function() {
});
};
DropboxChooser.prototype.onMessage = function(e) {
DropboxChooser.prototype.onMessage = function (e) {
if (e.source !== this.popup || e.origin !== 'https://www.dropbox.com') {
return;
}
@@ -69,7 +69,7 @@ DropboxChooser.prototype.onMessage = function(e) {
}
};
DropboxChooser.prototype.checkClose = function() {
DropboxChooser.prototype.checkClose = function () {
if (this.popup.closed) {
clearInterval(this.closeInt);
window.removeEventListener('message', this.onMessage);
@@ -79,7 +79,7 @@ DropboxChooser.prototype.checkClose = function() {
}
};
DropboxChooser.prototype.success = function(params) {
DropboxChooser.prototype.success = function (params) {
if (!params || !params[0] || !params[0].link || params[0].is_dir) {
return this.callback('bad result');
}
@@ -87,7 +87,7 @@ DropboxChooser.prototype.success = function(params) {
this.readFile(this.result.link);
};
DropboxChooser.prototype.readFile = function(url) {
DropboxChooser.prototype.readFile = function (url) {
const xhr = new XMLHttpRequest();
xhr.addEventListener('load', () => {
this.callback(null, { name: this.result.name, data: xhr.response });
+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];
}
}
};
+19 -7
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,
@@ -67,16 +79,16 @@ const GeneratorPresets = {
get all() {
let presets = this.builtIn;
presets.forEach(preset => {
presets.forEach((preset) => {
preset.builtIn = true;
});
const setting = AppSettingsModel.generatorPresets;
if (setting) {
if (setting.user) {
presets = presets.concat(setting.user.map(item => ({ ...item })));
presets = presets.concat(setting.user.map((item) => ({ ...item })));
}
let hasDefault = false;
presets.forEach(preset => {
presets.forEach((preset) => {
if (setting.disabled && setting.disabled[preset.name]) {
preset.disabled = true;
}
@@ -93,7 +105,7 @@ const GeneratorPresets = {
},
get enabled() {
const allPresets = this.all.filter(preset => !preset.disabled);
const allPresets = this.all.filter((preset) => !preset.disabled);
if (!allPresets.length) {
allPresets.push(this.defaultPreset);
}
@@ -110,7 +122,7 @@ const GeneratorPresets = {
add(preset) {
const setting = this.getOrCreateSetting();
if (preset.name && !setting.user.filter(p => p.name === preset.name).length) {
if (preset.name && !setting.user.filter((p) => p.name === preset.name).length) {
setting.user.push(preset);
this.save(setting);
}
@@ -118,13 +130,13 @@ const GeneratorPresets = {
remove(name) {
const setting = this.getOrCreateSetting();
setting.user = setting.user.filter(p => p.name !== name);
setting.user = setting.user.filter((p) => p.name !== name);
this.save(setting);
},
setPreset(name, props) {
const setting = this.getOrCreateSetting();
const preset = setting.user.filter(p => p.name === name)[0];
const preset = setting.user.filter((p) => p.name === name)[0];
if (preset) {
Object.assign(preset, props);
this.save(setting);
@@ -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 };
+2 -2
View File
@@ -56,7 +56,7 @@ const Shortcuts = {
}
return shortcutValue
.split(/\+/g)
.map(part => {
.map((part) => {
switch (part) {
case 'Ctrl':
return this.ctrlShortcutSymbol(formatting);
@@ -85,7 +85,7 @@ const Shortcuts = {
return Features.isMac ? '⌃' : this.formatShortcut(Locale.ctrlKey, formatting);
},
formatShortcut(shortcut, formatting) {
return formatting ? `<span class="thin">${shortcut} + </span>` : `${shortcut}+`;
return formatting ? `${shortcut} + ` : `${shortcut}+`;
},
globalShortcutText(type, formatting) {
return this.presentShortcut(this.globalShortcut(type), formatting);
+10 -12
View File
@@ -19,22 +19,20 @@ const StartProfiler = {
operations.unshift({ name: 'fetching', elapsed: networkTime });
const time = Math.round(performance.now());
const details = operations.map(op => `${op.name}=${Math.round(op.elapsed)}ms`).join(', ');
let message = `Started in ${time}ms: ${details}.`;
if (this.appProfile) {
message += ` Electron app started in ${this.appProfile.totalTime}ms: `;
message +=
this.appProfile.timings
.map(op => `${op.name}=${Math.round(op.elapsed)}ms`)
.join(', ') + '.';
}
logger.info(message);
this.printReport('App', operations, time);
},
reportAppProfile(data) {
this.appProfile = data;
this.printReport('Electron app', data.timings, data.totalTime);
},
printReport(name, operations, totalTime) {
const message =
`${name} started in ${totalTime}ms: ` +
operations.map((op) => `${op.name}=${Math.round(op.elapsed)}ms`).join(', ');
logger.info(message);
},
getNetworkTime() {
+113 -85
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
);
},
@@ -43,7 +43,7 @@ const Updater = {
if (!Launcher && navigator.serviceWorker && !RuntimeInfo.beta && !RuntimeInfo.devMode) {
navigator.serviceWorker
.register('service-worker.js')
.then(reg => {
.then((reg) => {
logger.info('Service worker registered');
reg.addEventListener('updatefound', () => {
if (reg.active) {
@@ -52,7 +52,7 @@ const Updater = {
}
});
})
.catch(e => {
.catch((e) => {
logger.error('Failed to register a service worker', e);
});
}
@@ -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();
@@ -145,7 +143,7 @@ const Updater = {
UpdateModel.set({ updateStatus: 'found' });
}
},
error: e => {
error: (e) => {
logger.error('Update check error', e);
UpdateModel.set({
status: 'error',
@@ -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);
}
};
+69
View File
@@ -0,0 +1,69 @@
import { Events } from 'framework/events';
import { Logger } from 'util/logger';
import { NativeModules } from 'comp/launcher/native-modules';
import { AppSettingsModel } from 'models/app-settings-model';
import { Features } from 'util/features';
const logger = new Logger('usb-listener');
const UsbListener = {
supported: Features.isDesktop,
attachedYubiKeys: 0,
init() {
if (!this.supported) {
return;
}
Events.on('native-modules-yubikeys', (e) => {
if (e.numYubiKeys !== this.attachedYubiKeys) {
logger.debug(`YubiKeys changed ${this.attachedYubiKeys} => ${e.numYubiKeys}`);
this.attachedYubiKeys = e.numYubiKeys;
Events.emit('usb-devices-changed');
}
});
AppSettingsModel.on('change:enableUsb', (model, enabled) => {
if (enabled) {
this.start();
} else {
this.stop();
}
});
if (AppSettingsModel.enableUsb) {
this.start();
}
},
start() {
logger.info('Starting USB listener');
if (this.usb) {
this.stop();
}
try {
NativeModules.startUsbListener();
} catch (e) {
logger.error('Error starting USB listener', e);
}
},
stop() {
logger.info('Stopping USB listener');
try {
NativeModules.stopUsbListener();
} catch (e) {
logger.error('Error stopping USB listener', e);
}
if (this.attachedYubiKeys) {
this.attachedYubiKeys = 0;
Events.emit('usb-devices-changed');
}
}
};
export { UsbListener };
+291
View File
@@ -0,0 +1,291 @@
import { Events } from 'framework/events';
import { Launcher } from 'comp/launcher';
import { NativeModules } from 'comp/launcher/native-modules';
import { Logger } from 'util/logger';
import { UsbListener } from 'comp/app/usb-listener';
import { AppSettingsModel } from 'models/app-settings-model';
import { Timeouts } from 'const/timeouts';
import { YubiKeyProductIds, YubiKeyChallengeSize } from 'const/hardware';
import { Locale } from 'util/locale';
const logger = new Logger('yubikey');
const YubiKey = {
ykmanStatus: undefined,
process: null,
aborted: false,
cmd() {
if (this._cmd) {
return this._cmd;
}
const macYkmanPath = '/usr/local/bin/ykman';
if (Launcher.platform() === 'darwin' && Launcher.fileExistsSync(macYkmanPath)) {
this._cmd = macYkmanPath;
} else {
this._cmd = 'ykman';
}
return this._cmd;
},
checkToolStatus() {
if (this.ykmanStatus === 'ok') {
return Promise.resolve(this.ykmanStatus);
}
return new Promise((resolve) => {
this.ykmanStatus = 'checking';
this._cmd = undefined;
Launcher.spawn({
cmd: this.cmd(),
args: ['-v'],
noStdOutLogging: true,
complete: (err, stdout, code) => {
if (err || code !== 0) {
this.ykmanStatus = 'error';
} else {
this.ykmanStatus = 'ok';
}
resolve(this.ykmanStatus);
}
});
});
},
abort() {
logger.info('Aborting');
if (this.process) {
logger.info('Killing the process');
try {
this.process.kill();
} catch {}
}
this.aborted = true;
this.process = null;
},
list(callback) {
NativeModules.getYubiKeys({})
.then((yubiKeys) => {
yubiKeys = yubiKeys.map(({ serial, vid, pid, version, slots }) => {
return {
vid,
pid,
serial,
slots,
fullName: this.getKeyFullName(pid, version, serial)
};
});
callback(null, yubiKeys);
})
.catch(callback);
},
getKeyFullName(pid, version, serial) {
let name = 'YubiKey';
if (YubiKeyProductIds.Gen1.includes(pid)) {
name += ' Gen 1';
} else if (YubiKeyProductIds.NEO.includes(pid)) {
name += ' NEO';
} else if (YubiKeyProductIds.YK4.includes(pid)) {
if (version >= '5.1.0') {
name += ' 5';
}
}
return `${name} ${serial}`;
},
listWithYkman(callback) {
this._listWithYkman(callback, true);
},
_listWithYkman(callback, canRetry) {
if (this.process) {
return callback('Already in progress');
}
this.aborted = false;
logger.info('Listing YubiKeys');
if (!UsbListener.attachedYubiKeys) {
return callback(null, []);
}
this.process = Launcher.spawn({
cmd: this.cmd(),
args: ['list'],
noStdOutLogging: true,
complete: (err, stdout) => {
this.process = null;
if (this.aborted) {
return callback('Aborted');
}
if (err) {
return callback(err);
}
const yubiKeysIncludingEmpty = stdout
.trim()
.split(/\n/g)
.map((line) => {
const fullName = line;
const serial = (line.match(/\d{5,}$/g) || [])[0];
return { fullName, serial };
});
const yubiKeys = yubiKeysIncludingEmpty.filter((s) => s.serial);
if (
yubiKeysIncludingEmpty.length === 1 &&
yubiKeys.length === 0 &&
stdout.startsWith('YubiKey') &&
stdout.includes('CCID') &&
!stdout.includes('Serial')
) {
logger.info('The YubiKey is probably stuck');
if (!AppSettingsModel.yubiKeyStuckWorkaround) {
return callback(Locale.yubiKeyStuckError);
}
if (canRetry) {
return this._repairStuckYubiKey(callback);
}
}
if (!yubiKeys.length) {
return callback('No YubiKeys returned by "ykman list"');
}
callback(null, yubiKeys);
}
});
},
_repairStuckYubiKey(callback) {
logger.info('Repairing a stuck YubiKey');
let openTimeout;
const countYubiKeys = UsbListener.attachedYubiKeys;
const onDevicesChangedDuringRepair = () => {
if (UsbListener.attachedYubiKeys === countYubiKeys) {
logger.info('YubiKey was reconnected');
Events.off('usb-devices-changed', onDevicesChangedDuringRepair);
clearTimeout(openTimeout);
this.aborted = false;
setTimeout(() => {
this._listWithYkman(callback, false);
}, Timeouts.ExternalDeviceAfterReconnect);
}
};
Events.on('usb-devices-changed', onDevicesChangedDuringRepair);
Launcher.spawn({
cmd: this.cmd(),
args: ['config', 'usb', '-e', 'oath', '-f'],
noStdOutLogging: true,
complete: (err) => {
logger.info('Repair complete', err ? 'with error' : 'OK');
if (err) {
Events.off('usb-devices-changed', onDevicesChangedDuringRepair);
return callback(`YubiKey repair error: ${err}`);
}
openTimeout = setTimeout(() => {
Events.off('usb-devices-changed', onDevicesChangedDuringRepair);
}, Timeouts.ExternalDeviceReconnect);
}
});
},
getOtpCodes(serial, callback) {
if (this.process) {
return callback('Already in progress');
}
this.aborted = false;
this.process = Launcher.spawn({
cmd: this.cmd(),
args: ['-d', serial, 'oath', 'accounts', 'code'],
noStdOutLogging: true,
throwOnStdErr: true,
complete: (err, stdout) => {
this.process = null;
if (this.aborted) {
return callback('Aborted');
}
if (err) {
return callback(err);
}
const codes = [];
for (const line of stdout.split('\n')) {
const match = line.match(/^(.*?):(.*?)\s+(.*)$/);
if (!match) {
continue;
}
const [, title, user, code] = match;
const needsTouch = !code.match(/^\d+$/);
codes.push({
title,
user,
needsTouch
});
}
callback(null, codes);
}
});
},
getOtp(serial, entry, callback) {
return Launcher.spawn({
cmd: this.cmd(),
args: ['-d', serial, 'oath', 'accounts', 'code', '--single', entry],
noStdOutLogging: true,
complete: (err, stdout) => {
if (err) {
return callback(err);
}
const otp = stdout.trim();
callback(null, otp);
}
});
},
calculateChalResp(chalResp, challenge, callback) {
const { vid, pid, serial, slot } = chalResp;
const yubiKey = { vid, pid, serial };
challenge = Buffer.from(challenge);
// https://github.com/Yubico/yubikey-personalization-gui/issues/86
// https://github.com/keepassxreboot/keepassxc/blob/develop/src/keys/drivers/YubiKey.cpp#L318
const padLen = YubiKeyChallengeSize - challenge.byteLength;
const paddedChallenge = Buffer.alloc(YubiKeyChallengeSize, padLen);
challenge.copy(paddedChallenge);
NativeModules.yubiKeyChallengeResponse(
yubiKey,
[...paddedChallenge],
slot,
(err, result) => {
if (result) {
result = Buffer.from(result);
}
if (err) {
err.ykError = true;
}
return callback(err, result);
}
);
},
cancelChalResp() {
NativeModules.yubiKeyCancelChallengeResponse();
}
};
export { YubiKey };
-31
View File
@@ -1,31 +0,0 @@
import { Features } from 'util/features';
const AuthReceiver = {
receive() {
if (!Features.isPopup) {
return false;
}
const opener = window.opener || window.parent;
const message = this.urlArgsToMessage(window.location.href);
const hasKeys = Object.keys(message).filter(key => key !== 'config').length > 0;
if (!hasKeys) {
return false;
}
opener.postMessage(message, window.location.origin);
window.close();
return true;
},
urlArgsToMessage(url) {
const message = {};
url.split(/[?#&]/g).forEach(part => {
const parts = part.split('=');
if (parts.length === 2) {
message[parts[0]] = decodeURIComponent(parts[1]);
}
});
return message;
}
};
export { AuthReceiver };
+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');
}
},
+1 -1
View File
@@ -44,7 +44,7 @@ class KeyHandler {
offKey(key, handler, thisArg) {
if (this.shortcuts[key]) {
this.shortcuts[key] = this.shortcuts[key].filter(
sh => sh.handler !== handler || sh.thisArg !== thisArg
(sh) => sh.handler !== handler || sh.thisArg !== thisArg
);
}
}
+19 -3
View File
@@ -16,7 +16,7 @@ const PopupNotifier = {
window.open = noop;
} else {
const windowOpen = window.open;
window.open = function(...args) {
window.open = function (...args) {
const win = windowOpen.apply(window, args);
if (win) {
PopupNotifier.deferCheckClosed(win);
@@ -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 });
}
};
+10 -9
View File
@@ -1,6 +1,6 @@
import kdbxweb from 'kdbxweb';
import * as kdbxweb from 'kdbxweb';
const SecureInput = function() {
const SecureInput = function () {
this.el = null;
this.minChar = 0x1400 + Math.round(Math.random() * 100);
this.maxLen = 1024;
@@ -9,13 +9,13 @@ const SecureInput = function() {
this.salt = new Uint32Array(0);
};
SecureInput.prototype.setElement = function(el) {
SecureInput.prototype.setElement = function (el) {
this.el = el;
this.el.val(this.pseudoValue);
this.el.on('input', this._input.bind(this));
};
SecureInput.prototype.reset = function() {
SecureInput.prototype.reset = function () {
this.el = null;
this.length = 0;
this.pseudoValue = '';
@@ -28,7 +28,7 @@ SecureInput.prototype.reset = function() {
this.salt = new Uint32Array(0);
};
SecureInput.prototype._input = function() {
SecureInput.prototype._input = function () {
const selStart = this.el[0].selectionStart;
const value = this.el.val();
let newPs = '';
@@ -63,11 +63,11 @@ SecureInput.prototype._input = function() {
this.el[0].selectionEnd = selStart;
};
SecureInput.prototype._getChar = function(ix) {
SecureInput.prototype._getChar = function (ix) {
return String.fromCharCode(this.minChar + ix);
};
SecureInput.prototype._isSpecialChar = function(ch) {
SecureInput.prototype._isSpecialChar = function (ch) {
return ch >= this.minChar && ch <= this.minChar + this.maxLen;
};
@@ -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 };
+32 -7
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) {
@@ -27,7 +45,7 @@ const Transport = {
logger.info('GET ' + config.url);
const opts = Launcher.req('url').parse(config.url);
opts.headers = { 'User-Agent': navigator.userAgent };
Launcher.resolveProxy(config.url, proxy => {
Launcher.resolveProxy(config.url, (proxy) => {
logger.info(
'Request to ' +
config.url +
@@ -41,7 +59,7 @@ const Transport = {
opts.path = config.url;
}
Launcher.req(proto)
.get(opts, res => {
.get(opts, (res) => {
logger.info('Response from ' + config.url + ': ' + res.statusCode);
if (res.statusCode === 200) {
if (config.file) {
@@ -52,19 +70,26 @@ const Transport = {
config.success(tmpFile);
});
});
file.on('error', err => {
file.on('error', (err) => {
config.error(err);
});
} else {
let data = [];
res.on('data', chunk => {
res.on('data', (chunk) => {
data.push(chunk);
});
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);
});
}
@@ -79,7 +104,7 @@ const Transport = {
config.error('HTTP status ' + res.statusCode);
}
})
.on('error', e => {
.on('error', (e) => {
logger.error('Cannot GET ' + config.url, e);
if (tmpFile) {
fs.unlink(tmpFile, noop);
@@ -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 };
+17 -26
View File
@@ -1,12 +1,10 @@
/* 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 { MdToHtml } from 'util/formatting/md-to-html';
import { DateFormat } from 'comp/i18n/date-format';
import { StringFormat } from 'util/formatting/string-format';
import { Locale } from 'util/locale';
import { AppSettingsModel } from 'models/app-settings-model';
const Templates = {
db: require('templates/export/db.hbs'),
@@ -17,7 +15,7 @@ const FieldMapping = [
{ name: 'UserName', locStr: 'user' },
{ name: 'Password', locStr: 'password', protect: true },
{ name: 'URL', locStr: 'website' },
{ name: 'Notes', locStr: 'notes', markdown: true }
{ name: 'Notes', locStr: 'notes' }
];
const KnownFields = { 'Title': true };
@@ -33,41 +31,34 @@ function walkGroup(db, group, parents) {
) {
return '';
}
const self = group.entries.map(entry => walkEntry(db, entry, parents)).join('\n');
const children = group.groups.map(childGroup => walkGroup(db, childGroup, parents)).join('\n');
const self = group.entries.map((entry) => walkEntry(db, entry, parents)).join('\n');
const children = group.groups
.map((childGroup) => walkGroup(db, childGroup, parents))
.join('\n');
return self + children;
}
function walkEntry(db, entry, parents) {
const path = parents.map(group => group.name).join(' / ');
const path = parents.map((group) => group.name).join(' / ');
const fields = [];
for (const field of FieldMapping) {
let value = entryField(entry, field.name);
const value = entryField(entry, field.name);
if (value) {
let html = false;
if (field.markdown && AppSettingsModel.useMarkdown) {
const converted = MdToHtml.convert(value);
if (converted.html) {
value = converted.html;
html = true;
}
}
fields.push({
title: StringFormat.capFirst(Locale[field.locStr]),
value,
protect: field.protect,
html
protect: field.protect
});
}
}
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
});
}
}
@@ -78,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;
@@ -89,7 +80,7 @@ function walkEntry(db, entry, parents) {
}
return { name, data };
})
.filter(att => att.name && att.data);
.filter((att) => att.name && att.data);
return Templates.entry({
path,
@@ -104,19 +95,19 @@ 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 || '';
}
const KdbxToHtml = {
convert(db, options) {
const content = db.groups.map(group => walkGroup(db, group, [])).join('\n');
const content = db.groups.map((group) => walkGroup(db, group, [])).join('\n');
return Templates.db({
name: options.name,
date: DateFormat.dtStr(Date.now()),
appLink: Links.Homepage,
appVersion: RuntimeInfo.version,
content
contentHtml: content
});
},
+7 -17
View File
@@ -6,7 +6,6 @@ import { Otp } from 'util/data/otp';
import { Features } from 'util/features';
import { Locale } from 'util/locale';
import { Logger } from 'util/logger';
import { escape } from 'util/fn';
const logger = new Logger('otp-qr-reader');
@@ -22,17 +21,11 @@ class OtpQrReader {
read() {
let screenshotKey = Shortcuts.screenshotToClipboardShortcut();
if (screenshotKey) {
screenshotKey = Locale.detSetupOtpAlertBodyWith.replace(
'{}',
'<code>' + screenshotKey + '</code>'
);
screenshotKey = Locale.detSetupOtpAlertBodyWith.replace('{}', screenshotKey);
}
const pasteKey = Features.isMobile
? ''
: Locale.detSetupOtpAlertBodyWith.replace(
'{}',
'<code>' + Shortcuts.actionShortcutSymbol() + 'V</code>'
);
: Locale.detSetupOtpAlertBodyWith.replace('{}', Shortcuts.actionShortcutSymbol() + 'V');
this.startListenClipoard();
const buttons = [
{ result: 'manually', title: Locale.detSetupOtpManualButton, silent: true },
@@ -53,12 +46,12 @@ class OtpQrReader {
Locale.detSetupOtpAlertBody2.replace('{}', screenshotKey || ''),
line3,
Locale.detSetupOtpAlertBody4
].join('<br/>'),
].join('\n'),
esc: '',
click: '',
enter: '',
buttons,
complete: res => {
complete: (res) => {
this.alert = null;
this.stopListenClipboard();
if (res === 'select') {
@@ -101,7 +94,7 @@ class OtpQrReader {
pasteEvent(e) {
const item = [...e.clipboardData.items].find(
item => item.kind === 'file' && item.type.indexOf('image') !== -1
(item) => item.kind === 'file' && item.type.indexOf('image') !== -1
);
if (!item) {
logger.debug('Paste without file');
@@ -141,11 +134,8 @@ class OtpQrReader {
logger.error('Error parsing QR code', err);
Alerts.error({
header: Locale.detOtpQrWrong,
body:
Locale.detOtpQrWrongBody +
'<pre class="modal__pre">' +
escape(err.toString()) +
'</pre>'
body: Locale.detOtpQrWrongBody,
pre: err.toString()
});
}
} catch (e) {
+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 };
-2
View File
@@ -2,8 +2,6 @@ let Launcher;
if (window.process && window.process.versions && window.process.versions.electron) {
Launcher = require('./launcher-electron').Launcher;
} else if (window.cordova) {
Launcher = require('./launcher-cordova').Launcher;
}
export { Launcher };
@@ -1,286 +0,0 @@
/* global FingerprintAuth */
import { Events } from 'framework/events';
const Launcher = {
name: 'cordova',
version: window.cordova.platformVersion,
autoTypeSupported: false,
thirdPartyStoragesSupported: false,
clipboardSupported: false,
ready(callback) {
document.addEventListener('deviceready', callback, false);
document.addEventListener(
'pause',
() => {
Events.emit('app-minimized');
},
false
);
},
platform() {
return 'cordova';
},
openLink(href) {
window.open(href, '_system');
},
devTools: false,
openDevTools() {},
getSaveFileName(defaultPath, callback) {
/* skip in cordova */
},
getDataPath(...args) {
const storagePath = window.cordova.file.externalDataDirectory;
return [storagePath].concat(Array.from(args)).filter(s => !!s);
},
getUserDataPath(fileName) {
return this.getDataPath('userdata', fileName).join('/');
},
getTempPath(fileName) {
return this.getDataPath('temp', fileName).join('/');
},
getDocumentsPath(fileName) {
return this.getDataPath('documents', fileName).join('/');
},
getAppPath(fileName) {
return this.getDataPath(fileName).join('/');
},
getWorkDirPath(fileName) {
return this.getDataPath(fileName).join('/');
},
joinPath(...parts) {
return [...parts].join('/');
},
writeFile(path, data, callback) {
const createFile = filePath => {
window.resolveLocalFileSystemURL(
filePath.dir,
dir => {
dir.getFile(filePath.file, { create: true }, writeFile);
},
callback,
callback
);
};
const writeFile = fileEntry => {
fileEntry.createWriter(fileWriter => {
fileWriter.onerror = callback;
fileWriter.onwriteend = () => callback();
fileWriter.write(data);
}, callback);
};
if (path.startsWith('cdvfile://')) {
// then file exists
window.resolveLocalFileSystemURL(path, writeFile, callback, callback);
} else {
// create file on sd card
const filePath = this.parsePath(path);
this.mkdir(filePath.dir, () => {
createFile(filePath);
});
}
},
readFile(path, encoding, callback) {
window.resolveLocalFileSystemURL(
path,
fileEntry => {
fileEntry.file(
file => {
const reader = new FileReader();
reader.onerror = callback;
reader.onloadend = () => {
const contents = new Uint8Array(reader.result);
callback(
encoding ? String.fromCharCode.apply(null, contents) : contents
);
};
reader.readAsArrayBuffer(file);
},
err => callback(undefined, err)
);
},
err => callback(undefined, err)
);
},
fileExists(path, callback) {
window.resolveLocalFileSystemURL(
path,
fileEntry => callback(true),
() => callback(false)
);
},
deleteFile(path, callback) {
window.resolveLocalFileSystemURL(
path,
fileEntry => {
fileEntry.remove(callback, callback, callback);
},
callback
);
},
statFile(path, callback) {
window.resolveLocalFileSystemURL(
path,
fileEntry => {
fileEntry.file(
file => {
callback({
ctime: new Date(file.lastModified),
mtime: new Date(file.lastModified)
});
},
err => callback(undefined, err)
);
},
err => callback(undefined, err)
);
},
mkdir(dir, callback) {
const basePath = this.getDataPath().join('/');
const createDir = (dirEntry, path, callback) => {
const name = path.shift();
dirEntry.getDirectory(
name,
{ create: true },
dirEntry => {
if (path.length) {
// there is more to create
createDir(dirEntry, path, callback);
} else {
callback();
}
},
callback
);
};
const localPath = dir
.replace(basePath, '')
.split('/')
.filter(s => !!s);
if (localPath.length) {
window.resolveLocalFileSystemURL(
basePath,
dirEntry => {
createDir(dirEntry, localPath, callback);
},
callback
);
} else {
callback();
}
},
parsePath(fileName) {
const parts = fileName.split('/');
return {
path: fileName,
file: parts.pop(),
dir: parts.join('/')
};
},
createFsWatcher(path) {
return null; // not in android with content provider
},
ensureRunnable(path) {},
preventExit(e) {
e.returnValue = false;
return false;
},
exit() {
this.hideApp();
},
requestExit() {
/* skip in cordova */
},
requestRestart() {
window.location.reload();
},
cancelRestart() {
/* skip in cordova */
},
setClipboardText(text) {},
getClipboardText() {},
clearClipboardText() {},
minimizeApp() {
this.hideApp();
},
canMinimize() {
return false;
},
canDetectOsSleep() {
return false;
},
updaterEnabled() {
return false;
},
// getMainWindow() {},
resolveProxy(url, callback) {
/* skip in cordova */
},
hideApp() {
/* skip in cordova */
},
isAppFocused() {
return false; /* skip in cordova */
},
showMainWindow() {
/* skip in cordova */
},
// spawn(config) {},
openFileChooser(callback) {
const onFileSelected = function(selected) {
window.resolveLocalFileSystemURL(selected.uri, fileEntry => {
fileEntry.file(file => {
file.path = file.localURL;
file.name = selected.name;
callback(null, file);
});
});
};
window.cordova.exec(onFileSelected, callback, 'FileChooser', 'choose');
},
setGlobalShortcuts(appSettings) {},
fingerprints: {
config: {
disableBackup: true,
clientId: 'keeweb'
},
register(fileId, password, callback) {
FingerprintAuth.isAvailable(result => {
if (!result.isAvailable) {
return;
}
const encryptConfig = {
...this.config,
username: fileId,
password: password.getText()
};
FingerprintAuth.encrypt(encryptConfig, result => {
callback(result.token);
});
});
},
auth(fileId, token, callback) {
if (!token) {
return callback();
}
const decryptConfig = { ...this.config, username: fileId, token };
FingerprintAuth.decrypt(decryptConfig, result => {
callback(result.password);
});
}
}
};
export { Launcher };
+99 -90
View File
@@ -17,6 +17,9 @@ const Launcher = {
platform() {
return process.platform;
},
arch() {
return process.arch;
},
electron() {
return this.req('electron');
},
@@ -33,9 +36,7 @@ const Launcher = {
},
devTools: true,
openDevTools() {
this.electron()
.remote.getCurrentWindow()
.openDevTools({ mode: 'bottom' });
this.electron().remote.getCurrentWindow().webContents.openDevTools({ mode: 'bottom' });
},
getSaveFileName(defaultPath, callback) {
if (defaultPath) {
@@ -48,18 +49,24 @@ const Launcher = {
defaultPath,
filters: [{ name: Locale.launcherFileFilter, extensions: ['kdbx'] }]
})
.then(res => callback(res.filePath));
.then((res) => callback(res.filePath));
},
getUserDataPath(fileName) {
if (!this.userDataPath) {
const realUserDataPath = this.remoteApp().getPath('userData');
const suffixReplacementRegex = /[\\/]temp[\\/]\d+\.\d+[\\/]?$/;
this.userDataPath = realUserDataPath.replace(suffixReplacementRegex, '');
this.userDataPath = this.remoteApp().getPath('userData');
}
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 || '');
@@ -85,7 +92,12 @@ const Launcher = {
});
},
fileExists(path, callback) {
this.req('fs').exists(path, callback);
const fs = this.req('fs');
fs.access(path, fs.constants.F_OK, (err) => callback(!err));
},
fileExistsSync(path) {
const fs = this.req('fs');
return !fs.accessSync(path, fs.constants.F_OK);
},
deleteFile(path, callback) {
this.req('fs').unlink(path, callback || noop);
@@ -98,8 +110,8 @@ const Launcher = {
const path = this.req('path');
const stack = [];
const collect = function(dir, stack, callback) {
fs.exists(dir, exists => {
const collect = function (dir, stack, callback) {
fs.exists(dir, (exists) => {
if (exists) {
return callback();
}
@@ -114,12 +126,12 @@ const Launcher = {
});
};
const create = function(stack, callback) {
const create = function (stack, callback) {
if (!stack.length) {
return callback();
}
fs.mkdir(stack.shift(), err => (err ? callback(err) : create(stack, callback)));
fs.mkdir(stack.shift(), (err) => (err ? callback(err) : create(stack, callback)));
};
collect(dir, stack, () => create(stack, callback));
@@ -135,16 +147,11 @@ const Launcher = {
createFsWatcher(path) {
return this.req('fs').watch(path, { persistent: false });
},
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);
}
}
loadConfig(name) {
return this.remoteApp().loadConfig(name);
},
saveConfig(name, data) {
return this.remoteApp().saveConfig(name, data);
},
preventExit(e) {
e.returnValue = false;
@@ -156,18 +163,19 @@ const Launcher = {
},
requestExit() {
const app = this.remoteApp();
if (this.restartPending) {
app.restartApp();
app.setHookBeforeQuitEvent(false);
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);
@@ -176,7 +184,14 @@ const Launcher = {
return this.electron().clipboard.readText();
},
clearClipboardText() {
return this.electron().clipboard.clear();
const { clipboard } = this.electron();
clipboard.clear();
if (process.platform === 'linux') {
clipboard.clear('selection');
}
},
quitOnRealQuitEventIfMinimizeOnQuitIsEnabled() {
return !!this.pendingUpdateFile;
},
minimizeApp() {
this.remoteApp().minimizeApp({
@@ -184,14 +199,11 @@ const Launcher = {
quit: Locale.menuQuitApp.replace('{}', 'KeeWeb')
});
},
canMinimize() {
return process.platform !== 'darwin';
},
canDetectOsSleep() {
return process.platform !== 'linux';
},
updaterEnabled() {
return this.electron().remote.process.argv.indexOf('--disable-updater') === -1;
return process.platform !== 'linux';
},
getMainWindow() {
return this.remoteApp().getMainWindow();
@@ -199,7 +211,7 @@ const Launcher = {
resolveProxy(url, callback) {
const window = this.getMainWindow();
const session = window.webContents.session;
session.resolveProxy(url).then(proxy => {
session.resolveProxy(url).then((proxy) => {
const match = /^proxy\s+([\w\.]+):(\d+)+\s*/i.exec(proxy);
proxy = match && match[1] ? { host: match[1], port: +match[2] } : null;
callback(proxy);
@@ -207,69 +219,46 @@ const Launcher = {
},
hideApp() {
const app = this.remoteApp();
if (this.canMinimize()) {
app.minimizeThenHideIfInTray();
} else {
if (this.platform() === 'darwin') {
app.hide();
} else {
app.minimizeThenHideIfInTray();
}
},
isAppFocused() {
return !!this.electron().remote.BrowserWindow.getFocusedWindow();
},
showMainWindow() {
const win = this.getMainWindow();
win.show();
win.focus();
win.restore();
this.remoteApp().showAndFocusMainWindow();
},
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');
});
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) {
logger.error(msg + '\n' + stdout + '\n' + stderr);
} else {
logger.info(msg + (stdout ? '\n' + stdout : ''));
}
if (complete) {
complete(code ? '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;
@@ -287,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();
}
};
@@ -294,11 +295,12 @@ 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));
Events.on('start-profile', (data) => StartProfiler.reportAppProfile(data));
window.launcherOpen = file => Launcher.openFile(file);
window.launcherOpen = (file) => Launcher.openFile(file);
if (window.launcherOpenedFile) {
logger.info('Open file request', window.launcherOpenedFile);
Launcher.openFile(window.launcherOpenedFile);
@@ -314,7 +316,14 @@ Events.on('app-ready', () =>
}, 0)
);
Launcher.remoteApp().on('remote-app-event', e => {
if (process.platform === 'darwin') {
Launcher.remoteApp().setHookBeforeQuitEvent(true);
}
Launcher.remoteApp().on('remote-app-event', (e) => {
if (window.debugRemoteAppEvents) {
logger.debug('remote-app-event', e.name);
}
Events.emit(e.name, e.data);
});
+250
View File
@@ -0,0 +1,250 @@
import * as kdbxweb from 'kdbxweb';
import { Events } from 'framework/events';
import { Logger } from 'util/logger';
import { Launcher } from 'comp/launcher';
import { Timeouts } from 'const/timeouts';
let NativeModules;
if (Launcher) {
const logger = new Logger('native-module-connector');
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 });
},
log(...args) {
logger.info('Message from host', ...args);
},
result({ callId, result, error }) {
const promise = promises[callId];
if (promise) {
delete promises[callId];
if (error) {
logger.error('Received an error', promise.cmd, error);
promise.reject(error);
} else {
promise.resolve(result);
}
}
},
yubiKeyChallengeResponseResult({ callbackId, error, result }) {
const callback = ykChalRespCallbacks[callbackId];
if (callback) {
const willBeCalledAgain = error && error.touchRequested;
if (!willBeCalledAgain) {
delete ykChalRespCallbacks[callbackId];
}
callback(error, result);
}
}
};
NativeModules = {
startHost() {
if (hostRunning) {
return Promise.resolve();
}
if (hostStartPromise) {
return hostStartPromise;
}
logger.debug('Starting native module host');
hostStartPromise = this.callNoWait('start').then(() => {
hostStartPromise = undefined;
hostRunning = true;
if (this.usbListenerRunning) {
return this.call('startUsbListener');
}
});
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}`);
hostRunning = false;
const err = new Error('Native module host crashed');
for (const promise of Object.values(promises)) {
promise.reject(err);
}
promises = {};
for (const callback of Object.values(ykChalRespCallbacks)) {
callback(err);
}
ykChalRespCallbacks = {};
if (code !== 0) {
this.autoRestartHost();
}
},
hostCallback(message) {
const { cmd, args } = message;
// logger.debug('Callback', cmd, args);
if (handlers[cmd]) {
handlers[cmd](...args);
} else {
logger.error('No callback', cmd);
}
},
log(name, level, ...args) {
if (!name) {
return;
}
const logger = new Logger(name);
logger[level](...args);
},
autoRestartHost() {
setTimeout(() => {
try {
this.startHost();
} catch (e) {
logger.error('Native module host failed to auto-restart', e);
}
}, Timeouts.NativeModuleHostRestartTime);
},
call(cmd, ...args) {
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 };
ipcRenderer.send('nativeModuleCall', { cmd, args, callId });
});
},
startUsbListener() {
this.call('startUsbListener');
this.usbListenerRunning = true;
},
stopUsbListener() {
this.usbListenerRunning = false;
if (hostRunning) {
this.call('stopUsbListener');
}
},
getYubiKeys(config) {
return this.call('getYubiKeys', config);
},
yubiKeyChallengeResponse(yubiKey, challenge, slot, callback) {
ykChalRespCallbacks[callId] = callback;
return this.call('yubiKeyChallengeResponse', yubiKey, challenge, slot, callId);
},
yubiKeyCancelChallengeResponse() {
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 };
+115 -22
View File
@@ -1,68 +1,147 @@
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) {
if (settings.theme) {
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());
}
} catch (ex) {}
},
getDefaultTheme() {
return 'dark';
},
setTheme(theme) {
if (!theme) {
if (this.activeTheme) {
return;
}
theme = this.getDefaultTheme();
}
for (const cls of document.body.classList) {
if (/^th-/.test(cls)) {
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';
@@ -73,7 +152,7 @@ const SettingsManager = {
return;
}
let localeValues;
if (loc !== 'en') {
if (loc !== 'en-US') {
if (this.customLocales[loc]) {
localeValues = this.customLocales[loc];
} else {
@@ -86,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;
}
+19 -45
View File
@@ -5,57 +5,31 @@ import { Logger } from 'util/logger';
const logger = new Logger('settings');
const SettingsStore = {
fileName(key) {
return `${key}.json`;
},
load(key) {
return new Promise(resolve => {
if (Launcher) {
const settingsFile = Launcher.getUserDataPath(this.fileName(key));
Launcher.fileExists(settingsFile, exists => {
if (exists) {
Launcher.readFile(settingsFile, 'utf8', data => {
return this.parseData(key, data, resolve);
});
} else {
resolve();
}
});
} else {
const data = localStorage[StringFormat.camelCase(key)];
return this.parseData(key, data, resolve);
}
});
},
parseData(key, data, resolve) {
try {
if (data) {
return resolve(JSON.parse(data));
} else {
resolve();
}
} catch (e) {
logger.error('Error loading ' + key, e);
resolve();
let loadPromise;
if (Launcher) {
loadPromise = Launcher.loadConfig(key);
} else {
loadPromise = Promise.resolve().then(() => {
return localStorage[StringFormat.camelCase(key)];
});
}
return loadPromise
.then((data) => (data ? JSON.parse(data) : null))
.catch((err) => {
logger.error(`Error loading ${key}`, err);
});
},
save(key, data) {
return new Promise(resolve => {
if (Launcher) {
const settingsFile = Launcher.getUserDataPath(this.fileName(key));
data = JSON.stringify(data);
Launcher.writeFile(settingsFile, data, err => {
if (err) {
logger.error(`Error saving ${key}`, err);
}
resolve();
});
} else if (typeof localStorage !== 'undefined') {
if (Launcher) {
return Launcher.saveConfig(key, JSON.stringify(data)).catch((err) => {
logger.error(`Error saving ${key}`, err);
});
}
return Promise.resolve().then(() => {
if (typeof localStorage !== 'undefined') {
localStorage[StringFormat.camelCase(key)] = JSON.stringify(data);
resolve();
}
});
}
+16 -2
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,8 +50,7 @@ const Alerts = {
Alerts.alertDisplayed = true;
const view = new ModalView(config);
view.render();
view.on('result', (res, check) => {
Alerts.alertDisplayed = false;
view.once('result', (res, check) => {
if (res && config.success) {
config.success(res, check);
}
@@ -50,6 +61,9 @@ const Alerts = {
config.complete(res, check);
}
});
view.on('will-close', () => {
Alerts.alertDisplayed = false;
});
return view;
},
+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 };

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