use serde_json::json;
use crate::base64::base64url_encode;
use crate::crypto::MegaRsaKey;
use crate::crypto::aes::aes128_ecb_encrypt;
use crate::error::{MegaError, Result};
use crate::fs::NodeType;
use crate::session::Session;
impl Session {
pub async fn get_public_key(&mut self, email: &str) -> Result<MegaRsaKey> {
let response = self
.api
.request(json!({
"a": "uk",
"u": email
}))
.await?;
let pubk_b64 = response["pubk"]
.as_str()
.ok_or_else(|| MegaError::Custom("Public key not found for user".to_string()))?;
MegaRsaKey::from_encoded_public_key(pubk_b64)
.map_err(|e| MegaError::CryptoError(format!("Invalid public key: {}", e)))
}
pub async fn share_folder(&mut self, node_handle: &str, email: &str, level: i32) -> Result<()> {
self.ensure_share_keys_ready().await?;
let node_key = {
let node = self
.nodes
.iter()
.find(|n| n.handle == node_handle)
.ok_or_else(|| MegaError::Custom("Node not found".to_string()))?;
if node.node_type != NodeType::Folder {
return Err(MegaError::Custom("Can only share folders".to_string()));
}
if node.key.is_empty() {
return Err(MegaError::Custom("Node key not available".to_string()));
}
node.key.clone()
};
let pub_key = self.get_public_key(email).await?;
let encrypted_key = pub_key.encrypt(&node_key);
let key_b64 = base64url_encode(&encrypted_key);
let mut share_nodes = self.collect_share_nodes_bottom_up(node_handle);
if share_nodes.is_empty() {
share_nodes.push((node_handle.to_string(), node_key.clone()));
}
let share_key: [u8; 16] = if node_key.len() >= 16 {
let mut sk = [0u8; 16];
sk.copy_from_slice(&node_key[..16]);
sk
} else {
return Err(MegaError::Custom("Invalid folder key length".to_string()));
};
if self.key_manager.is_ready() {
self.key_manager
.add_share_key_with_flags(node_handle, &share_key, true, false);
self.key_manager.add_pending_out_email(node_handle, email);
self.persist_keys_attribute().await?;
}
let cr = self.build_cr_for_nodes(node_handle, &share_key, &share_nodes);
let mut request = json!({
"a": "s2",
"n": node_handle,
"s": [{
"u": email,
"l": level
}],
"ok": key_b64
});
if let Some(cr_value) = cr {
request["cr"] = cr_value;
}
let mut response = self.api.request(request.clone()).await;
if matches!(response, Err(MegaError::ApiError { code: -11, .. })) {
self.ensure_share_keys_ready().await?;
let _ = self.refresh().await;
response = self.api.request(request).await;
}
let response = response?;
if let Some(err_code) = response.as_i64()
&& err_code < 0
{
let error_code = crate::api::ApiErrorCode::from(err_code);
return Err(MegaError::ApiError {
code: err_code as i32,
message: error_code.description().to_string(),
});
}
let _ = self.track_seqtag_from_response(&response);
if self
.key_manager
.get_share_key_from_str(node_handle)
.is_none()
{
self.key_manager
.add_share_key_from_str(node_handle, &share_key);
}
let _ = self.key_manager.set_share_key_trusted(node_handle, true);
let _ = self.key_manager.set_share_key_in_use(node_handle, true);
let _ = self.persist_keys_attribute().await;
Ok(())
}
pub(crate) fn find_share_for_handle(&self, start_handle: &str) -> Option<(String, [u8; 16])> {
let mut current = Some(start_handle.to_string());
while let Some(handle) = current {
if let Some(node) = self.nodes.iter().find(|n| n.handle == handle)
&& let Some(sk) = node.share_key
{
let sh = node.share_handle.clone().unwrap_or_else(|| handle.clone());
return Some((sh, sk));
}
if let Some(key) = self.key_manager.get_share_key_from_str(&handle) {
return Some((handle, key));
}
current = self
.nodes
.iter()
.find(|n| n.handle == handle)
.and_then(|n| n.parent_handle.clone());
}
None
}
pub(crate) fn collect_share_nodes_bottom_up(
&self,
root_handle: &str,
) -> Vec<(String, Vec<u8>)> {
fn walk(session: &Session, handle: &str, out: &mut Vec<(String, Vec<u8>)>) {
let children: Vec<String> = session
.nodes
.iter()
.filter(|&n| n.parent_handle.as_deref() == Some(handle))
.map(|n| n.handle.clone())
.collect();
for child in children {
walk(session, &child, out);
}
if let Some(node) = session.nodes.iter().find(|n| n.handle == handle) {
out.push((node.handle.clone(), node.key.clone()));
}
}
let mut out = Vec::new();
walk(self, root_handle, &mut out);
out
}
pub(crate) fn build_cr_for_nodes(
&self,
share_handle: &str,
share_key: &[u8; 16],
targets: &[(String, Vec<u8>)],
) -> Option<serde_json::Value> {
use serde_json::json;
let cr_nodes = vec![share_handle.to_string()];
let mut cr_items: Vec<String> = Vec::new();
let mut cr_triplets: Vec<serde_json::Value> = Vec::new();
for (idx, (node_handle, key_bytes)) in targets.iter().enumerate() {
if key_bytes.is_empty() || key_bytes.len() % 16 != 0 {
continue;
}
cr_items.push(node_handle.clone());
let enc = aes128_ecb_encrypt(key_bytes, share_key);
let enc_b64 = base64url_encode(&enc);
cr_triplets.push(json!(0));
cr_triplets.push(json!(idx as i64));
cr_triplets.push(json!(enc_b64));
}
if cr_items.is_empty() {
return None;
}
Some(json!([cr_nodes, cr_items, cr_triplets]))
}
}