chore: merge "release/trift" to devel
This commit is contained in:
commit
c0fc23d7cd
|
@ -158,7 +158,6 @@ test-windows:
|
|||
extends:
|
||||
- .env-windows
|
||||
- .script-test
|
||||
- .rules-branch-and-MR-manual
|
||||
|
||||
test-darwin:
|
||||
extends:
|
||||
|
@ -174,7 +173,7 @@ test-coverage:
|
|||
coverage: '/total:.*\(statements\).*\d+\.\d+%/'
|
||||
needs:
|
||||
- test-linux
|
||||
#- test-windows
|
||||
- test-windows
|
||||
- test-darwin
|
||||
- test-integration
|
||||
tags:
|
||||
|
|
54
Changelog.md
54
Changelog.md
|
@ -155,7 +155,7 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
|||
* GODT-2437: Silence harmless report to sentry.
|
||||
* GODT-2649: Clean up cache files after failed connector create (Gluon).
|
||||
* GODT-2638: Validate messages before import.
|
||||
* GODT-2646: Bump GPA and Gluon dependecy after CIRCL upgrade.
|
||||
* GODT-2646: Bump GPA and Gluon dependency after CIRCL upgrade.
|
||||
* GODT-2454: Only Send status update if transaction succeeded.
|
||||
* Test: fix flaky tests.
|
||||
* GODT-2628: Attempt to fix closed channel panic on logout.
|
||||
|
@ -215,7 +215,7 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
|||
* GODT-2574: Fix label/unlabel of large amounts of messages.
|
||||
* GODT-2573: Handle invalid header fields in message.
|
||||
* GODT-2573: Crash on null update.
|
||||
* GODT-2407: Replace invalid email addresses with emtpy for new Drafts.
|
||||
* GODT-2407: Replace invalid email addresses with empty for new Drafts.
|
||||
|
||||
## [Bridge 3.1.3] Quebec
|
||||
|
||||
|
@ -356,7 +356,7 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
|||
* GODT-2429: Do not report context cancel to sentry.
|
||||
|
||||
### Fixed
|
||||
* GODT-2467: elide long email adresses in 'bad event' QML notification dialog.
|
||||
* GODT-2467: elide long email addresses in 'bad event' QML notification dialog.
|
||||
* GODT-2449: fix bug in Bridge-GUI's Exception::what().
|
||||
* GODT-2427: Parsing header issues.
|
||||
* GODT-2426: Fix crash on user delete.
|
||||
|
@ -373,7 +373,7 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
|||
* GODT-2404: Handle unexpected EOF.
|
||||
* GODT-2400: Allow state updates to be applied if command fails.
|
||||
* GODT-2399: Fix immediate message deletion during updates.
|
||||
* GODT-2390: Missing changes from pervious commit.
|
||||
* GODT-2390: Missing changes from previous commit.
|
||||
* GODT-2390: Add reports for uncaught json and net.opErr.
|
||||
* GODT-2414: Multiple deletion bug in WriteControlledStore.
|
||||
|
||||
|
@ -438,7 +438,7 @@ GODT-1804: Preserve MIME parameters when uploading attachments.
|
|||
* GODT-2223: Improve event handling.
|
||||
* GODT-2305: Detect missing gluon DB.
|
||||
* GODT-2291: Change gluon store default location from Cache to Data.
|
||||
* Other: Disable dialer test until badssl cert is bumbed.
|
||||
* Other: Disable dialer test until badssl cert is bumped.
|
||||
* GODT-2292: Updated BUILDS.md doc.
|
||||
* GODT-2258: suggest email as login when signing in via status window.
|
||||
* Other: Report corrupt and/or insecure vaults to sentry.
|
||||
|
@ -718,7 +718,7 @@ GODT-1804: Preserve MIME parameters when uploading attachments.
|
|||
## [Bridge 2.4.6] Osney
|
||||
|
||||
### Changed
|
||||
* GODT-2019: When signing out and a single user is connecte* we do not go back to the welcome screen.
|
||||
* GODT-2019: When signing out and a single user is connected we do not go back to the welcome screen.
|
||||
* GODT-2071: Bridge-gui report error if an orphan bridge is detected.
|
||||
* GODT-2046: Bridge-gui log is included in optional archive sent with bug reports.
|
||||
* GODT-2039: Bridge monitors bridge-gui via its PID.
|
||||
|
@ -872,7 +872,7 @@ GODT-1804: Preserve MIME parameters when uploading attachments.
|
|||
* GODT-1260: Renaming.
|
||||
* GODT-1502: Rebranding: color and radius.
|
||||
* GODT-1549: Add notification when address list changes.
|
||||
* GODT-1560: Dependecy licenses update and link.
|
||||
* GODT-1560: Dependency licenses update and link.
|
||||
|
||||
### Changed
|
||||
* GODT-1543: Using one buffered event for off and on connection.
|
||||
|
@ -969,7 +969,7 @@ GODT-1537: Manual in-app update mechanism.
|
|||
* GODT-1338: GODT-1343 Help view buttons.
|
||||
* GODT-1340: Not crashing, user list updating in main thread.
|
||||
* GODT-1345: Adding panic handlers.
|
||||
* GODT-1271: Fix Status margings.
|
||||
* GODT-1271: Fix Status margins.
|
||||
* GODT-1320: Add loading property to each action within a notification.
|
||||
* GODT-1210: Add "free user" banner.
|
||||
* GODT-1314: Limit description field length within 150/800 bounds.
|
||||
|
@ -1011,7 +1011,7 @@ GODT-1537: Manual in-app update mechanism.
|
|||
* GODT-1381 Treat readonly folder as failure for cache on disk.
|
||||
* GODT-1431 Prevent watcher when not using disk on cache.
|
||||
* GODT-1381: Use in-memory cache in case local cache is unavailable.
|
||||
* GODT-1356 GODT-1302: Cache on disk concurency and API retries.
|
||||
* GODT-1356 GODT-1302: Cache on disk concurrency and API retries.
|
||||
* GODT-1332 Added tests for cache move functions.
|
||||
* GODT-1332: moved cache related functions to separate file.
|
||||
* GODT-1332 moving cache does not work on Windows.
|
||||
|
@ -1262,7 +1262,7 @@ GODT-1537: Manual in-app update mechanism.
|
|||
### Fixed
|
||||
* GODT-1029 Fix tray icon not updating under certain conditions.
|
||||
* GODT-1062 Fix lost notification bar when window is closed.
|
||||
* GODT-1058 Install version after chaning channel right away only in case of downgrade.
|
||||
* GODT-1058 Install version after changing channel right away only in case of downgrade.
|
||||
* GODT-1073 Re-write autostart link on every start if turned on in preferences.
|
||||
* GODT-1055 Fix flaky empty trash test.
|
||||
|
||||
|
@ -1352,7 +1352,7 @@ GODT-1537: Manual in-app update mechanism.
|
|||
* GODT-820 Added GUI notification on impossibility of update installation (both silent and manual).
|
||||
* GODT-870 Added GUI notification on error during silent update.
|
||||
* GODT-805 Added GUI notification on update available.
|
||||
* GODT-804 Added GUI notification on silent update installed (promt to restart).
|
||||
* GODT-804 Added GUI notification on silent update installed (prompt to restart).
|
||||
* GODT-275 Added option to disable autoupdates in settings (default autoupdate is enabled).
|
||||
* GODT-874 Added manual triggers to Updater module.
|
||||
* GODT-851 Added support of UID EXPUNGE.
|
||||
|
@ -1676,7 +1676,7 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
|
||||
### Changed
|
||||
* GODT-360 Detect charset embedded in html/xml.
|
||||
* GODT-354 Do not label/unlabel messsages from `All Mail` folder.
|
||||
* GODT-354 Do not label/unlabel messages from `All Mail` folder.
|
||||
* GODT-388 Support for both bridge and import/export credentials by package users.
|
||||
* GODT-387 Store factory to make store optional.
|
||||
* GODT-386 Renamed bridge to general users and keep bridge only for bridge stuff.
|
||||
|
@ -1841,13 +1841,13 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
* GODT-88 Run mbox sync in parallel when switch password mode (re-init not user).
|
||||
* GODT-95 Do not throw error when trying to create new mailbox in IMAP root.
|
||||
* GODT-75 Do not fail on unlabel inside delete.
|
||||
* #1095 always delete IMAP USER including wrong pasword.
|
||||
* #1095 always delete IMAP USER including wrong password.
|
||||
* Unique pmapi client userID (including #1098).
|
||||
* Using go.enmime@v0.6.1 snapshot.
|
||||
* Better detection of non-auth-error.
|
||||
* Reset `hasAuthChannel` during logout for proper login functionality (set up auth channel and unlock keys).
|
||||
* Allow `APPEND` messages without parsable email address in sender field.
|
||||
* #1060 avoid `Append` after internal message ID was found and message was copyed to mailbox using `MessageLabel`.
|
||||
* #1060 avoid `Append` after internal message ID was found and message was copied to mailbox using `MessageLabel`.
|
||||
* #1049 Basic usage of store in SMTP package to poll event loop during sending message.
|
||||
* #1050 pollNow waits for events to be processed.
|
||||
* #1047 Fix fetch of empty mailbox.
|
||||
|
@ -1973,7 +1973,7 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
* #903 added http.Client timeout to not hang out forever.
|
||||
* Closing body after checking internet connection.
|
||||
* Pedantic lint for bridgeUtils.
|
||||
* Selected events are buffered and emited again when frontend loop is ready.
|
||||
* Selected events are buffered and emitted again when frontend loop is ready.
|
||||
* #890 implemented 2FA endpoint (auth split).
|
||||
* #888 TLS Cert.
|
||||
* Error bar and modal with explanation in GUI.
|
||||
|
@ -1981,7 +1981,7 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
* Add pinning to bridge (only for live API builds).
|
||||
* #887 #883:
|
||||
* Wait before clearing data.
|
||||
* Configer which provides pmapi.ClientConfig and app directories.
|
||||
* Configure which provides pmapi.ClientConfig and app directories.
|
||||
* #861 restart after clear data.
|
||||
* Panic handler for all goroutines.
|
||||
* CD for linux.
|
||||
|
@ -2029,7 +2029,7 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
* #882 unassign PMAPI client after logout and force to run garbage collector.
|
||||
* #880, #884, #885, #886 fix of informing user about outgoing non-encrypted e-mail.
|
||||
* #838 `Sirupsen` -> `sirupsen`.
|
||||
* #893 save panic report file everytime.
|
||||
* #893 save panic report file every time.
|
||||
* #880 fix of informing user about outgoing non-encrypted e-mail.
|
||||
* Fix aliases in split mode.
|
||||
* Fix decrypted data in log notification.
|
||||
|
@ -2103,7 +2103,7 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
|
||||
### Changed
|
||||
* Fix custom message format.
|
||||
* #802 acumulated long lines while parsing body structure.
|
||||
* #802 accumulated long lines while parsing body structure.
|
||||
* Process `AddressEvent` before `MessageEvent`.
|
||||
* #791 updated crypto: fix wrong signature format.
|
||||
* #793 fix returning size.
|
||||
|
@ -2125,7 +2125,7 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
|
||||
### Changed
|
||||
* #748 when charset missing assume utf8 and check the validity.
|
||||
* #750 before sync check that events are uptodate, if not poll events instead of sync.
|
||||
* #750 before sync check that events are up-to-date, if not poll events instead of sync.
|
||||
* Use pmapi with support of decrypted access token.
|
||||
* #750 Status is using DB status instead of API.
|
||||
* Format panic error as string instead of struct dump.
|
||||
|
@ -2142,7 +2142,7 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
* Full version of program visible on release notes.
|
||||
|
||||
### Changed
|
||||
* #720 only one concurent DB sync.
|
||||
* #720 only one concurrent DB sync.
|
||||
* #720 sync every 3 pages.
|
||||
* #512 extending list of charsets go-pm-mime!4.
|
||||
|
||||
|
@ -2166,7 +2166,7 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
* Fix srp modulus issue with new `ProtonMail/crypto`.
|
||||
* Generate version files from main file.
|
||||
* Be able to set update set on build.
|
||||
* #597 check on start that certificat will be still valid after one month and generate new cert if not.
|
||||
* #597 check on start that certificate will be still valid after one month and generate new cert if not.
|
||||
* #597 extended certificate validity to 2 years.
|
||||
* Copyright 2019.
|
||||
* Exclude `protontech` repos from credits.
|
||||
|
@ -2185,7 +2185,7 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
* #592 internal references are added only when not present already.
|
||||
* #592 field `Date` changed to m.Time only when wrong format or missing `Date`.
|
||||
* #645 pmapi#26 `Message.Flags` instead of `IsEncrypted`, `Type`, `IsReplied`, `IsRepliedAll`, `IsForwarded`.
|
||||
* DB: do not allow to put Body or Attachements to db.
|
||||
* DB: do not allow to put Body or Attachments to db.
|
||||
* #574 SMTP: can now send more than one email.
|
||||
* #671 Verbosity levels: `debug` (only bridge), `debug-client` (bridge and client communication), `debug-server` (bridge, whole SMTP/IMAP communication).
|
||||
* #644 Return rfc.size 0 or correct size of fetched body (stored in DB).
|
||||
|
@ -2257,7 +2257,7 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
* Start with new versioning.
|
||||
|
||||
1.1.0
|
||||
| | `--- bug fix number (internal, irregular, beta relases)
|
||||
| | `--- bug fix number (internal, irregular, beta releases)
|
||||
| `----- minor version (features, release once per month, live release, milestones)
|
||||
`------- major version (big changes, once per year, breaking changes, api force upgrade)
|
||||
|
||||
|
@ -2323,7 +2323,7 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
* All `client.Do` errors are interpreted as connection issue.
|
||||
* Moved to internal gitlab.
|
||||
* Typo `frontend-qml`.
|
||||
* Better message for case when server is not reacheable.
|
||||
* Better message for case when server is not reachable.
|
||||
* Setting 1min timeout to IMAP connection.
|
||||
|
||||
### Changed
|
||||
|
@ -2355,12 +2355,12 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
* Keychain format and function refactor.
|
||||
* Create crash file on panic with full trace.
|
||||
* Clear old data only in main process (no double keychain typing).
|
||||
* Create label udpate API route.
|
||||
* Create label update API route.
|
||||
* Selectable text in release notes.
|
||||
|
||||
### Added
|
||||
* Support sending to external PGP recipients.
|
||||
* Return error codes: `0: Ok`, `2: Frontend crashed`, `3: Bridge already running`, `4: Uknown argument`, `42: Restart application`.
|
||||
* Return error codes: `0: Ok`, `2: Frontend crashed`, `3: Bridge already running`, `4: Unknown argument`, `42: Restart application`.
|
||||
|
||||
### Release notes
|
||||
* Support of encryption to external PGP recipients using contacts created on beta.protonmail.com (see https://protonmail.com/blog/pgp-vulnerability-efail/ to understand the vulnerabilities that may be associated with sending to other PGP clients).
|
||||
|
@ -2385,7 +2385,7 @@ CSB-331 Fix sending error due to mixed case in sender address.
|
|||
* Bug report window.
|
||||
* Checkbox and with label (only I/E).
|
||||
* Error dialog and Info tooltip (only I/E).
|
||||
* Add user modal formating (colors, text).
|
||||
* Add user modal formatting (colors, text).
|
||||
* Account view style.
|
||||
* Input box style (used in bug report).
|
||||
* Input field style (used in add account and change port).
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
## First login and sync
|
||||
|
||||
When user logs in to the bridge for the first time, immediatelly starts the first sync.
|
||||
When user logs in to the bridge for the first time, immediately starts the first sync.
|
||||
First sync downloads all headers of all e-mails and creates database to have proper UIDs
|
||||
and indexes for IMAP. See [database](database.md) for more information.
|
||||
|
||||
By default, whenever it's possible, sync downloads only all e-mails maiblox which already
|
||||
have list of labels so we can construct all mailboxes (inbox, sent, trash, custom folders
|
||||
and lables) without need to download each e-mail headers many times.
|
||||
and labels) without need to download each e-mail headers many times.
|
||||
|
||||
Note that we need to download also bodies to calculate size of the e-mail and set proper
|
||||
content type (clients uses content type for guess if e-mail contains attachment)--but only
|
||||
|
@ -22,7 +22,7 @@ client right after adding account.
|
|||
|
||||
When account is added to client, client start the sync. This sync will ask Bridge app
|
||||
for all headers (done quickly) and then starts to download all bodies and attachment.
|
||||
Unfortunatelly for some e-mail more than once if the same e-mail is in more mailboxes
|
||||
Unfortunately for some e-mail more than once if the same e-mail is in more mailboxes
|
||||
(e.g. inbox and all mail)--there is no way to tell over IMAP it's the same message.
|
||||
|
||||
After successful login of client to IMAP, Bridge starts event loop. That periodicly ask
|
||||
|
@ -37,7 +37,7 @@ sequenceDiagram
|
|||
Note right of B: Set up PM account<br/>by user
|
||||
|
||||
loop First sync
|
||||
B ->> S: Fetch body and attachements
|
||||
B ->> S: Fetch body and attachments
|
||||
Note right of B: Build local database<br/>(e-mail UIDs)
|
||||
end
|
||||
|
||||
|
@ -58,8 +58,8 @@ sequenceDiagram
|
|||
C ->> B: IMAP SELECT directory
|
||||
C ->> B: IMAP SEARCH e-mails UIDs
|
||||
C ->> B: IMAP FETCH of e-mail UID
|
||||
B ->> S: Fetch body and attachements
|
||||
Note right of B: Decrypt message<br/>and attachement
|
||||
B ->> S: Fetch body and attachments
|
||||
Note right of B: Decrypt message<br/>and attachment
|
||||
B ->> C: IMAP response
|
||||
end
|
||||
```
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
# Update mechanism of Bridge
|
||||
|
||||
There are mulitple options how to change version of application:
|
||||
There are multiple options how to change version of application:
|
||||
* Automatic in-app update
|
||||
* Manual in-app update
|
||||
* Manual install
|
||||
|
||||
In-app update ends with restarting bridge into new version. Automatic in-app
|
||||
update is downloading, verifying and installing the new version immediatelly
|
||||
update is downloading, verifying and installing the new version immediately
|
||||
without user confirmation. For manual in-app update user needs to confirm first.
|
||||
Update is done from special update file published on website.
|
||||
|
||||
|
@ -25,7 +25,7 @@ The bridge is installed and executed differently for given OS:
|
|||
|
||||
* macOS app does not use launcher
|
||||
* No launcher, only one executable
|
||||
* In-App udpate replaces the bridge files in installation path directly
|
||||
* In-App update replaces the bridge files in installation path directly
|
||||
|
||||
|
||||
```mermaid
|
||||
|
|
|
@ -31,7 +31,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Disabled due to flakyness.
|
||||
// Disabled due to flakiness.
|
||||
func _TestBridge_SyncExistsWithErrorWhenTooManyFilesAreOpen(t *testing.T) { //nolint:unused
|
||||
var rlimitCurrent syscall.Rlimit
|
||||
|
||||
|
|
|
@ -385,6 +385,14 @@ Status GRPCService::Login(ServerContext *, LoginRequest const *request, Empty *)
|
|||
app().log().debug(__FUNCTION__);
|
||||
UsersTab &usersTab = app().mainWindow().usersTab();
|
||||
loginUsername_ = QString::fromStdString(request->username());
|
||||
|
||||
SPUser const& user = usersTab.userTable().userWithUsernameOrEmail(QString::fromStdString(request->username()));
|
||||
if (user) {
|
||||
qtProxy_.sendDelayedEvent(newLoginAlreadyLoggedInEvent(user->id()));
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
|
||||
if (usersTab.nextUserUsernamePasswordError()) {
|
||||
qtProxy_.sendDelayedEvent(newLoginError(LoginErrorType::USERNAME_PASSWORD_ERROR, usersTab.usernamePasswordErrorMessage()));
|
||||
return Status::OK;
|
||||
|
@ -826,7 +834,7 @@ bool GRPCService::sendEvent(SPStreamEvent const &event) {
|
|||
//****************************************************************************************************************************************************
|
||||
void GRPCService::finishLogin() {
|
||||
UsersTab &usersTab = app().mainWindow().usersTab();
|
||||
SPUser user = usersTab.userWithUsername(loginUsername_);
|
||||
SPUser user = usersTab.userWithUsernameOrEmail(loginUsername_);
|
||||
bool const alreadyExist = user.get();
|
||||
if (!user) {
|
||||
user = randomUser();
|
||||
|
|
|
@ -272,8 +272,8 @@ bridgepp::SPUser UsersTab::userWithID(QString const &userID) {
|
|||
/// \return The user with the given username.
|
||||
/// \return A null pointer if the user is not in the list.
|
||||
//****************************************************************************************************************************************************
|
||||
bridgepp::SPUser UsersTab::userWithUsername(QString const &username) {
|
||||
return users_.userWithUsername(username);
|
||||
bridgepp::SPUser UsersTab::userWithUsernameOrEmail(QString const &username) {
|
||||
return users_.userWithUsernameOrEmail(username);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ public: // member functions.
|
|||
UsersTab &operator=(UsersTab &&) = delete; ///< Disabled move assignment operator.
|
||||
UserTable &userTable(); ///< Returns a reference to the user table.
|
||||
bridgepp::SPUser userWithID(QString const &userID); ///< Get the user with the given ID.
|
||||
bridgepp::SPUser userWithUsername(QString const &username); ///< Get the user with the given username.
|
||||
bridgepp::SPUser userWithUsernameOrEmail(QString const &username); ///< Get the user with the given username.
|
||||
bool nextUserUsernamePasswordError() const; ///< Check if next user login should trigger a username/password error.
|
||||
bool nextUserFreeUserError() const; ///< Check if next user login should trigger a Free user error.
|
||||
bool nextUserTFARequired() const; ///< Check if next user login should requires 2FA.
|
||||
|
|
|
@ -150,13 +150,16 @@ bridgepp::SPUser UserTable::userWithID(QString const &userID) {
|
|||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] username The username.
|
||||
/// \param[in] username The username, or any email address attached to the account.
|
||||
/// \return The user with the given username.
|
||||
/// \return A null pointer if the user is not in the list.
|
||||
//****************************************************************************************************************************************************
|
||||
bridgepp::SPUser UserTable::userWithUsername(QString const &username) {
|
||||
bridgepp::SPUser UserTable::userWithUsernameOrEmail(QString const &username) {
|
||||
QList<SPUser>::const_iterator it = std::find_if(users_.constBegin(), users_.constEnd(), [&username](SPUser const &user) -> bool {
|
||||
return user->username() == username;
|
||||
if (user->username().compare(username, Qt::CaseInsensitive) == 0) {
|
||||
return true;
|
||||
}
|
||||
return user->addresses().contains(username, Qt::CaseInsensitive);
|
||||
});
|
||||
|
||||
return it == users_.end() ? nullptr : *it;
|
||||
|
|
|
@ -40,7 +40,7 @@ public: // member functions.
|
|||
void append(bridgepp::SPUser const &user); ///< Append a user.
|
||||
bridgepp::SPUser userAtIndex(qint32 index); ///< Return the user at the given index.
|
||||
bridgepp::SPUser userWithID(QString const &userID); ///< Return the user with a given id.
|
||||
bridgepp::SPUser userWithUsername(QString const &username); ///< Return the user with a given username.
|
||||
bridgepp::SPUser userWithUsernameOrEmail(QString const &username); ///< Return the user with a given username.
|
||||
qint32 indexOfUser(QString const &userID); ///< Return the index of a given User.
|
||||
void touch(qint32 index); ///< touch the user at a given index (indicates it has been modified).
|
||||
void touch(QString const& userID); ///< touch the user with the given userID (indicates it has been modified).
|
||||
|
|
|
@ -51,7 +51,7 @@ public: // member functions.
|
|||
void showSettings(QString const &reason); ///< Show the settings page.
|
||||
void selectUser(QString const &userID, bool forceShowWindow, QString const &reason); ///< Select the user and display its account details (or login screen).
|
||||
|
||||
// invokable methods can be called from QML. They generally return a value, which slots cannot do.
|
||||
// invocable methods can be called from QML. They generally return a value, which slots cannot do.
|
||||
Q_INVOKABLE static QString buildYear(); ///< Return the application build year.
|
||||
Q_INVOKABLE QPoint getCursorPos() const; ///< Retrieve the cursor position.
|
||||
Q_INVOKABLE bool isPortFree(int port) const; ///< Check if a given network port is available.
|
||||
|
|
|
@ -192,6 +192,8 @@ TrayIcon::TrayIcon()
|
|||
if (!onLinux()) { // we disable this on linux because of a Qt bug that causes the signal to be emitted for other apps (GODT-2750)
|
||||
connect(this, &TrayIcon::messageClicked, []() { app().backend().showMainWindow("tray icon popup notification clicked"); });
|
||||
}
|
||||
|
||||
this->setIcon();
|
||||
this->show();
|
||||
|
||||
// TrayIcon does not expose its screen, so we connect relevant screen events to our DPI change handler.
|
||||
|
|
|
@ -63,7 +63,7 @@ BRIDGE_BUILD_ENV= ${BRIDGE_BUILD_ENV:-"dev"}
|
|||
git submodule update --init --recursive ${VCPKG_ROOT}
|
||||
check_exit "Failed to initialize vcpkg as a submodule."
|
||||
|
||||
echo submodule udpated
|
||||
echo submodule updated
|
||||
|
||||
VCPKG_EXE="${VCPKG_ROOT}/vcpkg"
|
||||
VCPKG_BOOTSTRAP="${VCPKG_ROOT}/bootstrap-vcpkg.sh"
|
||||
|
|
|
@ -137,12 +137,21 @@ bool checkSingleInstance(QLockFile &lock) {
|
|||
if (lock.getLockInfo(&pid, &hostname, &appName)) {
|
||||
details = QString("(PID : %1 - Host : %2 - App : %3)").arg(pid).arg(hostname, appName);
|
||||
}
|
||||
|
||||
if (lock.error() == QLockFile::LockFailedError) {
|
||||
// This happens if a stale lock file exists and another process uses that PID.
|
||||
// Try removing the stale file, which will fail if a real process is holding a
|
||||
// file-level lock. A false error is more problematic than not locking properly
|
||||
// on corner-case systems.
|
||||
if (lock.removeStaleLockFile() && lock.tryLock()) {
|
||||
app().log().info("Removed stale lock file");
|
||||
app().log().info(QString("lock file created %1").arg(lock.fileName()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
app().log().error(QString("Instance already exists %1 %2").arg(lock.fileName(), details));
|
||||
return false;
|
||||
} else {
|
||||
app().log().info(QString("lock file created %1").arg(lock.fileName()));
|
||||
}
|
||||
app().log().info(QString("lock file created %1").arg(lock.fileName()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -134,6 +134,11 @@ FocusScope {
|
|||
stackLayout.currentIndex = 0
|
||||
root.reset()
|
||||
}
|
||||
|
||||
function onLoginAlreadyLoggedIn(index) {
|
||||
stackLayout.currentIndex = 0
|
||||
root.reset()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
|
|
@ -101,7 +101,7 @@ CREATE_SUBDIRS = YES
|
|||
# level increment doubles the number of directories, resulting in 4096
|
||||
# directories at level 8 which is the default and also the maximum value. The
|
||||
# sub-directories are organized in 2 levels, the first level always has a fixed
|
||||
# numer of 16 directories.
|
||||
# number of 16 directories.
|
||||
# Minimum value: 0, maximum value: 8, default value: 8.
|
||||
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
|
||||
|
||||
|
|
|
@ -78,10 +78,10 @@ void FocusGRPCClient::removeServiceConfigFile(QString const &configDir) {
|
|||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] timeoutMs The timeout for the connexion.
|
||||
/// \param[in] timeoutMs The timeout for the connection.
|
||||
/// \param[in] port The gRPC server port.
|
||||
/// \param[out] outError if not null and the function returns false.
|
||||
/// \return true iff the connexion was successfully established.
|
||||
/// \return true iff the connection was successfully established.
|
||||
//****************************************************************************************************************************************************
|
||||
bool FocusGRPCClient::connectToServer(qint64 timeoutMs, quint16 port, QString *outError) {
|
||||
try {
|
||||
|
@ -97,7 +97,7 @@ bool FocusGRPCClient::connectToServer(qint64 timeoutMs, quint16 port, QString *o
|
|||
}
|
||||
|
||||
if (channel_->GetState(true) != GRPC_CHANNEL_READY) {
|
||||
throw Exception("Connexion check with focus service failed.");
|
||||
throw Exception("Connection check with focus service failed.");
|
||||
}
|
||||
|
||||
log_.debug(QString("Successfully connected to focus gRPC service."));
|
||||
|
|
|
@ -59,7 +59,7 @@ void GRPCClient::removeServiceConfigFile(QString const &configDir) {
|
|||
//****************************************************************************************************************************************************
|
||||
/// \param[in] sessionID The sessionID.
|
||||
/// \param[in] timeoutMs The timeout in milliseconds
|
||||
/// \param[in] serverProcess An optional server process to monitor. If the process it, no need and retry, as connexion cannot be established. Ignored if null.
|
||||
/// \param[in] serverProcess An optional server process to monitor. If the process it, no need and retry, as connection cannot be established. Ignored if null.
|
||||
/// \return The service config.
|
||||
//****************************************************************************************************************************************************
|
||||
GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(QString const & sessionID, QString const &configDir, qint64 timeoutMs,
|
||||
|
@ -118,7 +118,7 @@ void GRPCClient::setLog(Log *log) {
|
|||
//****************************************************************************************************************************************************
|
||||
/// \param[in] sessionID The sessionID.
|
||||
/// \param[in] configDir The configuration directory
|
||||
/// \param[in] serverProcess An optional server process to monitor. If the process it, no need and retry, as connexion cannot be established. Ignored if null.
|
||||
/// \param[in] serverProcess An optional server process to monitor. If the process it, no need and retry, as connection cannot be established. Ignored if null.
|
||||
/// \return true iff the connection was successful.
|
||||
//****************************************************************************************************************************************************
|
||||
void GRPCClient::connectToServer(QString const &sessionID, QString const &configDir, GRPCConfig const &config, ProcessMonitor *serverProcess) {
|
||||
|
@ -150,7 +150,7 @@ void GRPCClient::connectToServer(QString const &sessionID, QString const &config
|
|||
int i = 0;
|
||||
while (true) {
|
||||
if (serverProcess && serverProcess->getStatus().ended) {
|
||||
throw Exception("Bridge application ended before gRPC connexion could be established.", QString(), __FUNCTION__,
|
||||
throw Exception("Bridge application ended before gRPC connection could be established.", QString(), __FUNCTION__,
|
||||
tailOfLatestBridgeLog(sessionID));
|
||||
}
|
||||
|
||||
|
|
|
@ -461,7 +461,7 @@ func (s *Service) Login2FA(_ context.Context, login *LoginRequest) (*emptypb.Emp
|
|||
defer async.HandlePanic(s.panicHandler)
|
||||
|
||||
if s.auth.UID == "" || s.authClient == nil {
|
||||
s.log.Errorf("Login 2FA: authethication incomplete %s %p", s.auth.UID, s.authClient)
|
||||
s.log.Errorf("Login 2FA: authentication incomplete %s %p", s.auth.UID, s.authClient)
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ABORT, "Missing authentication, try again."))
|
||||
s.loginClean()
|
||||
return
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
|
||||
"github.com/ProtonMail/gluon/connector"
|
||||
"github.com/ProtonMail/gluon/imap"
|
||||
"github.com/ProtonMail/gluon/rfc5322"
|
||||
"github.com/ProtonMail/gluon/rfc822"
|
||||
"github.com/ProtonMail/go-proton-api"
|
||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||
|
@ -281,6 +282,11 @@ func (conn *imapConnector) CreateMessage(
|
|||
return imap.Message{}, nil, connector.ErrOperationNotAllowed
|
||||
}
|
||||
|
||||
toList, err := getLiteralToList(literal)
|
||||
if err != nil {
|
||||
return imap.Message{}, nil, fmt.Errorf("failed to retrieve addresses from literal:%w", err)
|
||||
}
|
||||
|
||||
// Compute the hash of the message (to match it against SMTP messages).
|
||||
hash, err := getMessageHash(literal)
|
||||
if err != nil {
|
||||
|
@ -288,7 +294,7 @@ func (conn *imapConnector) CreateMessage(
|
|||
}
|
||||
|
||||
// Check if we already tried to send this message recently.
|
||||
if messageID, ok, err := conn.sendHash.hasEntryWait(ctx, hash, time.Now().Add(90*time.Second)); err != nil {
|
||||
if messageID, ok, err := conn.sendHash.hasEntryWait(ctx, hash, time.Now().Add(90*time.Second), toList); err != nil {
|
||||
return imap.Message{}, nil, fmt.Errorf("failed to check send hash: %w", err)
|
||||
} else if ok {
|
||||
conn.log.WithField("messageID", messageID).Warn("Message already sent")
|
||||
|
@ -413,72 +419,13 @@ func (conn *imapConnector) RemoveMessagesFromMailbox(ctx context.Context, messag
|
|||
return connector.ErrOperationNotAllowed
|
||||
}
|
||||
|
||||
if err := conn.client.UnlabelMessages(ctx, mapTo[imap.MessageID, string](messageIDs), string(mailboxID)); err != nil {
|
||||
msgIDs := mapTo[imap.MessageID, string](messageIDs)
|
||||
if err := conn.client.UnlabelMessages(ctx, msgIDs, string(mailboxID)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mailboxID == proton.TrashLabel || mailboxID == proton.DraftsLabel {
|
||||
var msgToPermaDelete []string
|
||||
// There's currently no limit on how many IDs we can filter on,
|
||||
// but to be nice to API, let's chunk it by 150.
|
||||
for _, messageIDs := range xslices.Chunk(messageIDs, 150) {
|
||||
metadata, err := conn.client.GetMessageMetadata(ctx, proton.MessageFilter{
|
||||
ID: mapTo[imap.MessageID, string](messageIDs),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgIds, err := safe.LockRetErr(func() ([]string, error) {
|
||||
var msgIds []string
|
||||
|
||||
// If a message is not preset in any other label other than AllMail, AllDrafts and AllSent, it can be
|
||||
// permanently deleted.
|
||||
for _, m := range metadata {
|
||||
var remainingLabels []string
|
||||
|
||||
for _, id := range m.LabelIDs {
|
||||
label, ok := conn.apiLabels[id]
|
||||
if !ok {
|
||||
// Handle case where this label was newly introduced and we do not yet know about it.
|
||||
logrus.WithField("labelID", id).Warnf("Unknown label found during expung from Trash, attempting to locate it")
|
||||
label, err = conn.client.GetLabel(ctx, id, proton.LabelTypeFolder, proton.LabelTypeSystem, proton.LabelTypeSystem)
|
||||
if err != nil {
|
||||
if errors.Is(err, proton.ErrNoSuchLabel) {
|
||||
logrus.WithField("labelID", id).Warn("Label does not exist, ignoring")
|
||||
continue
|
||||
}
|
||||
|
||||
logrus.WithField("labelID", id).Errorf("Failed to resolve label: %v", err)
|
||||
return nil, fmt.Errorf("failed to resolve label: %w", err)
|
||||
}
|
||||
}
|
||||
if !wantLabel(label) {
|
||||
continue
|
||||
}
|
||||
|
||||
if id != proton.AllDraftsLabel && id != proton.AllMailLabel && id != proton.AllSentLabel {
|
||||
remainingLabels = append(remainingLabels, m.ID)
|
||||
}
|
||||
}
|
||||
|
||||
if len(remainingLabels) == 0 {
|
||||
msgIds = append(msgIds, m.ID)
|
||||
}
|
||||
}
|
||||
|
||||
return msgIds, nil
|
||||
}, conn.User.apiLabelsLock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgToPermaDelete = append(msgToPermaDelete, msgIds...)
|
||||
}
|
||||
|
||||
logrus.Debugf("Following message(s) will be perma-deleted: %v", msgToPermaDelete)
|
||||
|
||||
if err := conn.client.DeleteMessage(ctx, msgToPermaDelete...); err != nil {
|
||||
if err := conn.client.DeleteMessage(ctx, msgIDs...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -757,3 +704,45 @@ func buildFlagSetFromMessageMetadata(message proton.MessageMetadata) imap.FlagSe
|
|||
|
||||
return flags
|
||||
}
|
||||
|
||||
func getLiteralToList(literal []byte) ([]string, error) {
|
||||
headerLiteral, _ := rfc822.Split(literal)
|
||||
|
||||
header, err := rfc822.NewHeader(headerLiteral)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []string
|
||||
|
||||
parseAddress := func(field string) error {
|
||||
if fieldAddr, ok := header.GetChecked(field); ok {
|
||||
addr, err := rfc5322.ParseAddressList(fieldAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse addresses for '%v': %w", field, err)
|
||||
}
|
||||
|
||||
result = append(result, xslices.Map(addr, func(addr *mail.Address) string {
|
||||
return addr.Address
|
||||
})...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := parseAddress("To"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := parseAddress("Cc"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := parseAddress("Bcc"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
@ -32,11 +32,14 @@ import (
|
|||
|
||||
const sendEntryExpiry = 30 * time.Minute
|
||||
|
||||
type SendRecorderID uint64
|
||||
|
||||
type sendRecorder struct {
|
||||
expiry time.Duration
|
||||
|
||||
entries map[string][]*sendEntry
|
||||
entriesLock sync.Mutex
|
||||
entries map[string][]*sendEntry
|
||||
entriesLock sync.Mutex
|
||||
cancelIDCounter uint64
|
||||
}
|
||||
|
||||
func newSendRecorder(expiry time.Duration) *sendRecorder {
|
||||
|
@ -47,6 +50,7 @@ func newSendRecorder(expiry time.Duration) *sendRecorder {
|
|||
}
|
||||
|
||||
type sendEntry struct {
|
||||
srID SendRecorderID
|
||||
msgID string
|
||||
toList []string
|
||||
exp time.Time
|
||||
|
@ -69,16 +73,17 @@ func (h *sendRecorder) tryInsertWait(
|
|||
hash string,
|
||||
toList []string,
|
||||
deadline time.Time,
|
||||
) (bool, error) {
|
||||
) (SendRecorderID, bool, error) {
|
||||
// If we successfully inserted the hash, we can return true.
|
||||
if h.tryInsert(hash, toList) {
|
||||
return true, nil
|
||||
srID, waitCh, ok := h.tryInsert(hash, toList)
|
||||
if ok {
|
||||
return srID, true, nil
|
||||
}
|
||||
|
||||
// A message with this hash is already being sent; wait for it.
|
||||
_, wasSent, err := h.wait(ctx, hash, deadline)
|
||||
_, wasSent, err := h.wait(ctx, hash, waitCh, srID, deadline)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to wait for message to be sent: %w", err)
|
||||
return 0, false, fmt.Errorf("failed to wait for message to be sent: %w", err)
|
||||
}
|
||||
|
||||
// If the message failed to send, try to insert it again.
|
||||
|
@ -86,18 +91,23 @@ func (h *sendRecorder) tryInsertWait(
|
|||
return h.tryInsertWait(ctx, hash, toList, deadline)
|
||||
}
|
||||
|
||||
return false, nil
|
||||
return srID, false, nil
|
||||
}
|
||||
|
||||
// hasEntryWait returns whether the given message already exists in the send recorder.
|
||||
// If it does, it waits for its ID to be known, then returns it and true.
|
||||
// If no entry exists, or it times out while waiting for its ID to be known, it returns false.
|
||||
func (h *sendRecorder) hasEntryWait(ctx context.Context, hash string, deadline time.Time) (string, bool, error) {
|
||||
if !h.hasEntry(hash) {
|
||||
func (h *sendRecorder) hasEntryWait(ctx context.Context,
|
||||
hash string,
|
||||
deadline time.Time,
|
||||
toList []string,
|
||||
) (string, bool, error) {
|
||||
srID, waitCh, found := h.getEntryWaitInfo(hash, toList)
|
||||
if !found {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
messageID, wasSent, err := h.wait(ctx, hash, deadline)
|
||||
messageID, wasSent, err := h.wait(ctx, hash, waitCh, srID, deadline)
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return "", false, nil
|
||||
} else if err != nil {
|
||||
|
@ -108,7 +118,7 @@ func (h *sendRecorder) hasEntryWait(ctx context.Context, hash string, deadline t
|
|||
return messageID, true, nil
|
||||
}
|
||||
|
||||
return h.hasEntryWait(ctx, hash, deadline)
|
||||
return h.hasEntryWait(ctx, hash, deadline, toList)
|
||||
}
|
||||
|
||||
func (h *sendRecorder) removeExpiredUnsafe() {
|
||||
|
@ -125,7 +135,7 @@ func (h *sendRecorder) removeExpiredUnsafe() {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *sendRecorder) tryInsert(hash string, toList []string) bool {
|
||||
func (h *sendRecorder) tryInsert(hash string, toList []string) (SendRecorderID, <-chan struct{}, bool) {
|
||||
h.entriesLock.Lock()
|
||||
defer h.entriesLock.Unlock()
|
||||
|
||||
|
@ -135,42 +145,50 @@ func (h *sendRecorder) tryInsert(hash string, toList []string) bool {
|
|||
if ok {
|
||||
for _, entry := range entries {
|
||||
if matchToList(entry.toList, toList) {
|
||||
return false
|
||||
return entry.srID, entry.waitCh, false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cancelID := h.newSendRecorderID()
|
||||
waitCh := make(chan struct{})
|
||||
|
||||
h.entries[hash] = append(entries, &sendEntry{
|
||||
srID: cancelID,
|
||||
exp: time.Now().Add(h.expiry),
|
||||
toList: toList,
|
||||
waitCh: make(chan struct{}),
|
||||
waitCh: waitCh,
|
||||
})
|
||||
|
||||
return true
|
||||
return cancelID, waitCh, true
|
||||
}
|
||||
|
||||
func (h *sendRecorder) hasEntry(hash string) bool {
|
||||
func (h *sendRecorder) getEntryWaitInfo(hash string, toList []string) (SendRecorderID, <-chan struct{}, bool) {
|
||||
h.entriesLock.Lock()
|
||||
defer h.entriesLock.Unlock()
|
||||
|
||||
h.removeExpiredUnsafe()
|
||||
|
||||
if _, ok := h.entries[hash]; ok {
|
||||
return true
|
||||
if entries, ok := h.entries[hash]; ok {
|
||||
for _, e := range entries {
|
||||
if matchToList(e.toList, toList) {
|
||||
return e.srID, e.waitCh, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return 0, nil, false
|
||||
}
|
||||
|
||||
// signalMessageSent should be called after a message has been successfully sent.
|
||||
func (h *sendRecorder) signalMessageSent(hash, msgID string, toList []string) {
|
||||
func (h *sendRecorder) signalMessageSent(hash string, srID SendRecorderID, msgID string) {
|
||||
h.entriesLock.Lock()
|
||||
defer h.entriesLock.Unlock()
|
||||
|
||||
entries, ok := h.entries[hash]
|
||||
if ok {
|
||||
for _, entry := range entries {
|
||||
if matchToList(entry.toList, toList) {
|
||||
if entry.srID == srID {
|
||||
entry.msgID = msgID
|
||||
entry.closeWaitChannel()
|
||||
return
|
||||
|
@ -181,7 +199,7 @@ func (h *sendRecorder) signalMessageSent(hash, msgID string, toList []string) {
|
|||
logrus.Warn("Cannot add message ID to send hash entry, it may have expired")
|
||||
}
|
||||
|
||||
func (h *sendRecorder) removeOnFail(hash string, toList []string) {
|
||||
func (h *sendRecorder) removeOnFail(hash string, id SendRecorderID) {
|
||||
h.entriesLock.Lock()
|
||||
defer h.entriesLock.Unlock()
|
||||
|
||||
|
@ -191,7 +209,7 @@ func (h *sendRecorder) removeOnFail(hash string, toList []string) {
|
|||
}
|
||||
|
||||
for idx, entry := range entries {
|
||||
if entry.msgID == "" && matchToList(entry.toList, toList) {
|
||||
if entry.srID == id && entry.msgID == "" {
|
||||
entry.closeWaitChannel()
|
||||
|
||||
remaining := xslices.Remove(entries, idx, 1)
|
||||
|
@ -204,15 +222,16 @@ func (h *sendRecorder) removeOnFail(hash string, toList []string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *sendRecorder) wait(ctx context.Context, hash string, deadline time.Time) (string, bool, error) {
|
||||
func (h *sendRecorder) wait(
|
||||
ctx context.Context,
|
||||
hash string,
|
||||
waitCh <-chan struct{},
|
||||
srID SendRecorderID,
|
||||
deadline time.Time,
|
||||
) (string, bool, error) {
|
||||
ctx, cancel := context.WithDeadline(ctx, deadline)
|
||||
defer cancel()
|
||||
|
||||
waitCh, ok := h.getWaitCh(hash)
|
||||
if !ok {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return "", false, ctx.Err()
|
||||
|
@ -225,21 +244,19 @@ func (h *sendRecorder) wait(ctx context.Context, hash string, deadline time.Time
|
|||
defer h.entriesLock.Unlock()
|
||||
|
||||
if entry, ok := h.entries[hash]; ok {
|
||||
return entry[0].msgID, true, nil
|
||||
for _, e := range entry {
|
||||
if e.srID == srID {
|
||||
return e.msgID, true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
func (h *sendRecorder) getWaitCh(hash string) (<-chan struct{}, bool) {
|
||||
h.entriesLock.Lock()
|
||||
defer h.entriesLock.Unlock()
|
||||
|
||||
if entry, ok := h.entries[hash]; ok {
|
||||
return entry[0].waitCh, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
func (h *sendRecorder) newSendRecorderID() SendRecorderID {
|
||||
h.cancelIDCounter++
|
||||
return SendRecorderID(h.cancelIDCounter)
|
||||
}
|
||||
|
||||
// getMessageHash returns the hash of the given message.
|
||||
|
|
|
@ -29,67 +29,73 @@ func TestSendHasher_Insert(t *testing.T) {
|
|||
h := newSendRecorder(sendEntryExpiry)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
hash1, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
srdID1, hash1, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash1)
|
||||
|
||||
// Simulate successfully sending the message.
|
||||
h.signalMessageSent(hash1, "abc", nil)
|
||||
h.signalMessageSent(hash1, srdID1, "abc")
|
||||
|
||||
// Inserting a message with the same hash should return false.
|
||||
_, ok, err = testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
srdID2, _, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.False(t, ok)
|
||||
require.Equal(t, srdID1, srdID2)
|
||||
|
||||
// Inserting a message with a different hash should return true.
|
||||
hash2, ok, err := testTryInsert(h, literal2, time.Now().Add(time.Second))
|
||||
srdID3, hash2, ok, err := testTryInsert(h, literal2, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash2)
|
||||
require.NotEqual(t, srdID3, srdID1)
|
||||
}
|
||||
|
||||
func TestSendHasher_Insert_Expired(t *testing.T) {
|
||||
h := newSendRecorder(time.Second)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
hash1, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
srID1, hash1, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash1)
|
||||
|
||||
// Simulate successfully sending the message.
|
||||
h.signalMessageSent(hash1, "abc", nil)
|
||||
h.signalMessageSent(hash1, srID1, "abc")
|
||||
|
||||
// Wait for the entry to expire.
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// Inserting a message with the same hash should return true because the previous entry has since expired.
|
||||
hash2, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
srID2, hash2, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
|
||||
// The hashes should be the same.
|
||||
require.Equal(t, hash1, hash2)
|
||||
|
||||
// Send IDs should differ
|
||||
require.NotEqual(t, srID2, srID1)
|
||||
}
|
||||
|
||||
func TestSendHasher_Insert_DifferentToList(t *testing.T) {
|
||||
h := newSendRecorder(time.Second)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
hash1, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second), []string{"abc", "def"}...)
|
||||
srID1, hash1, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second), []string{"abc", "def"}...)
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash1)
|
||||
|
||||
// Insert the same message into the hasher but with a different to list.
|
||||
hash2, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second), []string{"abc", "def", "ghi"}...)
|
||||
srID2, hash2, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second), []string{"abc", "def", "ghi"}...)
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash2)
|
||||
require.NotEqual(t, srID1, srID2)
|
||||
|
||||
// Insert the same message into the hasher but with the same to list.
|
||||
_, ok, err = testTryInsert(h, literal1, time.Now().Add(time.Second), []string{"abc", "def", "ghi"}...)
|
||||
_, _, ok, err = testTryInsert(h, literal1, time.Now().Add(time.Second), []string{"abc", "def", "ghi"}...)
|
||||
require.Error(t, err)
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
@ -98,7 +104,7 @@ func TestSendHasher_Wait_SendSuccess(t *testing.T) {
|
|||
h := newSendRecorder(sendEntryExpiry)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
srID1, hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash)
|
||||
|
@ -106,20 +112,21 @@ func TestSendHasher_Wait_SendSuccess(t *testing.T) {
|
|||
// Simulate successfully sending the message after half a second.
|
||||
go func() {
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
h.signalMessageSent(hash, "abc", nil)
|
||||
h.signalMessageSent(hash, srID1, "abc")
|
||||
}()
|
||||
|
||||
// Inserting a message with the same hash should fail.
|
||||
_, ok, err = testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
srID2, _, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.False(t, ok)
|
||||
require.Equal(t, srID1, srID2)
|
||||
}
|
||||
|
||||
func TestSendHasher_Wait_SendFail(t *testing.T) {
|
||||
h := newSendRecorder(sendEntryExpiry)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
srID1, hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash)
|
||||
|
@ -127,13 +134,14 @@ func TestSendHasher_Wait_SendFail(t *testing.T) {
|
|||
// Simulate failing to send the message after half a second.
|
||||
go func() {
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
h.removeOnFail(hash, nil)
|
||||
h.removeOnFail(hash, srID1)
|
||||
}()
|
||||
|
||||
// Inserting a message with the same hash should succeed because the first message failed to send.
|
||||
hash2, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
srID2, hash2, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEqual(t, srID2, srID1)
|
||||
|
||||
// The hashes should be the same.
|
||||
require.Equal(t, hash, hash2)
|
||||
|
@ -143,13 +151,13 @@ func TestSendHasher_Wait_Timeout(t *testing.T) {
|
|||
h := newSendRecorder(sendEntryExpiry)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
_, hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash)
|
||||
|
||||
// We should fail to insert because the message is not sent within the timeout period.
|
||||
_, _, err = testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
_, _, _, err = testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
|
@ -157,13 +165,13 @@ func TestSendHasher_HasEntry(t *testing.T) {
|
|||
h := newSendRecorder(sendEntryExpiry)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
srID1, hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash)
|
||||
|
||||
// Simulate successfully sending the message.
|
||||
h.signalMessageSent(hash, "abc", nil)
|
||||
h.signalMessageSent(hash, srID1, "abc")
|
||||
|
||||
// The message was already sent; we should find it in the hasher.
|
||||
messageID, ok, err := testHasEntry(h, literal1, time.Now().Add(time.Second))
|
||||
|
@ -176,7 +184,7 @@ func TestSendHasher_HasEntry_SendSuccess(t *testing.T) {
|
|||
h := newSendRecorder(sendEntryExpiry)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
srID1, hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash)
|
||||
|
@ -184,7 +192,7 @@ func TestSendHasher_HasEntry_SendSuccess(t *testing.T) {
|
|||
// Simulate successfully sending the message after half a second.
|
||||
go func() {
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
h.signalMessageSent(hash, "abc", nil)
|
||||
h.signalMessageSent(hash, srID1, "abc")
|
||||
}()
|
||||
|
||||
// The message was already sent; we should find it in the hasher.
|
||||
|
@ -202,15 +210,15 @@ func TestSendHasher_DualAddDoesNotCauseCrash(t *testing.T) {
|
|||
h := newSendRecorder(sendEntryExpiry)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
srID1, hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash)
|
||||
|
||||
// Simulate successfully sending the message. We call this method twice as it possible for multiple SMTP connections
|
||||
// to attempt to send the same message.
|
||||
h.signalMessageSent(hash, "abc", nil)
|
||||
h.signalMessageSent(hash, "abc", nil)
|
||||
h.signalMessageSent(hash, srID1, "abc")
|
||||
h.signalMessageSent(hash, srID1, "abc")
|
||||
|
||||
// The message was already sent; we should find it in the hasher.
|
||||
messageID, ok, err := testHasEntry(h, literal1, time.Now().Add(time.Second))
|
||||
|
@ -223,23 +231,52 @@ func TestSendHashed_MessageWithSameHasButDifferentRecipientsIsInserted(t *testin
|
|||
h := newSendRecorder(sendEntryExpiry)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second), "Receiver <receiver@pm.me>")
|
||||
srID1, hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second), "Receiver <receiver@pm.me>")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash)
|
||||
|
||||
hash2, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second), "Receiver <receiver@pm.me>", "Receiver2 <receiver2@pm.me>")
|
||||
srID2, hash2, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second), "Receiver <receiver@pm.me>", "Receiver2 <receiver2@pm.me>")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash2)
|
||||
require.Equal(t, hash, hash2)
|
||||
|
||||
// Should map to different requests
|
||||
require.NotEqual(t, srID2, srID1)
|
||||
}
|
||||
|
||||
func TestSendHashed_SameMessageWIthDifferentToListShouldWaitSuccessfullyAfterSend(t *testing.T) {
|
||||
// Check that if we send the same message twice with different recipients and the second message is somehow
|
||||
// sent before the first, ensure that we check if the message was sent we wait on the correct object.
|
||||
h := newSendRecorder(sendEntryExpiry)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
_, hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Minute), "Receiver <receiver@pm.me>")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash)
|
||||
|
||||
srID2, hash2, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Microsecond), "Receiver <receiver@pm.me>", "Receiver2 <receiver2@pm.me>")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash2)
|
||||
require.Equal(t, hash, hash2)
|
||||
|
||||
// simulate message sent
|
||||
h.signalMessageSent(hash2, srID2, "newID")
|
||||
|
||||
// Simulate Wait on message 2
|
||||
_, ok, err = h.hasEntryWait(context.Background(), hash2, time.Now().Add(time.Second), []string{"Receiver <receiver@pm.me>", "Receiver2 <receiver2@pm.me>"})
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
}
|
||||
|
||||
func TestSendHasher_HasEntry_SendFail(t *testing.T) {
|
||||
h := newSendRecorder(sendEntryExpiry)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
srID1, hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash)
|
||||
|
@ -247,7 +284,7 @@ func TestSendHasher_HasEntry_SendFail(t *testing.T) {
|
|||
// Simulate failing to send the message after half a second.
|
||||
go func() {
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
h.removeOnFail(hash, nil)
|
||||
h.removeOnFail(hash, srID1)
|
||||
}()
|
||||
|
||||
// The message failed to send; we should not find it in the hasher.
|
||||
|
@ -260,7 +297,7 @@ func TestSendHasher_HasEntry_Timeout(t *testing.T) {
|
|||
h := newSendRecorder(sendEntryExpiry)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
_, hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash)
|
||||
|
@ -275,13 +312,13 @@ func TestSendHasher_HasEntry_Expired(t *testing.T) {
|
|||
h := newSendRecorder(time.Second)
|
||||
|
||||
// Insert a message into the hasher.
|
||||
hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
srID1, hash, ok, err := testTryInsert(h, literal1, time.Now().Add(time.Second))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, hash)
|
||||
|
||||
// Simulate successfully sending the message.
|
||||
h.signalMessageSent(hash, "abc", nil)
|
||||
h.signalMessageSent(hash, srID1, "abc")
|
||||
|
||||
// Wait for the entry to expire.
|
||||
time.Sleep(time.Second)
|
||||
|
@ -410,25 +447,25 @@ func TestGetMessageHash(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func testTryInsert(h *sendRecorder, literal string, deadline time.Time, toList ...string) (string, bool, error) { //nolint:unparam
|
||||
func testTryInsert(h *sendRecorder, literal string, deadline time.Time, toList ...string) (SendRecorderID, string, bool, error) { //nolint:unparam
|
||||
hash, err := getMessageHash([]byte(literal))
|
||||
if err != nil {
|
||||
return 0, "", false, err
|
||||
}
|
||||
|
||||
srID, ok, err := h.tryInsertWait(context.Background(), hash, toList, deadline)
|
||||
if err != nil {
|
||||
return 0, "", false, err
|
||||
}
|
||||
|
||||
return srID, hash, ok, nil
|
||||
}
|
||||
|
||||
func testHasEntry(h *sendRecorder, literal string, deadline time.Time, toList ...string) (string, bool, error) { //nolint:unparam
|
||||
hash, err := getMessageHash([]byte(literal))
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
ok, err := h.tryInsertWait(context.Background(), hash, toList, deadline)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
return hash, ok, nil
|
||||
}
|
||||
|
||||
func testHasEntry(h *sendRecorder, literal string, deadline time.Time) (string, bool, error) { //nolint:unparam
|
||||
hash, err := getMessageHash([]byte(literal))
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
return h.hasEntryWait(context.Background(), hash, deadline)
|
||||
return h.hasEntryWait(context.Background(), hash, deadline, toList)
|
||||
}
|
||||
|
|
|
@ -81,7 +81,8 @@ func (user *User) sendMail(authID string, from string, to []string, r io.Reader)
|
|||
}
|
||||
|
||||
// Check if we already tried to send this message recently.
|
||||
if ok, err := user.sendHash.tryInsertWait(ctx, hash, to, time.Now().Add(90*time.Second)); err != nil {
|
||||
srID, ok, err := user.sendHash.tryInsertWait(ctx, hash, to, time.Now().Add(90*time.Second))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check send hash: %w", err)
|
||||
} else if !ok {
|
||||
user.log.Warn("A duplicate message was already sent recently, skipping")
|
||||
|
@ -89,7 +90,7 @@ func (user *User) sendMail(authID string, from string, to []string, r io.Reader)
|
|||
}
|
||||
|
||||
// If we fail to send this message, we should remove the hash from the send recorder.
|
||||
defer user.sendHash.removeOnFail(hash, to)
|
||||
defer user.sendHash.removeOnFail(hash, srID)
|
||||
|
||||
// Create a new message parser from the reader.
|
||||
parser, err := parser.New(bytes.NewReader(b))
|
||||
|
@ -162,7 +163,7 @@ func (user *User) sendMail(authID string, from string, to []string, r io.Reader)
|
|||
}
|
||||
|
||||
// If the message was successfully sent, we can update the message ID in the record.
|
||||
user.sendHash.signalMessageSent(hash, sent.ID, to)
|
||||
user.sendHash.signalMessageSent(hash, srID, sent.ID)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
|
|
@ -355,7 +355,7 @@ func (user *User) syncMessages(
|
|||
case syncLimits.MaxSyncMemory == 2*Gigabyte:
|
||||
// Increasing the max download capacity has very little effect on sync speed. We could increase the download
|
||||
// memory but the user would see less sync notifications. A smaller value here leads to more frequent
|
||||
// updates. Additionally, most of ot sync time is spent in the message building.
|
||||
// updates. Additionally, most of sync time is spent in the message building.
|
||||
syncMaxDownloadRequestMem = syncLimits.MaxDownloadRequestMem
|
||||
// Currently limited so that if a user has multiple accounts active it also doesn't cause excessive memory usage.
|
||||
syncMaxMessageBuildingMem = syncLimits.MaxMessageBuildingMem
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
|
||||
var ErrInvalidReplyType = errors.New("reply type does not match")
|
||||
|
||||
// Utilities to implement Chanel Procedure Calls. Similar in concept to RPC, but with between go-routines.
|
||||
// Utilities to implement Channel Procedure Calls. Similar in concept to RPC, but with between go-routines.
|
||||
|
||||
// Request contains the data for a request as well as the means to reply to a request.
|
||||
type Request struct {
|
||||
|
|
|
@ -115,7 +115,7 @@ func (h *macOSHelper) Get(secretURL string) (string, string, error) {
|
|||
|
||||
results, err := keychain.QueryItem(query)
|
||||
if err != nil {
|
||||
l.WithError(err).Error("Querry item failed")
|
||||
l.WithError(err).Error("Query item failed")
|
||||
return "", "", parseError(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -205,7 +205,7 @@ func EncodeHeader(s string) string {
|
|||
return mime.QEncoding.Encode("utf-8", s)
|
||||
}
|
||||
|
||||
// DecodeCharset decodes the orginal using content type parameters.
|
||||
// DecodeCharset decodes the original using content type parameters.
|
||||
// If the charset parameter is missing it checks that the content is valid utf8.
|
||||
// If it isn't, it checks if it's embedded in the html/xml.
|
||||
// If it isn't, it falls back to windows-1252.
|
||||
|
@ -240,7 +240,7 @@ func DecodeCharset(original []byte, contentType string) ([]byte, error) {
|
|||
logrus.WithField("encoding", name).Warn("Determined encoding but was not certain")
|
||||
}
|
||||
|
||||
// Reencode as UTF-8.
|
||||
// Re-encode as UTF-8.
|
||||
decoded, err := encoding.NewDecoder().Bytes(original)
|
||||
if err != nil {
|
||||
return original, errors.Wrap(err, "failed to decode as windows-1252")
|
||||
|
|
|
@ -446,7 +446,7 @@ func TestEncodeReader(t *testing.T) {
|
|||
}
|
||||
|
||||
if bytes.Equal(decoded, expected) {
|
||||
// fmt.Println("Succesfull decoding of ", val.params, ":", string(decoded))
|
||||
// fmt.Println("Successful decoding of ", val.params, ":", string(decoded))
|
||||
} else {
|
||||
t.Error("Wrong encoding of ", val.charset, ".Expected\n", expected, "\nbut have\n", decoded)
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// maxFileSize limit tre single file size after decopression is not larger than 1GB.
|
||||
// maxFileSize limit the single file size after decompression is not larger than 1GB.
|
||||
const maxFileSize = int64(1 * 1024 * 1024 * 1024) // 1 GB
|
||||
|
||||
// ErrFileTooLarge returned when decompressed file is too large.
|
||||
|
|
|
@ -1,3 +1,238 @@
|
|||
## v3.3.1
|
||||
- 2023-07-07
|
||||
|
||||
### New
|
||||
- Improved Bridge debugging capabilities by adding more information to the application logs
|
||||
- Started measuring the Bridge setup experience
|
||||
|
||||
### Fixed
|
||||
- Temporarily removed clickable notifications on Linux to not raise the Bridge window when receiving non-Bridge notifications
|
||||
|
||||
|
||||
## v3.3.0
|
||||
- 2023-06-08
|
||||
|
||||
### New
|
||||
- Reduced the number of occasions when email clients ask for Bridge credentials
|
||||
- Added new Bridge notifications to help users to configure and troubleshoot their email clients
|
||||
- To avoid the need to reconfigure email clients, Bridge remembers the old account password when an account is re-added (removed and added again)
|
||||
- Further improved logging to support troubleshooting
|
||||
- 2 factor authentication (2FA) is submitted automatically after entering a code
|
||||
- Removed the requirement of having an administrator account on macOS to install Bridge
|
||||
|
||||
### Fixed
|
||||
- Fixed numerous crashes
|
||||
- Fixed the case when an email could not be sent if a PDF was attached to the email
|
||||
- Added varioius bugfixes and security improvemenets
|
||||
- Reduced the Bridge cache size by cleaning up temporary emails that were saved during failed initial synchronizations
|
||||
- Further reduced the chance of desyncronization between the email client and Bridge
|
||||
|
||||
|
||||
## v3.2.0
|
||||
- 2023-05-15
|
||||
|
||||
### New
|
||||
- Enhanced Proton infrastructure protection
|
||||
- Enhanced the integration with the operating system by replacing status windows with native tray icon context menu
|
||||
- Switched to two columns layout on the account details page to make the informaion easier to access
|
||||
- Improved logs to support troubleshooting
|
||||
- Added optional usage sharing to support user experience improvements. Additional information about data sharing can be found on our [support page](https://proton.me/support/share-usage-statistics).
|
||||
- Implemented smart picking of default IMAP and SMTP ports
|
||||
- Added various security and performance improvements
|
||||
|
||||
### Fixed
|
||||
- Replaced invalid email addresses with empty field for new drafts so it can be syncronized across Proton clients
|
||||
- Improved crash handling
|
||||
- Fixed label / unlabel performance when applied on large amount of emails
|
||||
- Fixed "reply to" related issues
|
||||
- Updated build instructions
|
||||
- Announced IMAP ID capability to email clients
|
||||
|
||||
|
||||
## v3.1.3
|
||||
- 2023-05-10
|
||||
|
||||
### Fixed
|
||||
- Added a missing error handler that can make the initial synchronization to stuck
|
||||
|
||||
|
||||
## v3.1.2
|
||||
- 2023-04-27
|
||||
|
||||
### New
|
||||
- Optimized Recovered Messages folder size by not adding a message to the folder if that message has been added to it before (deduplication)
|
||||
|
||||
## v3.1.1
|
||||
- 2023-04-11
|
||||
|
||||
### Fixed
|
||||
- Improved exception / crash handling
|
||||
|
||||
## v3.1.0
|
||||
- 2023-04-05
|
||||
|
||||
### New
|
||||
- Significantly reduced memory consumption both during synchronization and communication with email clients
|
||||
- Added synchronization indicator to the graphical user interface (GUI)
|
||||
- Added "Close window" and "Quit Bridge" buttons to the main window
|
||||
- Added command line switches to control GUI rendering
|
||||
- Switched to software rendering on Windows to support old graphics cards
|
||||
- Added support for Proton's Scheduled send feature
|
||||
- Avoided making email clients to ask for Bridge credentials when they started faster than Bridge at startup
|
||||
- Added a notification when a user is signed out from Bridge in the background
|
||||
- Improved desynchronization avoidence by setting UIDValidity from the current time
|
||||
- Started updating emails in the email clients frequently when Bridge is started after not being online for longer period of time
|
||||
- Improved error detection and handling
|
||||
|
||||
### Fixed
|
||||
- Fixed transparent window with old graphics cards or virtual machines on Windows
|
||||
- Reduced notifications that does not require user actions
|
||||
- Improved exception / crash handling
|
||||
- Improved handling complex MIME types
|
||||
- Reduced the source of errors that can lead to gRPC related error messages
|
||||
- Fixed sub-folder rename issues
|
||||
- Fixed various bugs related to secure vault handling, network communication errors, Proton server communication, operating system integration.
|
||||
|
||||
|
||||
## v3.0.21
|
||||
- 2023-03-23
|
||||
|
||||
### New
|
||||
- Extended the migration from the previous major Bridge version with certificates
|
||||
- Improved error detection
|
||||
|
||||
### Fixed
|
||||
- Fixed the misplaced .desktop file on Linux
|
||||
- Fixed DBUS secret service integration (e.g., KWallet, KeePass)
|
||||
- Made Bridge more resilient against Proton server outages
|
||||
|
||||
|
||||
## v3.0.20
|
||||
- 2023-03-09
|
||||
|
||||
### New
|
||||
- Added better explanation when an email cannot be sent because of non-existing email addresses
|
||||
- Added a dialog to Bridge where users can repair the application when it encounters an internal error
|
||||
- Improved error detection
|
||||
|
||||
### Fixed
|
||||
- Reduced the cases when Bridge could not restart automatically
|
||||
- Fixed the bug that could cause email states (e.g., read, unread, answered) to come out of sync with the web application. **NOTE: This fix is only applied to new emails. In order to fix older emails in Bridge, the account in Bridge needs to be removed and added back.**
|
||||
- Fixed incorrect subject parsing caused by double quotes
|
||||
|
||||
|
||||
## v3.0.19
|
||||
- 2023-03-01
|
||||
|
||||
### New
|
||||
- Improved inter-process communication error detection
|
||||
- Improved exceptions related error detection
|
||||
|
||||
### Fixed
|
||||
- Fixed numerous sources of errors leading to logout (internal errors)
|
||||
- Fixed inter-process communication related startup issues (e.g., gRPC, service configuration file exchange)
|
||||
|
||||
|
||||
## v3.0.18
|
||||
- 2023-02-24
|
||||
|
||||
### New
|
||||
- Improved event processing related error handling
|
||||
|
||||
### Fixed
|
||||
- Fixed manual update errors on Windows by ensuring that all new files are deployed by the Bridge installer
|
||||
|
||||
|
||||
## v3.0.16
|
||||
- 2023-02-17
|
||||
|
||||
### Fixed
|
||||
- Desynchronization while creating draft.
|
||||
|
||||
|
||||
## v3.0.15
|
||||
- 2023-02-14
|
||||
|
||||
### Fixed
|
||||
- Better network error handling
|
||||
|
||||
|
||||
## v3.0.14
|
||||
- 2023-02-09
|
||||
|
||||
### New
|
||||
- Improved error detection
|
||||
|
||||
### Fixed
|
||||
- Fixed the sync issues that can happen when updating from an earlier v3 version
|
||||
- Improved attachment handling by setting proper MIME parameters
|
||||
- Improved update processing while Bridge is not active or performs a synchronization with Proton servers
|
||||
|
||||
## v3.0.12
|
||||
- 2023-02-01
|
||||
|
||||
### New
|
||||
- Changed the default location of the database and storage files. **NOTE: Please delete the old cache location if necessary.**
|
||||
- Optimised cache, database and storage placement
|
||||
- Improved email sending performance
|
||||
- Improved unexpected event handling
|
||||
|
||||
### Fixed
|
||||
- Outlook does not show sent messages as drafts
|
||||
- Improved 'Reply to' behaviour
|
||||
|
||||
|
||||
## v3.0.10
|
||||
- 2023-01-17
|
||||
|
||||
### New
|
||||
- Program argument to use software rendering.
|
||||
- Improved exception handling in GUI.
|
||||
|
||||
### Fixed
|
||||
- API event processing more robust.
|
||||
- Improve the startup process.
|
||||
- Fixed sub-folder creation bug.
|
||||
|
||||
|
||||
## v3.0.9
|
||||
- 2023-01-05
|
||||
|
||||
### New
|
||||
- Added an option to the GUI to export TLS certificates
|
||||
- Increased tolerance of invalid messages
|
||||
|
||||
### Fixed
|
||||
- Autostart is set only when changed by the user
|
||||
- Folders that are created during initial sync are synchronized correctly
|
||||
- Improved settings migration from 2.x to 3.x
|
||||
- Error reporting improvements on Intel Macs
|
||||
- Show the setup guide after the first login
|
||||
- User name and password validation messages are shown only when the Sign in button is pressed
|
||||
- The Bridge main window is not shown on startup or after a crash
|
||||
- Sign in button is not greyed out after the first login
|
||||
|
||||
|
||||
## v3.0.8
|
||||
- 2022-12-20
|
||||
|
||||
### New
|
||||
- Improved error detection when Proton server updates cannot be processed
|
||||
|
||||
### Fixed
|
||||
- Proton server update processing will not stop after a folder update failure
|
||||
|
||||
## v3.0.7
|
||||
- 2022-12-19
|
||||
|
||||
### New
|
||||
- Increase worker count (performance improvement)
|
||||
|
||||
### Fixed
|
||||
- Bridge password migration from 2.x to 3.x
|
||||
- Ensure proper handling of folders and labels with non-US ASCII chars
|
||||
|
||||
|
||||
## v3.0.6
|
||||
- 2022-12-12
|
||||
|
||||
|
@ -178,7 +413,7 @@
|
|||
- Improved Sentry reporting
|
||||
|
||||
### Fixed
|
||||
- Ensure messageID is properly removed from DB when it is no logner present on the API
|
||||
- Ensure messageID is properly removed from DB when it is no longer present on the API
|
||||
|
||||
|
||||
## v2.1.0
|
||||
|
@ -372,7 +607,7 @@ New local cache
|
|||
- Added IMAP requests to the logs for easier debugging
|
||||
|
||||
### Fixed
|
||||
- NoGUI bulid
|
||||
- NoGUI build
|
||||
- Background of GUI welcome message
|
||||
- Incorrect total mailbox size displayed in Apple Mail
|
||||
|
||||
|
|
|
@ -1,31 +1,157 @@
|
|||
## v2.4.8
|
||||
- 2022-11-22
|
||||
## v3.3.0
|
||||
- 2023-06-20
|
||||
|
||||
### New
|
||||
- Native Mac M1 release
|
||||
- Upgrade to Qt 6:
|
||||
- Change the app architecture
|
||||
- Drop therecipe/qt dependency
|
||||
- Update to go1.18
|
||||
- Update to Qt 6.3.2
|
||||
- Ensured the use of random port for gRPC
|
||||
- Implemented token exchange for identity validation
|
||||
- Ensured gRPC generates its own TLS certificate
|
||||
- Increased bridge-gui timeout for gRPC server connection
|
||||
- Added new warnings for 'TLS pinning' and 'no active key for recipient' errors
|
||||
- GUI improvements
|
||||
- More verbose logs for GUI-related issues
|
||||
- New icon for .dmg installer
|
||||
- Reduced the number of occasions when email clients ask for Bridge credentials
|
||||
- Added new Bridge notifications to help users to configure and troubleshoot their email clients
|
||||
- To avoid the need to reconfigure email clients, Bridge remembers the old account password when an account is re-added (removed and added again)
|
||||
- Further improved logging to support troubleshooting
|
||||
- 2 factor authentication (2FA) is submitted automatically after entering a code
|
||||
- Removed the requirement of having an administrator account on macOS to install Bridge
|
||||
|
||||
### Fixed
|
||||
- Fixed numerous crashes
|
||||
- Fixed the case when an email could not be sent if a PDF was attached to the email
|
||||
- Added varioius bugfixes and security improvemenets
|
||||
- Reduced the Bridge cache size by cleaning up temporary emails that were saved during failed initial synchronizations
|
||||
- Further reduced the chance of desyncronization between the email client and Bridge
|
||||
|
||||
|
||||
## v3.2.0
|
||||
- 2023-05-26
|
||||
|
||||
### New
|
||||
- Enhanced Proton infrastructure protection
|
||||
- Enhanced the integration with the operating system by replacing status windows with native tray icon context menu
|
||||
- Switched to two columns layout on the account details page to make the informaion easier to access
|
||||
- Improved logs to support troubleshooting
|
||||
- Added optional usage sharing to support user experience improvements. Additional information about data sharing can be found on our [support page](https://proton.me/support/share-usage-statistics).
|
||||
- Implemented smart picking of default IMAP and SMTP ports
|
||||
- Added various security and performance improvements
|
||||
|
||||
### Fixed
|
||||
- Replaced invalid email addresses with empty field for new drafts so it can be syncronized across Proton clients
|
||||
- Improved crash handling
|
||||
- Fixed label / unlabel performance when applied on large amount of emails
|
||||
- Fixed "reply to" related issues
|
||||
- Updated build instructions
|
||||
- Announced IMAP ID capability to email clients
|
||||
|
||||
## v3.1.3
|
||||
- 2023-05-10
|
||||
|
||||
### Fixed
|
||||
- Added a missing error handler that can make the initial synchronization to stuck
|
||||
|
||||
|
||||
## v3.1.2
|
||||
- 2023-04-27
|
||||
|
||||
### New
|
||||
- Significantly reduced memory consumption both during synchronization and communication with email clients
|
||||
- Added synchronization indicator to the graphical user interface (GUI)
|
||||
- Added "Close window" and "Quit Bridge" buttons to the main window
|
||||
- Added command line switches to control GUI rendering
|
||||
- Switched to software rendering on Windows to support old graphics cards
|
||||
- Added support for Proton's Scheduled send feature
|
||||
- Avoided making email clients to ask for Bridge credentials when they started faster than Bridge at startup
|
||||
- Added a notification when a user is signed out from Bridge in the background
|
||||
- Improved desynchronization avoidence by setting UIDValidity from the current time
|
||||
- Started updating emails in the email clients frequently when Bridge is started after not being online for longer period of time
|
||||
- Improved error detection and handling
|
||||
- Optimized Recovered Messages folder size by not adding a message to the folder if that message has been added to it before (deduplication)
|
||||
|
||||
### Fixed
|
||||
- Fixed transparent window with old graphics cards or virtual machines on Windows
|
||||
- Reduced notifications that does not require user actions
|
||||
- Improved exception / crash handling
|
||||
- Improved handling complex MIME types
|
||||
- Reduced the source of errors that can lead to gRPC related error messages
|
||||
- Fixed sub-folder rename issues
|
||||
- Fixed various bugs related to secure vault handling, network communication errors, Proton server communication, operating system integration.
|
||||
|
||||
|
||||
## v3.0.21
|
||||
- 2023-03-23
|
||||
|
||||
### New
|
||||
- Extended the migration from the previous major Bridge version with certificates
|
||||
- Improved error detection
|
||||
|
||||
### Fixed
|
||||
- Fixed the misplaced .desktop file on Linux
|
||||
- Fixed DBUS secret service integration (e.g., KWallet, KeePass)
|
||||
- Made Bridge more resilient against Proton server outages
|
||||
|
||||
|
||||
## v3.0.20
|
||||
- 2023-03-09
|
||||
|
||||
### New
|
||||
- Added better explanation when an email cannot be sent because of non-existing email addresses
|
||||
- Added a dialog to Bridge where users can repair the application when it encounters an internal error
|
||||
- Improved error detection
|
||||
|
||||
### Fixed
|
||||
- Reduced the cases when Bridge could not restart automatically
|
||||
- Fixed the bug that could cause email states (e.g., read, unread, answered) to come out of sync with the web application. **NOTE: This fix is only applied to new emails. In order to fix older emails in Bridge, the account in Bridge needs to be removed and added back.**
|
||||
- Fixed incorrect subject parsing caused by double quotes
|
||||
|
||||
|
||||
## v3.0.19
|
||||
- 2023-03-01
|
||||
|
||||
### New
|
||||
- Improved inter-process communication error detection
|
||||
- Improved exceptions related error detection
|
||||
|
||||
### Fixed
|
||||
- Fixed numerous sources of errors leading to logout (internal errors)
|
||||
- Fixed inter-process communication related startup issues (e.g., gRPC, service configuration file exchange)
|
||||
|
||||
|
||||
## v3.0.18
|
||||
- 2023-02-24
|
||||
|
||||
### New
|
||||
- Improved event processing related error handling
|
||||
|
||||
### Fixed
|
||||
- Fixed manual update errors on Windows by ensuring that all new files are deployed by the Bridge installer
|
||||
|
||||
|
||||
## v3.0.17
|
||||
- 2023-02-22
|
||||
|
||||
### New
|
||||
- Rewrote a significant part of Bridge to improve overall Bridge stability and performance
|
||||
- Open sourced and integrated a new IMAP library, Gluon (https://github.com/ProtonMail/gluon)
|
||||
- Open sourced and integrated a new Proton API library, Go Proton API (GPA) (https://github.com/ProtonMail/go-proton-api)
|
||||
- Significantly improved error detection and unexpected error handling
|
||||
- Improved email sending performance
|
||||
- Improved synchronization performance
|
||||
- Added new command line argument for software rendering
|
||||
- Extended the coverage of the Bridge data that is encrypted on the users' computers
|
||||
- Added an option to the graphical user interface to export TLS certificates
|
||||
- Reimplemented the user interface (upgraded to the Qt 6 user interface library)
|
||||
- Added native Apple Silicon macOS support
|
||||
- Added an option to change IMAP connection mode
|
||||
- Added subfolder support
|
||||
- Added a new icon for the .dmg installer
|
||||
- Increased automated test coverage
|
||||
|
||||
### Fixed
|
||||
- Desynchronization while creating a draft email
|
||||
- Fixed sub-folder creation issues
|
||||
- User name and password validation messages are shown only when the Sign in button is pressed
|
||||
- Improved handling SMTP send deduplication
|
||||
- Improved robustness of Bridge restart
|
||||
- The notification for when Bridge ports are occupied
|
||||
- Fixed the user notification for occupied Bridge ports
|
||||
- Fixed vulnerabilities of golang.org/x/crypto
|
||||
- Missing Library on Fedora/Gnome upgrade form 2.3 to 2.4
|
||||
- Added Digital-Signature for DLLs (Windows Security Alert to show Bridge as coming from a trusted publisher)
|
||||
- Change download and version check urls to proton.me
|
||||
- Fixed manual check for updates after switching the update channel
|
||||
- Fixes to the update process on Linux and Windows (qt6 related)
|
||||
- Added the missing Library on Fedora/Gnome for 2.3 to 2.4 update
|
||||
- Added digital-signature for DLLs (to avoid the Windows Security alert, and to show if Bridge is coming from a trusted publisher)
|
||||
- Fixed many Qt 6 related Linux and Windows update process issues
|
||||
- Changed the default location of the database and storage files to avoid conflicts with cache cleaner applications
|
||||
|
||||
|
||||
## v2.3.0
|
||||
|
@ -89,7 +215,7 @@
|
|||
- Improved Sentry reporting
|
||||
|
||||
### Fixed
|
||||
- Ensure messageID is properly removed from DB when it is no logner present on the API
|
||||
- Ensure messageID is properly removed from DB when it is no longer present on the API
|
||||
|
||||
|
||||
## v2.1.0
|
||||
|
@ -251,7 +377,7 @@ Other
|
|||
- Fixed GUI freeze while switching to early update channel
|
||||
- Fixed Bridge autostart
|
||||
- Improved signing of update packages
|
||||
- NoGUI bulid
|
||||
- NoGUI build
|
||||
- Background of GUI welcome message
|
||||
- Incorrect total mailbox size displayed in Apple Mail
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ Feature: IMAP remove messages from Trash
|
|||
| label | label |
|
||||
Then it succeeds
|
||||
|
||||
Scenario Outline: Message in Trash and some other label is not permanently deleted
|
||||
Scenario Outline: Message in Trash and some other label is permanently deleted
|
||||
Given the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Trash":
|
||||
| from | to | subject | body |
|
||||
| john.doe@mail.com | [user:user]@[domain] | foo | hello |
|
||||
|
@ -27,8 +27,8 @@ Feature: IMAP remove messages from Trash
|
|||
When IMAP client "1" expunges
|
||||
Then it succeeds
|
||||
And IMAP client "1" eventually sees 1 messages in "Trash"
|
||||
And IMAP client "1" eventually sees 2 messages in "All Mail"
|
||||
And IMAP client "1" eventually sees 1 messages in "Labels/label"
|
||||
And IMAP client "1" eventually sees 1 messages in "All Mail"
|
||||
And IMAP client "1" eventually sees 0 messages in "Labels/label"
|
||||
|
||||
Scenario Outline: Message in Trash only is permanently deleted
|
||||
Given the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Trash":
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
This is version of libqcocoa obtained from original Qt 5.13.0 source available at https://github.com/qt/qtbase with patch from cocoa.patch applyed.
|
||||
This is version of libqcocoa obtained from original Qt 5.13.0 source available at https://github.com/qt/qtbase with patch from cocoa.patch applied.
|
||||
|
||||
Please refer to https://bugreports.qt.io/browse/QTBUG-88600 for patch origin and https://doc.qt.io/qt-5/macos-building.html for instructions how to build Qt from source.
|
||||
|
|
|
@ -31,7 +31,7 @@ echo "0">$ERROR_COUNT_FILE
|
|||
# -- Helper functions -- #
|
||||
##########################
|
||||
|
||||
# err print out a given error ($2) and line where it hapens ($1),
|
||||
# err print out a given error ($2) and line where it happens ($1),
|
||||
# also it increases count of errors.
|
||||
err () {
|
||||
echo "CHANGELOG-LINTER: $2 on the following line:"
|
||||
|
@ -92,7 +92,7 @@ check_change_types () {
|
|||
|
||||
case "$1" in
|
||||
"### Added"|"### Changed"|"### Deprecated"|"### Removed"|"### Fixed"|"### Security") ;; # Standard keepachangelog.com compliant types.
|
||||
"### Release notes"|"### Fixed bugs") ;; # Bridge aditional in app release notes types.
|
||||
"### Release notes"|"### Fixed bugs") ;; # Bridge additional in app release notes types.
|
||||
"### Guiding Principles"|"### Types of changes") ;; # Ignoring guide at the end of the changelog.
|
||||
*) err "$1" "Change type must be one of the Added, Changed, Deprecated, Removed, Fixed, Hoftix"
|
||||
esac
|
||||
|
|
|
@ -45,7 +45,7 @@ generate_dep_licenses(){
|
|||
grep -E $'^\t[^=>]*$' $src | sed -r 's/\t([^ ]*) v.*/\1/g' > "$tmpDepLicenses"
|
||||
grep -E $'^\t.*=>.*v.*$' $src | sed -r 's/^.*=> ([^ ]*)( v.*)?/\1/g' >> "$tmpDepLicenses"
|
||||
|
||||
# Replace each line with formated link
|
||||
# Replace each line with formatted link
|
||||
sed -i -r '/^github.com\/therecipe\/qt\/internal\/binding\/files\/docs\//d;' "$tmpDepLicenses"
|
||||
sed -i -r 's|^(.*)/([[:alnum:]-]+)/(v[[:digit:]]+)$|* [\2](https://\1/\2/\3)|g' "$tmpDepLicenses"
|
||||
sed -i -r 's|^(.*)/([[:alnum:]-]+)$|* [\2](https://\1/\2)|g' "$tmpDepLicenses"
|
||||
|
|
|
@ -35,14 +35,16 @@ if ! which pandoc; then
|
|||
fi
|
||||
|
||||
# Check Pandoc version
|
||||
PANDOC_VERSION=`pandoc --version | grep --color=never -m 1 "pandoc" | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p'`
|
||||
PANDOC_VERSION=$(pandoc --version | grep --color=never -m 1 "pandoc" | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p')
|
||||
printf "PANDOC FOUND ! version : %s\n", "$PANDOC_VERSION"
|
||||
|
||||
# self-contained is deprecated since 2.19 in profit of --embed-resource option
|
||||
DEPRECATING_VERSION="2.19.0"
|
||||
# Build release notes
|
||||
if [ "$(printf '%s\n' "$requiredver" "$PANDOC_VERSION" | sort -V | head -n1)" = "$DEPRECATING_VERSION" ]; then
|
||||
function ver { printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' ' '); }
|
||||
|
||||
if [ $(ver $PANDOC_VERSION) -lt $(ver $DEPRECATING_VERSION) ]; then
|
||||
pandoc "$INFILE" -f markdown -t html -s -o "$OUTFILE" -c utils/release_notes.css --self-contained --section-divs --metadata title="Release notes - Proton Mail Bridge - $CHANNEL"
|
||||
else
|
||||
pandoc "$INFILE" -f markdown -t html -s -o "$OUTFILE" -c utils/release_notes.css --embed-resource --standalone --section-divs --metadata title="Release notes - Proton Mail Bridge - $CHANNEL"
|
||||
fi
|
||||
|
||||
|
||||
|
|
|
@ -20,10 +20,10 @@
|
|||
# The Qt libs are dynamically loaded with rules like: `@rpath/QtGui.framework/Versions/5/QtGui`
|
||||
# @rpath instructs the dynamic linker to search a list of paths in order to locate the framework
|
||||
# The rules can be listed using `otool -l "${path_to_binary}"`
|
||||
# The building process of therecipe/qt or qmake leaves the rules with additinal unwanted paths
|
||||
# The building process of therecipe/qt or qmake leaves the rules with additional unwanted paths
|
||||
# + absolute path to build directory
|
||||
# + dummy replacement `/break_the_rpath`
|
||||
# We need to manually remove those and add the path relative to exectuable: `@executable_path/../Frameworks`
|
||||
# We need to manually remove those and add the path relative to executable: `@executable_path/../Frameworks`
|
||||
|
||||
path_to_binary=$1
|
||||
|
||||
|
|
Loading…
Reference in New Issue