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:
Родитель
82cd1a23a8
Коммит
cffb92ac88
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче