Storing torrent name in ManagedTorrentShared
This commit is contained in:
parent
0a92cf1d65
commit
0fabb453aa
4 changed files with 55 additions and 6 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue