@@ -8,9 +8,12 @@ use manifest::Component;
8
8
use manifest:: Manifest as ManifestV2 ;
9
9
use manifestation:: { Manifestation , UpdateStatus , Changes } ;
10
10
11
- use std:: path:: Path ;
11
+ use std:: path:: { Path , PathBuf } ;
12
+ use std:: ops;
13
+ use url:: Url ;
12
14
use std:: fmt;
13
15
use std:: env;
16
+ use std:: fs;
14
17
15
18
use regex:: Regex ;
16
19
use sha2:: { Sha256 , Digest } ;
@@ -482,9 +485,87 @@ pub fn download_and_check<'a>(url_str: &str,
482
485
pub struct DownloadCfg < ' a > {
483
486
pub dist_root : & ' a str ,
484
487
pub temp_cfg : & ' a temp:: Cfg ,
488
+ pub download_dir : & ' a PathBuf ,
485
489
pub notify_handler : & ' a Fn ( Notification ) ,
486
490
}
487
491
492
+
493
+ pub struct File {
494
+ path : PathBuf ,
495
+ }
496
+
497
+ impl ops:: Deref for File {
498
+ type Target = Path ;
499
+
500
+ fn deref ( & self ) -> & Path {
501
+ ops:: Deref :: deref ( & self . path )
502
+ }
503
+ }
504
+
505
+ impl < ' a > DownloadCfg < ' a > {
506
+
507
+ pub fn download ( & self , url : & Url , hash : & str , notify_handler : & ' a Fn ( Notification ) ) -> Result < File > {
508
+
509
+ try!( utils:: ensure_dir_exists ( "Download Directory" , & self . download_dir , & |n| notify_handler ( n. into ( ) ) ) ) ;
510
+ let target_file = self . download_dir . join ( Path :: new ( hash) ) ;
511
+
512
+ if target_file. exists ( ) {
513
+ let mut hasher = Sha256 :: new ( ) ;
514
+ use std:: io:: Read ;
515
+ let mut downloaded = try!( fs:: File :: open ( & target_file) . chain_err ( || "opening already downloaded file" ) ) ;
516
+ let mut buf = [ 0 ; 1024 ] ;
517
+ loop {
518
+ if let Ok ( n) = downloaded. read ( & mut buf) {
519
+ if n == 0 { break ; }
520
+ hasher. input ( & buf[ ..n] ) ;
521
+ } else {
522
+ break ;
523
+ }
524
+ }
525
+ let cached_result = hasher. result_str ( ) ;
526
+ if hash == cached_result {
527
+ notify_handler ( Notification :: FileAlreadyDownloaded ) ;
528
+ notify_handler ( Notification :: ChecksumValid ( & url. to_string ( ) ) ) ;
529
+ return Ok ( File { path : target_file, } ) ;
530
+ } else {
531
+ notify_handler ( Notification :: CachedFileChecksumFailed ) ;
532
+ try!( fs:: remove_file ( & target_file) . chain_err ( || "cleaning up previous download" ) ) ;
533
+ }
534
+ }
535
+
536
+ let mut hasher = Sha256 :: new ( ) ;
537
+
538
+ try!( utils:: download_file ( & url,
539
+ & target_file,
540
+ Some ( & mut hasher) ,
541
+ & |n| notify_handler ( n. into ( ) ) ) ) ;
542
+
543
+ let actual_hash = hasher. result_str ( ) ;
544
+
545
+ if hash != actual_hash {
546
+ // Incorrect hash
547
+ return Err ( ErrorKind :: ChecksumFailed {
548
+ url : url. to_string ( ) ,
549
+ expected : hash. to_string ( ) ,
550
+ calculated : actual_hash,
551
+ } . into ( ) ) ;
552
+ } else {
553
+ notify_handler ( Notification :: ChecksumValid ( & url. to_string ( ) ) ) ;
554
+ return Ok ( File { path : target_file, } )
555
+ }
556
+ }
557
+
558
+ pub fn clean ( & self , hashes : & Vec < String > ) -> Result < ( ) > {
559
+ for hash in hashes. iter ( ) {
560
+ let used_file = self . download_dir . join ( hash) ;
561
+ if self . download_dir . join ( & used_file) . exists ( ) {
562
+ try!( fs:: remove_file ( used_file) . chain_err ( || "cleaning up cached downloads" ) ) ;
563
+ }
564
+ }
565
+ Ok ( ( ) )
566
+ }
567
+ }
568
+
488
569
pub fn download_hash ( url : & str , cfg : DownloadCfg ) -> Result < String > {
489
570
let hash_url = try!( utils:: parse_url ( & ( url. to_owned ( ) + ".sha256" ) ) ) ;
490
571
let hash_file = try!( cfg. temp_cfg . new_file ( ) ) ;
@@ -551,7 +632,7 @@ pub fn update_from_dist_<'a>(download: DownloadCfg<'a>,
551
632
Ok ( Some ( ( m, hash) ) ) => {
552
633
return match try!( manifestation. update ( & m,
553
634
changes,
554
- & download. temp_cfg ,
635
+ & download,
555
636
download. notify_handler . clone ( ) ) ) {
556
637
UpdateStatus :: Unchanged => Ok ( None ) ,
557
638
UpdateStatus :: Changed => Ok ( Some ( hash) ) ,
0 commit comments