@@ -11,11 +11,12 @@ use crate::errors::ServiceError;
1111use crate :: models:: category:: CategoryId ;
1212use crate :: models:: info_hash:: InfoHash ;
1313use crate :: models:: response:: { DeletedTorrentResponse , TorrentResponse , TorrentsResponse } ;
14- use crate :: models:: torrent:: { AddTorrentRequest , TorrentId , TorrentListing } ;
14+ use crate :: models:: torrent:: { Metadata , TorrentId , TorrentListing } ;
1515use crate :: models:: torrent_file:: { DbTorrentInfo , Torrent , TorrentFile } ;
1616use crate :: models:: torrent_tag:: { TagId , TorrentTag } ;
1717use crate :: models:: user:: UserId ;
1818use crate :: tracker:: statistics_importer:: StatisticsImporter ;
19+ use crate :: utils:: parse_torrent;
1920use crate :: { tracker, AsCSV } ;
2021
2122const MIN_TORRENT_TITLE_LENGTH : usize = 3 ;
@@ -34,6 +35,14 @@ pub struct Index {
3435 torrent_listing_generator : Arc < DbTorrentListingGenerator > ,
3536}
3637
38+ pub struct AddTorrentRequest {
39+ pub title : String ,
40+ pub description : String ,
41+ pub category : String ,
42+ pub tags : Vec < TagId > ,
43+ pub torrent_buffer : Vec < u8 > ,
44+ }
45+
3746/// User request to generate a torrent listing.
3847#[ derive( Debug , Deserialize ) ]
3948pub struct ListingRequest {
@@ -101,46 +110,75 @@ impl Index {
101110 /// * Unable to insert the torrent into the database.
102111 /// * Unable to add the torrent to the whitelist.
103112 /// * Torrent title is too short.
104- pub async fn add_torrent ( & self , mut torrent_request : AddTorrentRequest , user_id : UserId ) -> Result < TorrentId , ServiceError > {
113+ ///
114+ /// # Panics
115+ ///
116+ /// This function will panic if:
117+ ///
118+ /// * Unable to parse the torrent info-hash.
119+ pub async fn add_torrent (
120+ & self ,
121+ add_torrent_form : AddTorrentRequest ,
122+ user_id : UserId ,
123+ ) -> Result < ( TorrentId , InfoHash ) , ServiceError > {
124+ let metadata = Metadata {
125+ title : add_torrent_form. title ,
126+ description : add_torrent_form. description ,
127+ category : add_torrent_form. category ,
128+ tags : add_torrent_form. tags ,
129+ } ;
130+
131+ metadata. verify ( ) ?;
132+
133+ let mut torrent =
134+ parse_torrent:: decode_torrent ( & add_torrent_form. torrent_buffer ) . map_err ( |_| ServiceError :: InvalidTorrentFile ) ?;
135+
136+ // Make sure that the pieces key has a length that is a multiple of 20
137+ if let Some ( pieces) = torrent. info . pieces . as_ref ( ) {
138+ if pieces. as_ref ( ) . len ( ) % 20 != 0 {
139+ return Err ( ServiceError :: InvalidTorrentPiecesLength ) ;
140+ }
141+ }
142+
105143 let _user = self . user_repository . get_compact ( & user_id) . await ?;
106144
107- torrent_request . torrent . set_announce_urls ( & self . configuration ) . await ;
145+ torrent. set_announce_urls ( & self . configuration ) . await ;
108146
109- if torrent_request . metadata . title . len ( ) < MIN_TORRENT_TITLE_LENGTH {
147+ if metadata. title . len ( ) < MIN_TORRENT_TITLE_LENGTH {
110148 return Err ( ServiceError :: InvalidTorrentTitleLength ) ;
111149 }
112150
113151 let category = self
114152 . category_repository
115- . get_by_name ( & torrent_request . metadata . category )
153+ . get_by_name ( & metadata. category )
116154 . await
117155 . map_err ( |_| ServiceError :: InvalidCategory ) ?;
118156
119- let torrent_id = self . torrent_repository . add ( & torrent_request, user_id, category) . await ?;
157+ let torrent_id = self . torrent_repository . add ( & torrent, & metadata, user_id, category) . await ?;
158+ let info_hash: InfoHash = torrent
159+ . info_hash ( )
160+ . parse ( )
161+ . expect ( "the parsed torrent should have a valid info hash" ) ;
120162
121163 drop (
122164 self . tracker_statistics_importer
123- . import_torrent_statistics ( torrent_id, & torrent_request . torrent . info_hash ( ) )
165+ . import_torrent_statistics ( torrent_id, & torrent. info_hash ( ) )
124166 . await ,
125167 ) ;
126168
127169 // We always whitelist the torrent on the tracker because even if the tracker mode is `public`
128170 // it could be changed to `private` later on.
129- if let Err ( e) = self
130- . tracker_service
131- . whitelist_info_hash ( torrent_request. torrent . info_hash ( ) )
132- . await
133- {
171+ if let Err ( e) = self . tracker_service . whitelist_info_hash ( torrent. info_hash ( ) ) . await {
134172 // If the torrent can't be whitelisted somehow, remove the torrent from database
135173 drop ( self . torrent_repository . delete ( & torrent_id) . await ) ;
136174 return Err ( e) ;
137175 }
138176
139177 self . torrent_tag_repository
140- . link_torrent_to_tags ( & torrent_id, & torrent_request . metadata . tags )
178+ . link_torrent_to_tags ( & torrent_id, & metadata. tags )
141179 . await ?;
142180
143- Ok ( torrent_id)
181+ Ok ( ( torrent_id, info_hash ) )
144182 }
145183
146184 /// Gets a torrent from the Index.
@@ -436,18 +474,13 @@ impl DbTorrentRepository {
436474 /// This function will return an error there is a database error.
437475 pub async fn add (
438476 & self ,
439- torrent_request : & AddTorrentRequest ,
477+ torrent : & Torrent ,
478+ metadata : & Metadata ,
440479 user_id : UserId ,
441480 category : Category ,
442481 ) -> Result < TorrentId , Error > {
443482 self . database
444- . insert_torrent_and_get_id (
445- & torrent_request. torrent ,
446- user_id,
447- category. category_id ,
448- & torrent_request. metadata . title ,
449- & torrent_request. metadata . description ,
450- )
483+ . insert_torrent_and_get_id ( torrent, user_id, category. category_id , & metadata. title , & metadata. description )
451484 . await
452485 }
453486
0 commit comments