Backed out changeset 2e4a17dc941a (bug 1896171) for causing build bustage. rs=backout

This commit is contained in:
Alessandro Castellani 2024-07-08 16:40:53 -07:00
Родитель c3a43db018
Коммит 190cd87c78
7 изменённых файлов: 65 добавлений и 359 удалений

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

@ -171,7 +171,7 @@ async function createAccountInBackend(config) {
smtpServer.clientid = newOutgoingClientid;
} else if (config.outgoing.type == "ews") {
const ewsServer = outServer.QueryInterface(Ci.nsIEwsServer);
ewsServer.initialize(config.outgoing.ewsURL);
ewsServer.ewsURL = config.outgoing.ewsURL;
} else {
// Note: createServer should already have thrown if given a type we don't
// support, so if we're able to reach this then something has gone very

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

@ -977,7 +977,7 @@ export var MsgUtils = {
formatStringWithSMTPHostName(userIdentity, composeBundle, errorName) {
const smtpServer =
MailServices.outgoingServer.getServerByIdentity(userIdentity);
const smtpHostname = smtpServer.serverURI.host;
const smtpHostname = smtpServer.hostname;
return composeBundle.formatStringFromName(errorName, [smtpHostname]);
},

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

@ -1,108 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
use nserror::{nsresult, NS_OK};
use nsstring::nsACString;
use xpcom::{
interfaces::{nsILoadGroup, nsLoadFlags},
xpcom_method, RefPtr,
};
/// A stub [`nsIRequest`] that only implementes the `Cancel` method. Currently
/// only used for sending.
///
/// This struct is to be expanded (to actually cancel outgoing requests) once
/// the code architecture for creating and sending request with backoff allows
/// for idiosyncratic semantics.
///
/// [`nsIRequest`]: xpcom::interfaces::nsIRequest
#[xpcom::xpcom(implement(nsIRequest), atomic)]
pub(crate) struct CancellableRequest {}
impl CancellableRequest {
pub fn new() -> RefPtr<Self> {
CancellableRequest::allocate(InitCancellableRequest {})
}
xpcom_method!(cancel => Cancel(aStatus: nsresult));
fn cancel(&self, _status: nsresult) -> Result<(), nsresult> {
log::error!("request cancellation is not currently fully implemented, only stubbed out");
Ok(())
}
///////////////////////////////////
/// Rest of the nsIRequest impl ///
///////////////////////////////////
#[allow(non_snake_case)]
unsafe fn CancelWithReason(&self, _aStatus: nsresult, _aReason: *const nsACString) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
#[allow(non_snake_case)]
unsafe fn GetCanceledReason(&self, _aCanceledReason: *mut nsACString) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
#[allow(non_snake_case)]
unsafe fn GetLoadFlags(&self, _aLoadFlags: *mut nsLoadFlags) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
#[allow(non_snake_case)]
unsafe fn GetLoadGroup(&self, _aLoadGroup: *mut *const nsILoadGroup) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
#[allow(non_snake_case)]
unsafe fn GetName(&self, _aName: *mut nsACString) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
#[allow(non_snake_case)]
unsafe fn GetStatus(&self, _aStatus: *mut nsresult) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
#[allow(non_snake_case)]
unsafe fn GetTRRMode(&self, _retval: *mut u32) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
#[allow(non_snake_case)]
unsafe fn IsPending(&self, _retval: *mut bool) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
#[allow(non_snake_case)]
unsafe fn Resume(&self) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
#[allow(non_snake_case)]
unsafe fn SetCanceledReason(&self, _aCanceledReason: *const nsACString) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
#[allow(non_snake_case)]
unsafe fn SetLoadFlags(&self, _aLoadFlags: nsLoadFlags) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
#[allow(non_snake_case)]
unsafe fn SetLoadGroup(&self, _aLoadGroup: *const nsILoadGroup) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
#[allow(non_snake_case)]
unsafe fn SetTRRMode(&self, _mode: u32) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
#[allow(non_snake_case)]
unsafe fn Suspend(&self) -> nsresult {
return nserror::NS_ERROR_NOT_IMPLEMENTED;
}
}

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

@ -4,17 +4,14 @@
use std::collections::{HashMap, HashSet, VecDeque};
use base64::prelude::*;
use ews::{
create_item::{self, CreateItem, MessageDisposition},
get_folder::GetFolder,
get_item::GetItem,
soap,
sync_folder_hierarchy::{self, SyncFolderHierarchy},
sync_folder_items::{self, SyncFolderItems},
ArrayOfRecipients, BaseFolderId, BaseItemId, BaseShape, Folder, FolderShape, Importance,
ItemShape, Message, MimeContent, Operation, PathToElement, RealItem, ResponseClass,
ResponseCode,
ItemShape, Message, Operation, PathToElement, RealItem, ResponseClass,
};
use fxhash::FxHashMap;
use moz_http::StatusCode;
@ -26,13 +23,13 @@ use uuid::Uuid;
use xpcom::{
getter_addrefs,
interfaces::{
nsIMsgDBHdr, nsIRequestObserver, nsMsgFolderFlagType, nsMsgFolderFlags, nsMsgMessageFlags,
nsMsgPriority, IEwsClient, IEwsFolderCallbacks, IEwsMessageCallbacks,
nsIMsgDBHdr, nsMsgFolderFlagType, nsMsgFolderFlags, nsMsgMessageFlags, nsMsgPriority,
IEwsClient, IEwsFolderCallbacks, IEwsMessageCallbacks,
},
RefPtr,
};
use crate::{authentication::credentials::Credentials, cancellable_request::CancellableRequest};
use crate::authentication::credentials::Credentials;
pub(crate) struct XpComEwsClient {
pub endpoint: Url,
@ -628,12 +625,41 @@ impl XpComEwsClient {
let response = self.make_operation_request(op).await?;
for response_message in response.response_messages.get_item_response_message {
process_response_message_class(
"GetItem",
response_message.response_class,
response_message.response_code,
response_message.message_text,
)?;
match response_message.response_class {
ResponseClass::Success => (),
ResponseClass::Warning => {
let message = if let Some(code) = response_message.response_code {
if let Some(text) = response_message.message_text {
format!("GetItem operation encountered `{code:?}' warning: {text}")
} else {
format!("GetItem operation encountered `{code:?}' warning")
}
} else if let Some(text) = response_message.message_text {
format!("GetItem operation encountered warning: {text}")
} else {
format!("GetItem operation encountered unknown warning")
};
log::warn!("{message}");
}
ResponseClass::Error => {
let message = if let Some(code) = response_message.response_code {
if let Some(text) = response_message.message_text {
format!("GetItem operation encountered `{code:?}' error: {text}")
} else {
format!("GetItem operation encountered `{code:?}' error")
}
} else if let Some(text) = response_message.message_text {
format!("GetItem operation encountered error: {text}")
} else {
format!("GetItem operation encountered unknown error")
};
return Err(XpComEwsError::Processing { message });
}
}
// The expected shape of the list of response messages is
// underspecified, but EWS always seems to return one message
@ -655,108 +681,6 @@ impl XpComEwsClient {
Ok(items)
}
/// Send a message by performing a [`CreateItem`] operation via EWS.
///
/// All headers except for Bcc are expected to be included in the provided
/// MIME content.
///
/// [`CreateItem`] https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/createitem-operation-email-message
pub async fn send_message(
self,
mime_content: String,
message_id: String,
should_request_dsn: bool,
observer: RefPtr<nsIRequestObserver>,
) {
let cancellable_request = CancellableRequest::new();
// Notify that the request has started.
if let Err(err) =
unsafe { observer.OnStartRequest(cancellable_request.coerce()) }.to_result()
{
log::error!("aborting sending: an error occurred while starting the observer: {err}");
return;
}
// Send the request, using an inner method to more easily handle errors.
// Use the return value to determine which status we should use when
// notifying the end of the request.
let status = match self
.send_message_inner(mime_content, message_id, should_request_dsn)
.await
{
Ok(_) => nserror::NS_OK,
Err(err) => {
log::error!("an error occurred while attempting to send the message: {err:?}");
match err {
XpComEwsError::XpCom(status) => status,
XpComEwsError::Http(err) => err.into(),
_ => nserror::NS_ERROR_FAILURE,
}
}
};
// Notify that the request has finished.
if let Err(err) =
unsafe { observer.OnStopRequest(cancellable_request.coerce(), status) }.to_result()
{
log::error!("an error occurred while stopping the observer: {err}")
}
}
async fn send_message_inner(
&self,
mime_content: String,
message_id: String,
should_request_dsn: bool,
) -> Result<(), XpComEwsError> {
// Create a new message using the default values, and set the ones we
// need.
let message = create_item::Message {
mime_content: Some(MimeContent {
character_set: None,
content: BASE64_STANDARD.encode(mime_content),
}),
is_delivery_receipt_requested: Some(should_request_dsn),
internet_message_id: Some(message_id),
..Default::default()
};
let create_item = CreateItem {
items: vec![create_item::Item::Message(message)],
// We don't need EWS to copy messages to the Sent folder after
// they've been sent, because the internal MessageSend module
// already takes care of it, and will include additional headers we
// don't send to EWS (such as Bcc).
message_disposition: Some(MessageDisposition::SendOnly),
saved_item_folder_id: None,
};
let response = self.make_operation_request(create_item).await?;
// We have only sent one message, therefore the response should only
// contain one response message.
let response_messages = response.response_messages.create_item_response_message;
if response_messages.len() != 1 {
return Err(XpComEwsError::Processing {
message: String::from("expected only one message in CreateItem response"),
});
}
// Get the first (and only) response message, and check if there's a
// warning or an error we should handle.
let response_message = response_messages.into_iter().next().unwrap();
process_response_message_class(
"CreateItem",
response_message.response_class,
response_message.response_code,
response_message.message_text,
)
}
/// Makes a request to the EWS endpoint to perform an operation.
///
/// If the request is throttled, it will be retried after the delay given in
@ -1057,50 +981,3 @@ where
handler(client_err, desc);
}
}
/// Look at the response class of a response message, and do nothing, warn or
/// return an error accordingly.
fn process_response_message_class(
op_name: &str,
response_class: ResponseClass,
response_code: Option<ResponseCode>,
message_text: Option<String>,
) -> Result<(), XpComEwsError> {
match response_class {
ResponseClass::Success => Ok(()),
ResponseClass::Warning => {
let message = if let Some(code) = response_code {
if let Some(text) = message_text {
format!("{op_name} operation encountered `{code:?}' warning: {text}")
} else {
format!("{op_name} operation encountered `{code:?}' warning")
}
} else if let Some(text) = message_text {
format!("{op_name} operation encountered warning: {text}")
} else {
format!("{op_name} operation encountered unknown warning")
};
log::warn!("{message}");
Ok(())
}
ResponseClass::Error => {
let message = if let Some(code) = response_code {
if let Some(text) = message_text {
format!("{op_name} operation encountered `{code:?}' error: {text}")
} else {
format!("{op_name} operation encountered `{code:?}' error")
}
} else if let Some(text) = message_text {
format!("{op_name} operation encountered error: {text}")
} else {
format!("{op_name} operation encountered unknown error")
};
Err(XpComEwsError::Processing { message })
}
}
}

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

@ -19,7 +19,6 @@ use xpcom::{
};
mod authentication;
mod cancellable_request;
mod client;
mod outgoing;

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

@ -6,7 +6,6 @@ use std::cell::{OnceCell, RefCell};
use std::os::raw::{c_char, c_void};
use std::ptr;
use cstr::cstr;
use nserror::nsresult;
use nserror::NS_OK;
use nsstring::{nsACString, nsCString};
@ -14,16 +13,13 @@ use url::Url;
use xpcom::{create_instance, getter_addrefs, nsIID};
use xpcom::{
interfaces::{
msgIOAuth2Module, nsIFile, nsIFileInputStream, nsIIOService, nsIMsgIdentity,
nsIMsgStatusFeedback, nsIMsgWindow, nsIRequestObserver, nsIURI, nsIUrlListener,
nsMsgAuthMethodValue, nsMsgSocketTypeValue,
nsIFile, nsIFileInputStream, nsIIOService, nsIMsgIdentity, nsIMsgStatusFeedback,
nsIMsgWindow, nsIRequestObserver, nsIURI, nsIUrlListener, nsMsgAuthMethodValue,
nsMsgSocketTypeValue,
},
xpcom_method, RefPtr,
};
use crate::authentication::credentials::AuthenticationProvider;
use crate::client::XpComEwsClient;
#[no_mangle]
pub unsafe extern "C" fn nsEwsOutgoingServerConstructor(
iid: &nsIID,
@ -189,7 +185,7 @@ impl EwsOutgoingServer {
let url = nsCString::from(ews_url.as_str());
let io_service =
xpcom::get_service::<nsIIOService>(cstr!("@mozilla.org/network/io-service;1"))
xpcom::get_service::<nsIIOService>(cstr::cstr!("@mozilla.org/network/io-service;1"))
.ok_or(nserror::NS_ERROR_FAILURE)?;
getter_addrefs(|p| unsafe { io_service.NewURI(&*url, ptr::null(), ptr::null(), p) })
@ -235,44 +231,28 @@ impl EwsOutgoingServer {
_sender: &nsACString,
_password: &nsACString,
_status_listener: &nsIMsgStatusFeedback,
should_request_dsn: bool,
message_id: &nsACString,
_request_dsn: bool,
_message_id: &nsACString,
observer: &nsIRequestObserver,
) -> Result<(), nsresult> {
unsafe {
// For now we don't pass in a request. Null/empty requests are
// supported by the MessageSend module, so it won't cause an issue
// if the user tries to abort before the operation completes. Once
// sending to EWS is implemented, we will retrieve the nsIChannel
// from the moz_http request and turn it into an nsIRequest, to let
// Necko deal with cancellations.
observer.OnStartRequest(ptr::null());
}
let message_content = read_file(file_path)?;
let message_content =
String::from_utf8(message_content).or(Err(nserror::NS_ERROR_FAILURE))?;
println!("{}", message_content);
// Ensure the URL is properly set.
let url = self
.ews_url
.get()
.ok_or_else(|| {
log::error!("EwsOutgoingServer::SendMailMessage: EWS URL not set");
nserror::NS_ERROR_NOT_INITIALIZED
})?
.clone();
let credentials = self.get_credentials()?;
// Set up the client to build and send the request.
let client = XpComEwsClient {
endpoint: url,
credentials,
client: moz_http::Client::new(),
};
// Send the request asynchronously.
moz_task::spawn_local(
"send_mail",
client.send_message(
message_content,
message_id.to_utf8().into(),
should_request_dsn,
RefPtr::new(observer),
),
)
.detach();
unsafe {
observer.OnStopRequest(ptr::null(), NS_OK);
}
Ok(())
}
@ -326,39 +306,12 @@ impl EwsOutgoingServer {
}
}
// Make it possible to create an Auth from this server's attributes.
impl AuthenticationProvider for &EwsOutgoingServer {
fn username(&self) -> Result<nsCString, nsresult> {
Ok(self.username.borrow().clone())
}
fn password(&self) -> Result<nsCString, nsresult> {
Ok(self.password.borrow().clone())
}
fn auth_method(&self) -> Result<nsMsgAuthMethodValue, nsresult> {
Ok(self.auth_method.borrow().clone())
}
fn oauth2_module(&self) -> Result<Option<RefPtr<msgIOAuth2Module>>, nsresult> {
let oauth2_module =
create_instance::<msgIOAuth2Module>(c"@mozilla.org/mail/oauth2-module;1").ok_or(
Err::<RefPtr<msgIOAuth2Module>, _>(nserror::NS_ERROR_FAILURE),
)?;
let mut oauth2_supported = false;
unsafe { oauth2_module.InitFromOutgoing(self.coerce(), &mut oauth2_supported) }
.to_result()?;
Ok(oauth2_supported.then_some(oauth2_module))
}
}
/// Open the file provided and read its content into a vector of bytes.
fn read_file(file: &nsIFile) -> Result<Vec<u8>, nsresult> {
let file_stream =
create_instance::<nsIFileInputStream>(cstr!("@mozilla.org/network/file-input-stream;1"))
.ok_or(nserror::NS_ERROR_FAILURE)?;
let file_stream = create_instance::<nsIFileInputStream>(cstr::cstr!(
"@mozilla.org/network/file-input-stream;1"
))
.ok_or(nserror::NS_ERROR_FAILURE)?;
// Open a stream from the file, and figure out how many bytes can be read
// from it.

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

@ -83,20 +83,5 @@ impl From<nsresult> for Error {
}
}
impl From<Error> for nsresult {
fn from(value: Error) -> Self {
match value {
Error::UnsupportedScheme(_) => nserror::NS_ERROR_UNKNOWN_PROTOCOL,
Error::TimedOut => nserror::NS_ERROR_NET_TIMEOUT,
Error::UnknownHost => nserror::NS_ERROR_UNKNOWN_HOST,
Error::UnknownNetworkError(result) => result,
Error::RedirectLoop => nserror::NS_ERROR_REDIRECT_LOOP,
Error::Unknown(result) => result,
_ => nserror::NS_ERROR_FAILURE,
}
}
}
/// A result which error type is always an [`enum@Error`].
pub type Result<T> = std::result::Result<T, Error>;