Bug 1738175 - Support plain auth in Pop3Client.jsm. r=mkmelin

Depends on D130149.

Differential Revision: https://phabricator.services.mozilla.com/D130150

--HG--
extra : histedit_source : cc95c1a45779eb0063ceb6ead5dd39932b21b638
This commit is contained in:
Ping Chen 2021-11-02 06:48:16 +00:00
Родитель 82cd1a23a8
Коммит cffb92ac88
3 изменённых файлов: 178 добавлений и 1 удалений

Просмотреть файл

@ -2,7 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const EXPORTED_SYMBOLS = ["SmtpAuthenticator", "NntpAuthenticator"];
const EXPORTED_SYMBOLS = [
"SmtpAuthenticator",
"NntpAuthenticator",
"Pop3Authenticator",
];
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var { MailServices } = ChromeUtils.import(
@ -308,3 +312,28 @@ class NntpAuthenticator extends IncomingServerAuthenticator {
return this._promptAuthFailed(null, this._server.prettyName);
}
}
class Pop3Authenticator extends IncomingServerAuthenticator {
getPassword() {
if (this._server.password) {
return this._server.password;
}
let composeBundle = Services.strings.createBundle(
"chrome://messenger/locale/localMsgs.properties"
);
let params = [this._server.username, this._server.hostname];
let promptString = composeBundle.formatStringFromName(
"pop3EnterPasswordPrompt",
params
);
let promptTitle = composeBundle.formatStringFromName(
"pop3EnterPasswordPromptTitleWithUsername",
[this._server.hostname]
);
return this._server.getPasswordWithUI(
promptString,
promptTitle,
MailServices.mailSession.topmostMsgWindow
);
}
}

Просмотреть файл

@ -467,6 +467,56 @@ class MsgIncomingServer {
}
}
/**
* Try to get the password from nsILoginManager.
* @returns {string}
*/
_getPasswordWithoutUI() {
let logins = Services.logins.findLogins(this.serverURI, "", this.serverURI);
for (let login of logins) {
if (login.username == this.username) {
return login.password;
}
}
return null;
}
getPasswordWithUI(promptMessage, promptTitle, msgWindow) {
let password = this._getPasswordWithoutUI();
if (password) {
this.password = password;
return this.password;
}
let outUsername = {};
let outPassword = {};
let ok;
if (this.username) {
ok = msgWindow.authPrompt.promptPassword(
promptTitle,
promptMessage,
this.serverURI,
Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY,
outPassword
);
} else {
ok = msgWindow.authPrompt.promptUsernameAndPassword(
promptTitle,
promptMessage,
this.serverURI,
Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY,
outUsername,
outPassword
);
}
if (ok) {
if (outUsername.value) {
this.username = outUsername.value;
}
this.password = outPassword.value;
}
return this.password;
}
forgetPassword() {
let serverURI = `${this.localStoreType}://${encodeURIComponent(
this.hostName
@ -477,6 +527,7 @@ class MsgIncomingServer {
Services.logins.removeLogin(login);
}
}
this.password = "";
}
closeCachedConnections() {}

Просмотреть файл

@ -4,7 +4,24 @@
const EXPORTED_SYMBOLS = ["Pop3Client"];
var { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
var { CommonUtils } = ChromeUtils.import("resource://services-common/utils.js");
var { Pop3Authenticator } = ChromeUtils.import(
"resource:///modules/MailAuthenticator.jsm"
);
/**
* A structure to represent a response received from the server. A response can
* be a single status line of a multi-line data block.
* @typedef {Object} Pop3Response
* @property {boolean} success - True for a positive status indicator ("+OK").
* @property {string} statusText - The status line of the response excluding the
* status indicator.
* @property {string} data - The part of a multi-line data block excluding the
* status line.
*/
/**
* A class to interact with POP3 server.
@ -15,12 +32,15 @@ class Pop3Client {
*/
constructor(server) {
this._server = server;
this._authenticator = new Pop3Authenticator(server);
this._logger = console.createInstance({
prefix: "mailnews.pop3",
maxLogLevel: "Warn",
maxLogLevelPref: "mailnews.pop3.loglevel",
});
this.onReady = () => {};
}
/**
@ -45,8 +65,23 @@ class Pop3Client {
this._logger.debug("Connected");
this._socket.ondata = this._onData;
this._socket.onclose = this._onClose;
this._nextAction = this._actionCapa;
};
/**
* Parse the server response.
* @param {string} str - Response received from the server.
* @returns {Pop3Response}
*/
_parse(str) {
let matches = /^(\+OK|-ERR) ?(.*)\r\n([^]*)/.exec(str);
if (matches) {
let [, status, statusText, data] = matches;
return { success: status == "+OK", statusText, data };
}
return { data: str };
}
/**
* The data event handler.
* @param {TCPSocketEvent} event - The data event.
@ -56,6 +91,8 @@ class Pop3Client {
new Uint8Array(event.data)
);
this._logger.debug(`S: ${stringPayload}`);
let res = this._parse(stringPayload);
this._nextAction?.(res);
};
/**
@ -72,4 +109,64 @@ class Pop3Client {
_onClose = () => {
this._logger.debug("Connection closed.");
};
/**
* Send a command to the server.
* @param {string} str - The command string to send.
* @param {boolean} [suppressLogging=false] - Whether to suppress logging the str.
*/
_send(str, suppressLogging) {
if (suppressLogging && AppConstants.MOZ_UPDATE_CHANNEL != "default") {
this._logger.debug(
"C: Logging suppressed (it probably contained auth information)"
);
} else {
// Do not suppress for non-release builds, so that debugging auth problems
// is easier.
this._logger.debug(`C: ${str}`);
}
this._socket.send(CommonUtils.byteStringToArrayBuffer(str + "\r\n").buffer);
}
/**
* Send `CAPA` request to the server.
*/
_actionCapa = () => {
this._nextAction = this._actionCapaResponse;
this._send("CAPA");
};
/**
* Handle `CAPA` response.
* @param {Pop3Response} res - CAPA response received from the server.
*/
_actionCapaResponse = res => {
this._actionAuth();
};
/**
* Init authentication depending on server capabilities and user prefs.
*/
_actionAuth = () => {
this._logger.debug("Authenticating via AUTH PLAIN");
this._nextAction = this._actionAuthResponse;
let password = String.fromCharCode(
...new TextEncoder().encode(this._authenticator.getPassword())
);
this._send(
"AUTH PLAIN " +
btoa("\0" + this._authenticator.username + "\0" + password),
true
);
};
/**
* Handle authentication response.
* @param {Pop3Response} res - CAPA response received from the server.
*/
_actionAuthResponse = res => {
if (res.success) {
this.onReady();
}
};
}