Storing torrent name in ManagedTorrentShared

This commit is contained in:
Igor Katson 2024-12-06 12:57:26 +00:00
parent 0a92cf1d65
commit 0fabb453aa
No known key found for this signature in database
GPG key ID: B4EC22B66D61A3F5
4 changed files with 55 additions and 6 deletions

View file

@ -209,10 +209,7 @@ impl Api {
let mut r = TorrentDetailsResponse {
id: Some(id),
info_hash: mgr.shared().info_hash.as_string(),
name: mgr
.with_metadata(|r| r.info.name.as_ref().map(|n| n.to_string()))
.ok()
.flatten(),
name: mgr.name(),
output_folder: mgr
.shared()
.options
@ -249,6 +246,7 @@ impl Api {
Some(handle.id()),
&info_hash,
handle.metadata.load().as_ref().map(|r| &r.info),
handle.name().as_deref(),
only_files.as_deref(),
output_folder,
)
@ -383,6 +381,7 @@ impl Api {
Some(id),
&handle.info_hash(),
handle.metadata.load().as_ref().map(|r| &r.info),
handle.name().as_deref(),
handle.only_files().as_deref(),
handle
.shared()
@ -419,6 +418,7 @@ impl Api {
None,
&info_hash,
Some(&info),
None,
only_files.as_deref(),
output_folder.to_string_lossy().into_owned().to_string(),
)
@ -429,6 +429,7 @@ impl Api {
Some(id),
&handle.info_hash(),
handle.metadata.load().as_ref().map(|r| &r.info),
handle.name().as_deref(),
handle.only_files().as_deref(),
handle
.shared()
@ -532,6 +533,7 @@ fn make_torrent_details(
id: Option<TorrentId>,
info_hash: &Id20,
info: Option<&TorrentMetaV1Info<ByteBufOwned>>,
name: Option<&str>,
only_files: Option<&[usize]>,
output_folder: String,
) -> Result<TorrentDetailsResponse> {
@ -564,7 +566,9 @@ fn make_torrent_details(
Ok(TorrentDetailsResponse {
id,
info_hash: info_hash.as_string(),
name: info.and_then(|i| i.name.as_ref().map(|b| b.to_string())),
name: name
.map(|s| s.to_owned())
.or_else(|| info.and_then(|i| i.name.as_ref().map(|b| b.to_string()))),
files: Some(files),
output_folder,
stats: None,

View file

@ -477,6 +477,7 @@ struct InternalAddResult {
info_hash: Id20,
metadata: Option<TorrentMetadata>,
trackers: Vec<String>,
name: Option<String>,
}
impl Session {
@ -899,6 +900,7 @@ impl Session {
info_hash,
trackers: magnet.trackers,
metadata: None,
name: magnet.name,
}
}
other => {
@ -943,6 +945,7 @@ impl Session {
torrent.info_bytes,
)?),
trackers,
name: None,
}
}
};
@ -956,6 +959,7 @@ impl Session {
fn get_default_subfolder_for_torrent(
&self,
info: &TorrentMetaV1Info<ByteBufOwned>,
magnet_name: Option<&str>,
) -> anyhow::Result<Option<PathBuf>> {
let files = info
.iter_file_details()?
@ -964,11 +968,23 @@ impl Session {
if files.len() < 2 {
return Ok(None);
}
fn check_valid(name: &str) -> anyhow::Result<()> {
if name.contains("/") || name.contains("\\") || name.contains("..") {
bail!("path traversal in torrent name detected")
}
Ok(())
}
if let Some(name) = &info.name {
let s =
std::str::from_utf8(name.as_slice()).context("invalid UTF-8 in torrent name")?;
check_valid(s)?;
return Ok(Some(PathBuf::from(s)));
};
if let Some(name) = magnet_name {
check_valid(name)?;
return Ok(Some(PathBuf::from(name)));
}
// Let the subfolder name be the longest filename
let longest = files
.iter()
@ -989,6 +1005,7 @@ impl Session {
info_hash,
metadata,
trackers,
name,
} = add_res;
let peer_stream_permanent = !opts.paused && !opts.list_only;
@ -1045,7 +1062,7 @@ impl Session {
let output_folder = match (opts.output_folder, opts.sub_folder) {
(None, None) => self.output_folder.join(
self.get_default_subfolder_for_torrent(&metadata.info)?
self.get_default_subfolder_for_torrent(&metadata.info, name.as_deref())?
.unwrap_or_default(),
),
(Some(o), None) => PathBuf::from(o),
@ -1118,6 +1135,7 @@ impl Session {
},
connector: self.connector.clone(),
session: Arc::downgrade(self),
magnet_name: name,
});
let initializing = Arc::new(TorrentStateInitializing::new(

View file

@ -141,6 +141,7 @@ pub struct TorrentMetadata {
pub info_bytes: Bytes,
pub lengths: Lengths,
pub file_infos: FileInfos,
pub name: Option<String>,
}
impl TorrentMetadata {
@ -162,12 +163,18 @@ impl TorrentMetadata {
})
})
.collect::<anyhow::Result<Vec<FileInfo>>>()?;
let name = info
.name
.as_ref()
.and_then(|n| std::str::from_utf8(n.as_ref()).ok())
.map(|s| s.to_owned());
Ok(Self {
info,
torrent_bytes,
info_bytes,
lengths,
file_infos,
name,
})
}
}
@ -188,6 +195,9 @@ pub struct ManagedTorrentShared {
pub(crate) connector: Arc<StreamConnector>,
pub(crate) storage_factory: BoxStorageFactory,
pub(crate) session: Weak<Session>,
// "dn" from magnet link
pub(crate) magnet_name: Option<String>,
}
pub struct ManagedTorrent {
@ -204,6 +214,13 @@ impl ManagedTorrent {
self.shared.id
}
pub fn name(&self) -> Option<String> {
if let Some(m) = &*self.metadata.load() {
return m.name.clone().or_else(|| self.shared.magnet_name.clone());
}
self.shared.magnet_name.clone()
}
pub fn shared(&self) -> &ManagedTorrentShared {
&self.shared
}

View file

@ -9,6 +9,7 @@ pub struct Magnet {
id20: Option<Id20>,
id32: Option<Id32>,
pub trackers: Vec<String>,
pub name: Option<String>,
select_only: Option<Vec<usize>>,
}
@ -29,6 +30,7 @@ impl Magnet {
id20: Some(id20),
id32: None,
trackers,
name: None,
select_only,
}
}
@ -40,6 +42,7 @@ impl Magnet {
return Ok(Magnet {
id20: Some(id20),
id32: None,
name: None,
trackers: vec![],
select_only: None,
});
@ -52,6 +55,7 @@ impl Magnet {
let mut info_hash_found = false;
let mut id20: Option<Id20> = None;
let mut id32: Option<Id32> = None;
let mut name: Option<String> = None;
let mut trackers = Vec::<String>::new();
let mut files = Vec::<usize>::new();
for (key, value) in url.query_pairs() {
@ -70,6 +74,11 @@ impl Magnet {
}
}
"tr" => trackers.push(value.into()),
"dn" => {
if !value.is_empty() {
name = Some(value.into_owned())
}
}
"so" => {
// Process 'so' values, but silently ignore any which fail parsing
for file_desc in value.split(',') {
@ -100,6 +109,7 @@ impl Magnet {
id20,
id32,
trackers,
name,
select_only: if files.is_empty() { None } else { Some(files) },
}),
false => {