@@ -12,7 +12,9 @@ use matrix_sdk::{
1212 Client , RoomState ,
1313} ;
1414use percent_encoding:: { utf8_percent_encode, AsciiSet , CONTROLS } ;
15- use std:: path:: PathBuf ;
15+ use std:: ffi:: OsStr ;
16+ use std:: path:: { Path , PathBuf } ;
17+
1618use tokio:: fs;
1719use tokio:: io:: AsyncWriteExt ;
1820
@@ -25,6 +27,37 @@ use crate::matrix::verification::handle_verification_request;
2527/// https://url.spec.whatwg.org/#fragment-percent-encode-set
2628const FRAGMENT : & AsciiSet = & CONTROLS . add ( b' ' ) . add ( b'"' ) . add ( b'<' ) . add ( b'>' ) . add ( b'`' ) ;
2729
30+ async fn generate_fresh_file ( dir : PathBuf , filename : & str ) -> Result < ( tokio:: fs:: File , String ) > {
31+ let filename = Path :: new ( filename) ;
32+ let prefix = filename
33+ . file_stem ( )
34+ . and_then ( OsStr :: to_str)
35+ . map ( |s| format ! ( "{}-" , s) )
36+ . unwrap_or_else ( || "noname-" . to_string ( ) ) ;
37+ let suffix = filename
38+ . extension ( )
39+ . and_then ( OsStr :: to_str)
40+ . map ( |s| format ! ( ".{}" , s) )
41+ . unwrap_or_default ( ) ;
42+
43+ tokio:: task:: spawn_blocking ( move || {
44+ let ( filehandle, filepath) = tempfile:: Builder :: new ( )
45+ . prefix ( & prefix)
46+ . suffix ( & suffix)
47+ . tempfile_in ( dir) ?
48+ . keep ( ) ?;
49+
50+ let fresh_filename = filepath
51+ . file_name ( )
52+ . context ( "Internal error: No filename component" ) ?
53+ . to_str ( )
54+ . context ( "Internal error: Non-utf8 filename" ) ?
55+ . to_owned ( ) ;
56+ Ok ( ( tokio:: fs:: File :: from ( filehandle) , fresh_filename) )
57+ } )
58+ . await ?
59+ }
60+
2861#[ async_trait]
2962pub trait SourceUri {
3063 async fn to_uri ( & self , client : & Client , body : & str ) -> Result < String > ;
@@ -54,12 +87,28 @@ impl SourceUri for MediaSource {
5487 . await ?
5588 }
5689 let file = dir. join ( filename) ;
57- fs:: File :: create ( file) . await ?. write_all ( & content) . await ?;
90+ let plainfh = fs:: OpenOptions :: new ( )
91+ . write ( true )
92+ . create_new ( true )
93+ . open ( file)
94+ . await ;
95+ let outfiletuple = match plainfh {
96+ Ok ( fh) => Ok ( ( fh, filename. to_owned ( ) ) ) ,
97+ Err ( err) => {
98+ if err. kind ( ) == std:: io:: ErrorKind :: AlreadyExists {
99+ generate_fresh_file ( dir, filename) . await
100+ } else {
101+ Err ( anyhow:: Error :: from ( err) )
102+ }
103+ }
104+ } ;
105+ let ( mut fh, filename) = outfiletuple?;
106+ fh. write_all ( & content) . await ?;
58107 let url = args ( ) . media_url . as_ref ( ) . unwrap_or ( dir_path) ;
59108 Ok ( format ! (
60109 "{}/{}" ,
61110 url,
62- utf8_percent_encode( filename, FRAGMENT )
111+ utf8_percent_encode( & filename, FRAGMENT )
63112 ) )
64113 }
65114}
0 commit comments