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 {
|
let mut r = TorrentDetailsResponse {
|
||||||
id: Some(id),
|
id: Some(id),
|
||||||
info_hash: mgr.shared().info_hash.as_string(),
|
info_hash: mgr.shared().info_hash.as_string(),
|
||||||
name: mgr
|
name: mgr.name(),
|
||||||
.with_metadata(|r| r.info.name.as_ref().map(|n| n.to_string()))
|
|
||||||
.ok()
|
|
||||||
.flatten(),
|
|
||||||
output_folder: mgr
|
output_folder: mgr
|
||||||
.shared()
|
.shared()
|
||||||
.options
|
.options
|
||||||
|
|
@ -249,6 +246,7 @@ impl Api {
|
||||||
Some(handle.id()),
|
Some(handle.id()),
|
||||||
&info_hash,
|
&info_hash,
|
||||||
handle.metadata.load().as_ref().map(|r| &r.info),
|
handle.metadata.load().as_ref().map(|r| &r.info),
|
||||||
|
handle.name().as_deref(),
|
||||||
only_files.as_deref(),
|
only_files.as_deref(),
|
||||||
output_folder,
|
output_folder,
|
||||||
)
|
)
|
||||||
|
|
@ -383,6 +381,7 @@ impl Api {
|
||||||
Some(id),
|
Some(id),
|
||||||
&handle.info_hash(),
|
&handle.info_hash(),
|
||||||
handle.metadata.load().as_ref().map(|r| &r.info),
|
handle.metadata.load().as_ref().map(|r| &r.info),
|
||||||
|
handle.name().as_deref(),
|
||||||
handle.only_files().as_deref(),
|
handle.only_files().as_deref(),
|
||||||
handle
|
handle
|
||||||
.shared()
|
.shared()
|
||||||
|
|
@ -419,6 +418,7 @@ impl Api {
|
||||||
None,
|
None,
|
||||||
&info_hash,
|
&info_hash,
|
||||||
Some(&info),
|
Some(&info),
|
||||||
|
None,
|
||||||
only_files.as_deref(),
|
only_files.as_deref(),
|
||||||
output_folder.to_string_lossy().into_owned().to_string(),
|
output_folder.to_string_lossy().into_owned().to_string(),
|
||||||
)
|
)
|
||||||
|
|
@ -429,6 +429,7 @@ impl Api {
|
||||||
Some(id),
|
Some(id),
|
||||||
&handle.info_hash(),
|
&handle.info_hash(),
|
||||||
handle.metadata.load().as_ref().map(|r| &r.info),
|
handle.metadata.load().as_ref().map(|r| &r.info),
|
||||||
|
handle.name().as_deref(),
|
||||||
handle.only_files().as_deref(),
|
handle.only_files().as_deref(),
|
||||||
handle
|
handle
|
||||||
.shared()
|
.shared()
|
||||||
|
|
@ -532,6 +533,7 @@ fn make_torrent_details(
|
||||||
id: Option<TorrentId>,
|
id: Option<TorrentId>,
|
||||||
info_hash: &Id20,
|
info_hash: &Id20,
|
||||||
info: Option<&TorrentMetaV1Info<ByteBufOwned>>,
|
info: Option<&TorrentMetaV1Info<ByteBufOwned>>,
|
||||||
|
name: Option<&str>,
|
||||||
only_files: Option<&[usize]>,
|
only_files: Option<&[usize]>,
|
||||||
output_folder: String,
|
output_folder: String,
|
||||||
) -> Result<TorrentDetailsResponse> {
|
) -> Result<TorrentDetailsResponse> {
|
||||||
|
|
@ -564,7 +566,9 @@ fn make_torrent_details(
|
||||||
Ok(TorrentDetailsResponse {
|
Ok(TorrentDetailsResponse {
|
||||||
id,
|
id,
|
||||||
info_hash: info_hash.as_string(),
|
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),
|
files: Some(files),
|
||||||
output_folder,
|
output_folder,
|
||||||
stats: None,
|
stats: None,
|
||||||
|
|
|
||||||
|
|
@ -477,6 +477,7 @@ struct InternalAddResult {
|
||||||
info_hash: Id20,
|
info_hash: Id20,
|
||||||
metadata: Option<TorrentMetadata>,
|
metadata: Option<TorrentMetadata>,
|
||||||
trackers: Vec<String>,
|
trackers: Vec<String>,
|
||||||
|
name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
|
|
@ -899,6 +900,7 @@ impl Session {
|
||||||
info_hash,
|
info_hash,
|
||||||
trackers: magnet.trackers,
|
trackers: magnet.trackers,
|
||||||
metadata: None,
|
metadata: None,
|
||||||
|
name: magnet.name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
|
|
@ -943,6 +945,7 @@ impl Session {
|
||||||
torrent.info_bytes,
|
torrent.info_bytes,
|
||||||
)?),
|
)?),
|
||||||
trackers,
|
trackers,
|
||||||
|
name: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -956,6 +959,7 @@ impl Session {
|
||||||
fn get_default_subfolder_for_torrent(
|
fn get_default_subfolder_for_torrent(
|
||||||
&self,
|
&self,
|
||||||
info: &TorrentMetaV1Info<ByteBufOwned>,
|
info: &TorrentMetaV1Info<ByteBufOwned>,
|
||||||
|
magnet_name: Option<&str>,
|
||||||
) -> anyhow::Result<Option<PathBuf>> {
|
) -> anyhow::Result<Option<PathBuf>> {
|
||||||
let files = info
|
let files = info
|
||||||
.iter_file_details()?
|
.iter_file_details()?
|
||||||
|
|
@ -964,11 +968,23 @@ impl Session {
|
||||||
if files.len() < 2 {
|
if files.len() < 2 {
|
||||||
return Ok(None);
|
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 {
|
if let Some(name) = &info.name {
|
||||||
let s =
|
let s =
|
||||||
std::str::from_utf8(name.as_slice()).context("invalid UTF-8 in torrent name")?;
|
std::str::from_utf8(name.as_slice()).context("invalid UTF-8 in torrent name")?;
|
||||||
|
check_valid(s)?;
|
||||||
return Ok(Some(PathBuf::from(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 the subfolder name be the longest filename
|
||||||
let longest = files
|
let longest = files
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -989,6 +1005,7 @@ impl Session {
|
||||||
info_hash,
|
info_hash,
|
||||||
metadata,
|
metadata,
|
||||||
trackers,
|
trackers,
|
||||||
|
name,
|
||||||
} = add_res;
|
} = add_res;
|
||||||
|
|
||||||
let peer_stream_permanent = !opts.paused && !opts.list_only;
|
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) {
|
let output_folder = match (opts.output_folder, opts.sub_folder) {
|
||||||
(None, None) => self.output_folder.join(
|
(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(),
|
.unwrap_or_default(),
|
||||||
),
|
),
|
||||||
(Some(o), None) => PathBuf::from(o),
|
(Some(o), None) => PathBuf::from(o),
|
||||||
|
|
@ -1118,6 +1135,7 @@ impl Session {
|
||||||
},
|
},
|
||||||
connector: self.connector.clone(),
|
connector: self.connector.clone(),
|
||||||
session: Arc::downgrade(self),
|
session: Arc::downgrade(self),
|
||||||
|
magnet_name: name,
|
||||||
});
|
});
|
||||||
|
|
||||||
let initializing = Arc::new(TorrentStateInitializing::new(
|
let initializing = Arc::new(TorrentStateInitializing::new(
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,7 @@ pub struct TorrentMetadata {
|
||||||
pub info_bytes: Bytes,
|
pub info_bytes: Bytes,
|
||||||
pub lengths: Lengths,
|
pub lengths: Lengths,
|
||||||
pub file_infos: FileInfos,
|
pub file_infos: FileInfos,
|
||||||
|
pub name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TorrentMetadata {
|
impl TorrentMetadata {
|
||||||
|
|
@ -162,12 +163,18 @@ impl TorrentMetadata {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<anyhow::Result<Vec<FileInfo>>>()?;
|
.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 {
|
Ok(Self {
|
||||||
info,
|
info,
|
||||||
torrent_bytes,
|
torrent_bytes,
|
||||||
info_bytes,
|
info_bytes,
|
||||||
lengths,
|
lengths,
|
||||||
file_infos,
|
file_infos,
|
||||||
|
name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -188,6 +195,9 @@ pub struct ManagedTorrentShared {
|
||||||
pub(crate) connector: Arc<StreamConnector>,
|
pub(crate) connector: Arc<StreamConnector>,
|
||||||
pub(crate) storage_factory: BoxStorageFactory,
|
pub(crate) storage_factory: BoxStorageFactory,
|
||||||
pub(crate) session: Weak<Session>,
|
pub(crate) session: Weak<Session>,
|
||||||
|
|
||||||
|
// "dn" from magnet link
|
||||||
|
pub(crate) magnet_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ManagedTorrent {
|
pub struct ManagedTorrent {
|
||||||
|
|
@ -204,6 +214,13 @@ impl ManagedTorrent {
|
||||||
self.shared.id
|
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 {
|
pub fn shared(&self) -> &ManagedTorrentShared {
|
||||||
&self.shared
|
&self.shared
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ pub struct Magnet {
|
||||||
id20: Option<Id20>,
|
id20: Option<Id20>,
|
||||||
id32: Option<Id32>,
|
id32: Option<Id32>,
|
||||||
pub trackers: Vec<String>,
|
pub trackers: Vec<String>,
|
||||||
|
pub name: Option<String>,
|
||||||
select_only: Option<Vec<usize>>,
|
select_only: Option<Vec<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,6 +30,7 @@ impl Magnet {
|
||||||
id20: Some(id20),
|
id20: Some(id20),
|
||||||
id32: None,
|
id32: None,
|
||||||
trackers,
|
trackers,
|
||||||
|
name: None,
|
||||||
select_only,
|
select_only,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -40,6 +42,7 @@ impl Magnet {
|
||||||
return Ok(Magnet {
|
return Ok(Magnet {
|
||||||
id20: Some(id20),
|
id20: Some(id20),
|
||||||
id32: None,
|
id32: None,
|
||||||
|
name: None,
|
||||||
trackers: vec![],
|
trackers: vec![],
|
||||||
select_only: None,
|
select_only: None,
|
||||||
});
|
});
|
||||||
|
|
@ -52,6 +55,7 @@ impl Magnet {
|
||||||
let mut info_hash_found = false;
|
let mut info_hash_found = false;
|
||||||
let mut id20: Option<Id20> = None;
|
let mut id20: Option<Id20> = None;
|
||||||
let mut id32: Option<Id32> = None;
|
let mut id32: Option<Id32> = None;
|
||||||
|
let mut name: Option<String> = None;
|
||||||
let mut trackers = Vec::<String>::new();
|
let mut trackers = Vec::<String>::new();
|
||||||
let mut files = Vec::<usize>::new();
|
let mut files = Vec::<usize>::new();
|
||||||
for (key, value) in url.query_pairs() {
|
for (key, value) in url.query_pairs() {
|
||||||
|
|
@ -70,6 +74,11 @@ impl Magnet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"tr" => trackers.push(value.into()),
|
"tr" => trackers.push(value.into()),
|
||||||
|
"dn" => {
|
||||||
|
if !value.is_empty() {
|
||||||
|
name = Some(value.into_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
"so" => {
|
"so" => {
|
||||||
// Process 'so' values, but silently ignore any which fail parsing
|
// Process 'so' values, but silently ignore any which fail parsing
|
||||||
for file_desc in value.split(',') {
|
for file_desc in value.split(',') {
|
||||||
|
|
@ -100,6 +109,7 @@ impl Magnet {
|
||||||
id20,
|
id20,
|
||||||
id32,
|
id32,
|
||||||
trackers,
|
trackers,
|
||||||
|
name,
|
||||||
select_only: if files.is_empty() { None } else { Some(files) },
|
select_only: if files.is_empty() { None } else { Some(files) },
|
||||||
}),
|
}),
|
||||||
false => {
|
false => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue