11//! # Messages and their identifiers.
22
3- use std:: collections:: BTreeSet ;
3+ use std:: collections:: { hash_map:: DefaultHasher , BTreeSet } ;
4+ use std:: hash:: { Hash , Hasher } ;
45use std:: path:: { Path , PathBuf } ;
56
67use anyhow:: { ensure, format_err, Context as _, Result } ;
@@ -288,9 +289,14 @@ WHERE id=?;
288289 ret += & format ! ( "Error: {error}" ) ;
289290 }
290291
291- if let Some ( path) = msg. get_file ( context) {
292+ if let Some ( path) = msg. get_filedata_path ( context) {
292293 let bytes = get_filebytes ( context, & path) . await ?;
293- ret += & format ! ( "\n File: {}, {} bytes\n " , path. display( ) , bytes) ;
294+ ret += & format ! (
295+ "\n File: {}, name: {}, {} bytes\n " ,
296+ path. display( ) ,
297+ msg. get_filename( ) . unwrap_or_default( ) ,
298+ bytes
299+ ) ;
294300 }
295301
296302 if msg. viewtype != Viewtype :: Text {
@@ -575,14 +581,49 @@ impl Message {
575581 None
576582 }
577583
578- /// Returns the full path to the file associated with a message.
579- pub fn get_file ( & self , context : & Context ) -> Option < PathBuf > {
584+ /// Returns the full path to the file associated with a message. The filename isn't meaningful,
585+ /// only the extension is preserved.
586+ pub fn get_filedata_path ( & self , context : & Context ) -> Option < PathBuf > {
580587 self . param . get_path ( Param :: File , context) . unwrap_or ( None )
581588 }
582589
590+ /// Returns the full path to the copy of the file, associated with a message, with the original
591+ /// filename.
592+ ///
593+ /// Deprecated, use `get_filedata_path()` and `get_filename()` instead.
594+ pub async fn get_file ( & self , context : & Context ) -> Result < Option < PathBuf > > {
595+ let Some ( data_path) = self . get_filedata_path ( context) else {
596+ return Ok ( None ) ;
597+ } ;
598+ let Some ( filename) = self . get_filename ( ) else {
599+ return Ok ( Some ( data_path) ) ;
600+ } ;
601+ if data_path. with_file_name ( & filename) == data_path {
602+ return Ok ( Some ( data_path) ) ;
603+ }
604+ let mut hasher = DefaultHasher :: new ( ) ;
605+ data_path
606+ . file_name ( )
607+ . context ( "no filename??" ) ?
608+ . hash ( & mut hasher) ;
609+ let dirname = format ! ( "tmp.{:016x}" , hasher. finish( ) ) ;
610+ let dir_path = context. get_blobdir ( ) . join ( & dirname) ;
611+ fs:: create_dir_all ( & dir_path) . await ?;
612+ let file_path = dir_path. join ( & filename) ;
613+ let mut src = fs:: OpenOptions :: new ( ) . read ( true ) . open ( data_path) . await ?;
614+ let mut dst = fs:: OpenOptions :: new ( )
615+ . write ( true )
616+ . create ( true )
617+ . truncate ( true )
618+ . open ( & file_path)
619+ . await ?;
620+ io:: copy ( & mut src, & mut dst) . await ?;
621+ Ok ( Some ( file_path) )
622+ }
623+
583624 /// Save file copy at the user-provided path.
584625 pub async fn save_file ( & self , context : & Context , path : & Path ) -> Result < ( ) > {
585- let path_src = self . get_file ( context) . context ( "No file" ) ?;
626+ let path_src = self . get_filedata_path ( context) . context ( "No file" ) ?;
586627 let mut src = fs:: OpenOptions :: new ( ) . read ( true ) . open ( path_src) . await ?;
587628 let mut dst = fs:: OpenOptions :: new ( )
588629 . write ( true )
@@ -728,8 +769,6 @@ impl Message {
728769 }
729770
730771 /// Returns original filename (as shown in chat).
731- ///
732- /// To get the full path, use [`Self::get_file()`].
733772 pub fn get_filename ( & self ) -> Option < String > {
734773 if let Some ( name) = self . param . get ( Param :: Filename ) {
735774 return Some ( name. to_string ( ) ) ;
@@ -904,7 +943,7 @@ impl Message {
904943 return None ;
905944 }
906945
907- if let Some ( filename) = self . get_file ( context) {
946+ if let Some ( filename) = self . get_filedata_path ( context) {
908947 if let Ok ( ref buf) = read_file ( context, filename) . await {
909948 if let Ok ( ( typ, headers, _) ) = split_armored_data ( buf) {
910949 if typ == pgp:: armor:: BlockType :: Message {
@@ -1925,7 +1964,7 @@ pub enum Viewtype {
19251964
19261965 /// Animated GIF message.
19271966 /// File, width and height are set via dc_msg_set_file(), dc_msg_set_dimension()
1928- /// and retrieved via dc_msg_get_file (), dc_msg_get_width(), dc_msg_get_height().
1967+ /// and retrieved via dc_msg_get_filedata_path (), dc_msg_get_width(), dc_msg_get_height().
19291968 Gif = 21 ,
19301969
19311970 /// Message containing a sticker, similar to image.
@@ -1935,26 +1974,24 @@ pub enum Viewtype {
19351974
19361975 /// Message containing an Audio file.
19371976 /// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
1938- /// and retrieved via dc_msg_get_file (), dc_msg_get_duration().
1977+ /// and retrieved via dc_msg_get_filedata_path (), dc_msg_get_duration().
19391978 Audio = 40 ,
19401979
19411980 /// A voice message that was directly recorded by the user.
19421981 /// For all other audio messages, the type #DC_MSG_AUDIO should be used.
19431982 /// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
1944- /// and retrieved via dc_msg_get_file (), dc_msg_get_duration()
1983+ /// and retrieved via dc_msg_get_filedata_path (), dc_msg_get_duration().
19451984 Voice = 41 ,
19461985
19471986 /// Video messages.
19481987 /// File, width, height and durarion
19491988 /// are set via dc_msg_set_file(), dc_msg_set_dimension(), dc_msg_set_duration()
1950- /// and retrieved via
1951- /// dc_msg_get_file(), dc_msg_get_width(),
1989+ /// and retrieved via dc_msg_get_filedata_path(), dc_msg_get_width(),
19521990 /// dc_msg_get_height(), dc_msg_get_duration().
19531991 Video = 50 ,
19541992
19551993 /// Message containing any file, eg. a PDF.
1956- /// The file is set via dc_msg_set_file()
1957- /// and retrieved via dc_msg_get_file().
1994+ /// The file is set via dc_msg_set_file() and retrieved via dc_msg_get_filedata_path().
19581995 File = 60 ,
19591996
19601997 /// Message is an invitation to a videochat.
0 commit comments