Created
November 3, 2025 15:56
-
-
Save babolivier/d9f1b0198112d0dd194d69a3525eed51 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/mailnews/protocols/ews/src/EwsIncomingServer.cpp b/mailnews/protocols/ews/src/EwsIncomingServer.cpp | |
| --- a/mailnews/protocols/ews/src/EwsIncomingServer.cpp | |
| +++ b/mailnews/protocols/ews/src/EwsIncomingServer.cpp | |
| @@ -104,6 +104,38 @@ NS_IMETHODIMP EwsBiffUrlListener::OnStop | |
| } // namespace | |
| +/** | |
| + * A timer used to delay requests when syncing multiple folders at once. See the | |
| + * comment in `EwsIncomingServer::SyncFolders` for more information. | |
| + */ | |
| +class EwsNTLMSyncTimer : public nsITimerCallback, public nsINamed { | |
| + public: | |
| + NS_DECL_ISUPPORTS | |
| + NS_DECL_NSITIMERCALLBACK | |
| + NS_DECL_NSINAMED | |
| + | |
| + explicit EwsNTLMSyncTimer(std::function<void()> done) | |
| + : mDone(std::move(done)) {}; | |
| + | |
| + protected: | |
| + virtual ~EwsNTLMSyncTimer() = default; | |
| + | |
| + private: | |
| + std::function<void()> mDone; | |
| +}; | |
| + | |
| +NS_IMPL_ISUPPORTS(EwsNTLMSyncTimer, nsITimerCallback, nsINamed) | |
| + | |
| +NS_IMETHODIMP EwsNTLMSyncTimer::GetName(nsACString& name) { | |
| + name.Assign("EwsNTLMSyncTimer"); | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP EwsNTLMSyncTimer::Notify(nsITimer* timer) { | |
| + mDone(); | |
| + return NS_OK; | |
| +} | |
| + | |
| NS_IMPL_ADDREF_INHERITED(EwsIncomingServer, nsMsgIncomingServer) | |
| NS_IMPL_RELEASE_INHERITED(EwsIncomingServer, nsMsgIncomingServer) | |
| NS_IMPL_QUERY_HEAD(EwsIncomingServer) | |
| @@ -498,20 +530,65 @@ nsresult EwsIncomingServer::SyncFolderLi | |
| nsresult EwsIncomingServer::SyncFolders( | |
| const nsTArray<RefPtr<nsIMsgFolder>>& folders, nsIMsgWindow* aMsgWindow, | |
| nsIUrlListener* urlListener) { | |
| + nsMsgAuthMethodValue authMethod; | |
| + MOZ_TRY(GetAuthMethod(&authMethod)); | |
| + | |
| // TODO: For now, we sync every folder at once, but obviously that's not an | |
| // amazing solution. In the future, we should probably try to maintain some | |
| // kind of queue so we can properly batch and sync folders. In the meantime, | |
| // though, the EWS client should handle any kind of rate limiting well enough, | |
| // so this improvement can come later. | |
| + uint32_t delay = 0; | |
| + nsCOMPtr<nsIMsgWindow> window = aMsgWindow; | |
| + nsCOMPtr<nsIUrlListener> listener = urlListener; | |
| for (const auto& folder : folders) { | |
| - nsresult rv = folder->GetNewMessages(aMsgWindow, urlListener); | |
| - if (NS_FAILED(rv)) { | |
| - // If we encounter an error, just log it rather than fail the whole sync. | |
| - nsCString name; | |
| - folder->GetName(name); | |
| - NS_ERROR(nsPrintfCString("failed to get new messages for folder %s: %s", | |
| - name.get(), mozilla::GetStaticErrorName(rv)) | |
| - .get()); | |
| + nsCString name; | |
| + folder->GetName(name); | |
| + | |
| + auto syncFn = [folder, window, listener, name]() { | |
| + nsresult rv = folder->GetNewMessages(window, listener); | |
| + if (NS_FAILED(rv)) { | |
| + // If we encounter an error, just log it rather than fail the whole | |
| + // sync. | |
| + NS_ERROR(nsPrintfCString("failed to get new messages for folder %s: %s", | |
| + name.get(), mozilla::GetStaticErrorName(rv)) | |
| + .get()); | |
| + } | |
| + }; | |
| + | |
| + // FIXME: NTLM works in a fairly different way than other supported | |
| + // authentication methods: request are authenticated by a cookie created | |
| + // from a challenge-based flow spanning multiple requests. This means that | |
| + // if an authentication error happens while we're trying to update a bunch | |
| + // of folders at once, properly handling it with the current EWS client | |
| + // architecture gets really difficult. | |
| + // | |
| + // So, as a temporary solution, we delay each request from each other in an | |
| + // attempt to ensure authentication errors are resolved before the next | |
| + // request. The plan is to improve the current EWS client architecture to | |
| + // address this situation, at which point this workaround will be removed, | |
| + // before we update the frontend to offer NTLM to EWS users. | |
| + if (authMethod == nsMsgAuthMethod::NTLM) { | |
| + delay += 500; | |
| + | |
| + nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1"); | |
| + nsresult rv = timer->InitWithCallback(new EwsNTLMSyncTimer(syncFn), delay, | |
| + nsITimer::TYPE_ONE_SHOT); | |
| + | |
| + if (NS_FAILED(rv)) { | |
| + // If we encounter an error, just log it rather than fail the whole | |
| + // sync. | |
| + NS_ERROR( | |
| + nsPrintfCString("failed to schedule new messages for folder %s: %s", | |
| + name.get(), mozilla::GetStaticErrorName(rv)) | |
| + .get()); | |
| + } | |
| + | |
| + // Add the timer to the member array to prevent it from being | |
| + // automatically dropped. | |
| + mNTLMSyncTimers.AppendElement(timer); | |
| + } else { | |
| + syncFn(); | |
| } | |
| } | |
| diff --git a/mailnews/protocols/ews/src/EwsIncomingServer.h b/mailnews/protocols/ews/src/EwsIncomingServer.h | |
| --- a/mailnews/protocols/ews/src/EwsIncomingServer.h | |
| +++ b/mailnews/protocols/ews/src/EwsIncomingServer.h | |
| @@ -7,6 +7,7 @@ | |
| #include "IEwsIncomingServer.h" | |
| #include "msgIOAuth2Module.h" | |
| +#include "nsITimer.h" | |
| #include "nsMsgIncomingServer.h" | |
| #define EWS_INCOMING_SERVER_IID \ | |
| @@ -94,6 +95,12 @@ class EwsIncomingServer : public nsMsgIn | |
| nsresult UpdateTrashFolder(); | |
| nsCOMPtr<msgIOAuth2Module> mOAuth2Module; | |
| + | |
| + // An array of timers used when syncing the account, if the account uses NTLM | |
| + // (see the comment in `EwsIncomingServer::SyncFolders` for why we're using | |
| + // timers). We need to store them here to prevent them from being dropped | |
| + // after `EwsIncomingServer::SyncFolders` returns. | |
| + nsTArray<nsCOMPtr<nsITimer>> mNTLMSyncTimers; | |
| }; | |
| #endif // COMM_MAILNEWS_PROTOCOLS_EWS_SRC_EWSINCOMINGSERVER_H_ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment