275 lines
8.3 KiB
Diff
275 lines
8.3 KiB
Diff
diff -x __pycache__ -ru thunderbird-128.10.1/comm/mailnews/base/src/nsMsgIncomingServer.cpp thunderbird/thunderbird-128.10.1/comm/mailnews/base/src/nsMsgIncomingServer.cpp
|
|
--- thunderbird-128.10.1/comm/mailnews/base/src/nsMsgIncomingServer.cpp 2025-05-13 15:22:25.000000000 +0200
|
|
+++ thunderbird/thunderbird-128.10.1/comm/mailnews/base/src/nsMsgIncomingServer.cpp 2025-08-21 03:49:50.217872643 +0200
|
|
@@ -41,6 +41,15 @@
|
|
#include "mozilla/Unused.h"
|
|
#include "nsIUUIDGenerator.h"
|
|
#include "nsIArray.h"
|
|
+#include "mozilla/SpinEventLoopUntil.h"
|
|
+#include "nsIProcess.h"
|
|
+#include "mozilla/RandomNum.h"
|
|
+#include "nsIServerSocket.h"
|
|
+#include "nsIAsyncInputStream.h"
|
|
+#include "nsISocketTransport.h"
|
|
+#include "nsIBinaryInputStream.h"
|
|
+#include "nsIObjectInputStream.h"
|
|
+#include "nsDirectoryServiceDefs.h"
|
|
|
|
#define PORT_NOT_SET -1
|
|
|
|
@@ -707,12 +716,241 @@
|
|
return NS_OK;
|
|
}
|
|
|
|
+nsresult
|
|
+nsMsgIncomingServer::GetPasswordCommand(nsTArray<nsString> &passwordCommand) {
|
|
+ nsString serializedCommand;
|
|
+
|
|
+ nsresult rv = GetUnicharValue("passwordCommand", serializedCommand);
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+
|
|
+ if (serializedCommand.Length() == 0)
|
|
+ return NS_OK;
|
|
+
|
|
+ // Serialization is achieved by joining the arguments with a comma.
|
|
+ // Commas are themselves allowed to be backslash-espaced.
|
|
+
|
|
+ nsString currentArgument;
|
|
+ bool nextShouldBeEscaped = false;
|
|
+
|
|
+ for (unsigned int i = 0; i < serializedCommand.Length(); i++) {
|
|
+ char c = serializedCommand[i];
|
|
+
|
|
+ switch (c) {
|
|
+ case ',':
|
|
+ if (nextShouldBeEscaped) {
|
|
+ currentArgument.Append(',');
|
|
+ nextShouldBeEscaped = false;
|
|
+ } else {
|
|
+ passwordCommand.AppendElement(currentArgument);
|
|
+ currentArgument.Truncate();
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case '\\':
|
|
+ if (nextShouldBeEscaped)
|
|
+ currentArgument.Append(',');
|
|
+
|
|
+ nextShouldBeEscaped = !nextShouldBeEscaped;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ currentArgument.Append(c);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (nextShouldBeEscaped)
|
|
+ currentArgument.Append('\\');
|
|
+ passwordCommand.AppendElement(currentArgument);
|
|
+
|
|
+ return NS_OK;
|
|
+}
|
|
+
|
|
+class PasswordCommandObserver final
|
|
+ : public nsIObserver,
|
|
+ public nsIServerSocketListener,
|
|
+ public nsIInputStreamCallback {
|
|
+ public:
|
|
+ NS_DECL_ISUPPORTS
|
|
+ NS_DECL_NSIOBSERVER
|
|
+ NS_DECL_NSISERVERSOCKETLISTENER
|
|
+ NS_DECL_NSIINPUTSTREAMCALLBACK
|
|
+
|
|
+ private:
|
|
+ bool isCommandDone;
|
|
+ bool passwordReceived;
|
|
+ nsAString& password;
|
|
+ nsTArray<uint8_t> received;
|
|
+
|
|
+ public:
|
|
+ PasswordCommandObserver(nsAString& password);
|
|
+ bool IsDone();
|
|
+ private:
|
|
+ ~PasswordCommandObserver();
|
|
+};
|
|
+
|
|
+NS_IMPL_ISUPPORTS(PasswordCommandObserver, nsIObserver, nsIServerSocketListener, nsIInputStreamCallback)
|
|
+
|
|
+PasswordCommandObserver::PasswordCommandObserver(nsAString& password) : password(password) {
|
|
+ isCommandDone = false;
|
|
+ passwordReceived = false;
|
|
+}
|
|
+PasswordCommandObserver::~PasswordCommandObserver() {}
|
|
+
|
|
+bool
|
|
+PasswordCommandObserver::IsDone() {
|
|
+ return isCommandDone && passwordReceived;
|
|
+}
|
|
+
|
|
+NS_IMETHODIMP
|
|
+PasswordCommandObserver::Observe(nsISupports* aSubject, const char* aTopic,
|
|
+ const char16_t* aData) {
|
|
+ isCommandDone = true;
|
|
+
|
|
+ return strcmp(aTopic, "process-failed") == 0 ? NS_ERROR_FAILURE : NS_OK;
|
|
+}
|
|
+
|
|
+NS_IMETHODIMP
|
|
+PasswordCommandObserver::OnSocketAccepted(nsIServerSocket *aServ, nsISocketTransport *aTransport) {
|
|
+ nsresult rv;
|
|
+
|
|
+ // The socket can be closed. This does not close the existing connection.
|
|
+ rv = aServ->Close();
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+
|
|
+ nsCOMPtr<nsIInputStream> stream;
|
|
+ rv = aTransport->OpenInputStream(0, 0, 0, getter_AddRefs(stream));
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+
|
|
+ nsCOMPtr<nsIAsyncInputStream> astream;
|
|
+ astream = do_QueryInterface(stream);
|
|
+ rv = astream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+
|
|
+ return NS_OK;
|
|
+}
|
|
+
|
|
+NS_IMETHODIMP
|
|
+PasswordCommandObserver::OnStopListening(nsIServerSocket *aServ, nsresult aStatus) {
|
|
+ return NS_OK;
|
|
+}
|
|
+
|
|
+NS_IMETHODIMP
|
|
+PasswordCommandObserver::OnInputStreamReady(nsIAsyncInputStream *aStream) {
|
|
+ nsresult rv;
|
|
+
|
|
+ int64_t len;
|
|
+ rv = aStream->Available((uint64_t*)&len);
|
|
+ if (NS_FAILED(rv))
|
|
+ // note: aStream->Available() can also return -1.
|
|
+ len = -1;
|
|
+
|
|
+ if (len == -1) {
|
|
+ password.Assign(NS_ConvertUTF8toUTF16((char*)received.Elements(), received.Length()));
|
|
+ passwordReceived = true;
|
|
+ } else {
|
|
+ if (len > 0) {
|
|
+ nsCOMPtr<nsIBinaryInputStream> bin = NS_NewObjectInputStream(aStream);
|
|
+ nsTArray<uint8_t> data;
|
|
+ rv = bin->ReadByteArray(len, data);
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+ received.AppendElements(data);
|
|
+ }
|
|
+
|
|
+ rv = aStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+ }
|
|
+
|
|
+ return NS_OK;
|
|
+}
|
|
+
|
|
+nsresult
|
|
+nsMsgIncomingServer::RunPasswordCommand(nsTArray<nsString> &passwordCommand, nsAString& password) {
|
|
+ nsresult rv;
|
|
+
|
|
+ nsCOMPtr<nsIFile> socketFile;
|
|
+ rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(socketFile));
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+ nsAutoString tempSocketName;
|
|
+ uint64_t randomSocketName = mozilla::RandomUint64().valueOrFrom([] {
|
|
+ return 0;
|
|
+ });
|
|
+ const char* alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
+ tempSocketName.AppendPrintf(
|
|
+ "tmp-%c%c%c.sock",
|
|
+ alphabet[randomSocketName % 36],
|
|
+ alphabet[(randomSocketName / 36) % 36],
|
|
+ alphabet[(randomSocketName / (36*36)) % 36]
|
|
+ );
|
|
+ socketFile->AppendRelativePath(tempSocketName);
|
|
+
|
|
+ nsCOMPtr<nsIServerSocket> socket =
|
|
+ do_CreateInstance(NS_SERVERSOCKET_CONTRACTID);
|
|
+ socket->InitWithFilename(socketFile, 0600, -1);
|
|
+
|
|
+ auto executable = passwordCommand[0];
|
|
+ nsCOMPtr<nsIFile> executableFile =
|
|
+ do_CreateInstance("@mozilla.org/file/local;1", &rv);
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+ executableFile->InitWithPath(executable);
|
|
+
|
|
+ nsCOMPtr<nsIProcess> process =
|
|
+ do_CreateInstance("@mozilla.org/process/util;1", &rv);
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+ rv = process->Init(executableFile);
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+
|
|
+ auto observer = new PasswordCommandObserver(password);
|
|
+
|
|
+ socket->AsyncListen(observer);
|
|
+
|
|
+ auto argsCount = passwordCommand.Length();
|
|
+ const char** args = new const char*[argsCount];
|
|
+ for (unsigned int i = 0; i < argsCount - 1; i++) {
|
|
+ NS_ConvertUTF16toUTF8 argUtf8(passwordCommand[i + 1]);
|
|
+ args[i] = strdup(argUtf8.get());
|
|
+ }
|
|
+ nsString socketPath;
|
|
+ rv = socketFile->GetPath(socketPath);
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+ NS_ConvertUTF16toUTF8 socketPathUtf8(socketPath);
|
|
+ args[argsCount - 1] = strdup(socketPathUtf8.get());
|
|
+
|
|
+ rv = process->RunAsync(args, argsCount, observer, false);
|
|
+ if (NS_FAILED(rv))
|
|
+ return rv;
|
|
+
|
|
+ mozilla::SpinEventLoopUntil("nsMsgIncomingServer::RunPasswordCommand"_ns, [&]() {
|
|
+ return observer->IsDone();
|
|
+ });
|
|
+
|
|
+ for (unsigned int i = 0; i < argsCount; i++)
|
|
+ free((void*)args[i]);
|
|
+ delete[] args;
|
|
+
|
|
+ return NS_OK;
|
|
+}
|
|
+
|
|
NS_IMETHODIMP
|
|
nsMsgIncomingServer::GetPasswordWithUI(const nsAString& aPromptMessage,
|
|
const nsAString& aPromptTitle,
|
|
nsAString& aPassword) {
|
|
nsresult rv = NS_OK;
|
|
|
|
+ nsTArray<nsString> passwordCommand;
|
|
+ rv = GetPasswordCommand(passwordCommand);
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+
|
|
+ if (passwordCommand.Length() > 0) {
|
|
+ rv = RunPasswordCommand(passwordCommand, aPassword);
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+
|
|
+ rv = SetPassword(aPassword);
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+
|
|
+ return NS_OK;
|
|
+ }
|
|
+
|
|
if (m_password.IsEmpty()) {
|
|
// let's see if we have the password in the password manager and
|
|
// can avoid this prompting thing. This makes it easier to get embedders
|
|
diff -x __pycache__ -ru thunderbird-128.10.1/comm/mailnews/base/src/nsMsgIncomingServer.h thunderbird/thunderbird-128.10.1/comm/mailnews/base/src/nsMsgIncomingServer.h
|
|
--- thunderbird-128.10.1/comm/mailnews/base/src/nsMsgIncomingServer.h 2025-05-13 15:22:25.000000000 +0200
|
|
+++ thunderbird/thunderbird-128.10.1/comm/mailnews/base/src/nsMsgIncomingServer.h 2025-08-19 12:39:19.236805257 +0200
|
|
@@ -48,6 +48,9 @@
|
|
nsCString m_serverKey;
|
|
bool m_hasShutDown;
|
|
|
|
+ nsresult GetPasswordCommand(nsTArray<nsString> &passwordCommand);
|
|
+ nsresult RunPasswordCommand(nsTArray<nsString> &passwordCommand, nsAString& password);
|
|
+
|
|
// Sets m_password, if password found. Can return NS_ERROR_ABORT if the
|
|
// user cancels the master password dialog.
|
|
nsresult GetPasswordWithoutUI();
|