15ea22d65a81f28e02c5dbbea8c30f6d64969e2c
[apt.git] / apt-pkg / acquire-item.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: acquire-item.cc,v 1.46 2003/02/02 22:19:17 jgg Exp $
4 /* ######################################################################
5
6    Acquire Item - Item to acquire
7
8    Each item can download to exactly one file at a time. This means you
9    cannot create an item that fetches two uri's to two files at the same 
10    time. The pkgAcqIndex class creates a second class upon instantiation
11    to fetch the other index files because of this.
12
13    ##################################################################### */
14                                                                         /*}}}*/
15 // Include Files                                                        /*{{{*/
16 #ifdef __GNUG__
17 #pragma implementation "apt-pkg/acquire-item.h"
18 #endif
19 #include <apt-pkg/acquire-item.h>
20 #include <apt-pkg/configuration.h>
21 #include <apt-pkg/sourcelist.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/strutl.h>
24 #include <apt-pkg/fileutl.h>
25
26 // CNC:2002-07-03
27 #include <apt-pkg/repository.h>
28 #include <apt-pkg/md5.h>
29 #include <config.h>
30 #include <apt-pkg/luaiface.h>
31 #include <iostream>
32 #include <assert.h>
33 using namespace std;
34
35 #include <apti18n.h>
36     
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <errno.h>
40 #include <string>
41 #include <stdio.h>
42                                                                         /*}}}*/
43
44 using std::string;
45
46 // CNC:2002-07-03
47 // VerifyChecksums - Check MD5 and SHA-1 checksums of a file            /*{{{*/
48 // ---------------------------------------------------------------------
49 /* Returns false only if the checksums fail (the file not existing is not
50    a checksum mismatch) */
51 bool VerifyChecksums(string File,unsigned long Size,string MD5)
52 {
53    struct stat Buf;
54    
55    if (stat(File.c_str(),&Buf) != 0) 
56       return true;
57
58    if (Buf.st_size != Size)
59    {
60       if (_config->FindB("Acquire::Verbose", false) == true)
61          cout << "Size of "<<File<<" did not match what's in the checksum list and was redownloaded."<<endl;
62       return false;
63    }
64
65    if (MD5.empty() == false)
66    {
67       MD5Summation md5sum = MD5Summation();
68       FileFd F(File, FileFd::ReadOnly);
69       
70       md5sum.AddFD(F.Fd(), F.Size());
71       if (md5sum.Result().Value() != MD5)
72       {
73          if (_config->FindB("Acquire::Verbose", false) == true)
74             cout << "MD5Sum of "<<File<<" did not match what's in the checksum list and was redownloaded."<<endl;
75          return false;
76       }
77    }
78    
79    return true;
80 }
81                                                                         /*}}}*/
82 // Acquire::Item::Item - Constructor                                    /*{{{*/
83 // ---------------------------------------------------------------------
84 /* */
85 pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
86                        PartialSize(0), Mode(0), ID(0), Complete(false), 
87                        Local(false), QueueCounter(0)
88 {
89    Owner->Add(this);
90    Status = StatIdle;
91 }
92                                                                         /*}}}*/
93 // Acquire::Item::~Item - Destructor                                    /*{{{*/
94 // ---------------------------------------------------------------------
95 /* */
96 pkgAcquire::Item::~Item()
97 {
98    Owner->Remove(this);
99 }
100                                                                         /*}}}*/
101 // Acquire::Item::Failed - Item failed to download                      /*{{{*/
102 // ---------------------------------------------------------------------
103 /* We return to an idle state if there are still other queues that could
104    fetch this object */
105 void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
106 {
107    Status = StatIdle;
108    ErrorText = LookupTag(Message,"Message");
109    if (QueueCounter <= 1)
110    {
111       /* This indicates that the file is not available right now but might
112          be sometime later. If we do a retry cycle then this should be
113          retried [CDROMs] */
114       if (Cnf->LocalOnly == true &&
115           StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
116       {
117          Status = StatIdle;
118          Dequeue();
119          return;
120       }
121       
122       Status = StatError;
123       Dequeue();
124    }   
125 }
126                                                                         /*}}}*/
127 // Acquire::Item::Start - Item has begun to download                    /*{{{*/
128 // ---------------------------------------------------------------------
129 /* Stash status and the file size. Note that setting Complete means 
130    sub-phases of the acquire process such as decompresion are operating */
131 void pkgAcquire::Item::Start(string /*Message*/,unsigned long Size)
132 {
133    Status = StatFetching;
134    if (FileSize == 0 && Complete == false)
135       FileSize = Size;
136 }
137                                                                         /*}}}*/
138 // Acquire::Item::Done - Item downloaded OK                             /*{{{*/
139 // ---------------------------------------------------------------------
140 /* */
141 void pkgAcquire::Item::Done(string Message,unsigned long Size,string,
142                             pkgAcquire::MethodConfig *Cnf)
143 {
144    // We just downloaded something..
145    string FileName = LookupTag(Message,"Filename");
146    if (Complete == false && FileName == DestFile)
147    {
148       if (Owner->Log != 0)
149          Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
150    }
151
152    if (FileSize == 0)
153       FileSize= Size;
154    
155    Status = StatDone;
156    ErrorText = string();
157    Owner->Dequeue(this);
158 }
159                                                                         /*}}}*/
160 // Acquire::Item::Rename - Rename a file                                /*{{{*/
161 // ---------------------------------------------------------------------
162 /* This helper function is used by alot of item methods as thier final
163    step */
164 void pkgAcquire::Item::Rename(string From,string To)
165 {
166    if (rename(From.c_str(),To.c_str()) != 0)
167    {
168       char S[300];
169       snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
170               From.c_str(),To.c_str());
171       Status = StatError;
172       ErrorText = S;
173    }   
174 }
175                                                                         /*}}}*/
176
177 // AcqIndex::AcqIndex - Constructor                                     /*{{{*/
178 // ---------------------------------------------------------------------
179 /* The package file is added to the queue and a second class is 
180    instantiated to fetch the revision file */   
181 // CNC:2002-07-03
182 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,pkgRepository *Repository,
183                          string URI,string URIDesc,string ShortDesc) :
184                       Item(Owner), RealURI(URI), Repository(Repository)
185 {
186    Decompression = false;
187    Erase = false;
188    
189    DestFile = _config->FindDir("Dir::State::lists") + "partial/";
190    DestFile += URItoFileName(URI);
191
192    // Create the item
193    // CNC:2002-07-03
194    Desc.URI = URI + _config->Find("Acquire::ComprExtension", ".bz2");
195    Desc.Description = URIDesc;
196    Desc.Owner = this;
197    Desc.ShortDesc = ShortDesc;
198       
199    // CNC:2002-07-03
200    // If we're verifying authentication, check whether the size and
201    // checksums match, if not, delete the cached files and force redownload
202    string MD5Hash;
203    unsigned long Size;
204
205    if (Repository != NULL)
206    {
207       if (Repository->HasRelease() == true)
208       {
209          if (Repository->FindChecksums(RealURI,Size,MD5Hash) == false)
210          {
211             if (Repository->IsAuthenticated() == true)
212             {
213                _error->Error(_("%s is not listed in the checksum list for its repository"),
214                              RealURI.c_str());
215                return;
216             }
217             else
218                _error->Warning("Release file did not contain checksum information for %s",
219                                RealURI.c_str());
220          }
221
222          string FinalFile = _config->FindDir("Dir::State::lists");
223          FinalFile += URItoFileName(RealURI);
224
225          if (VerifyChecksums(FinalFile,Size,MD5Hash) == false)
226          {
227             unlink(FinalFile.c_str());
228             unlink(DestFile.c_str());
229          }
230       }
231       else if (Repository->IsAuthenticated() == true)
232       {
233          _error->Error(_("Release information not available for %s"),
234                        URI.c_str());
235          return;
236       }
237    }
238
239    QueueURI(Desc);
240 }
241                                                                         /*}}}*/
242 // AcqIndex::Custom600Headers - Insert custom request headers           /*{{{*/
243 // ---------------------------------------------------------------------
244 /* The only header we use is the last-modified header. */
245 string pkgAcqIndex::Custom600Headers()
246 {
247    string Final = _config->FindDir("Dir::State::lists");
248    Final += URItoFileName(RealURI);
249    
250    struct stat Buf;
251    if (stat(Final.c_str(),&Buf) != 0)
252       return "\nIndex-File: true";
253    
254    return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
255 }
256                                                                         /*}}}*/
257 // AcqIndex::Done - Finished a fetch                                    /*{{{*/
258 // ---------------------------------------------------------------------
259 /* This goes through a number of states.. On the initial fetch the
260    method could possibly return an alternate filename which points
261    to the uncompressed version of the file. If this is so the file
262    is copied into the partial directory. In all other cases the file
263    is decompressed with a gzip uri. */
264 void pkgAcqIndex::Done(string Message,unsigned long Size,string MD5,
265                        pkgAcquire::MethodConfig *Cfg)
266 {
267    Item::Done(Message,Size,MD5,Cfg);
268
269    if (Decompression == true)
270    {
271       // CNC:2002-07-03
272       unsigned long FSize;
273       string MD5Hash;
274
275       if (Repository != NULL && Repository->HasRelease() == true &&
276           Repository->FindChecksums(RealURI,FSize,MD5Hash) == true)
277       {
278          // We must always get here if the repository is authenticated
279          
280          if (FSize != Size)
281          {
282             Status = StatError;
283             ErrorText = _("Size mismatch");
284             Rename(DestFile,DestFile + ".FAILED");
285             if (_config->FindB("Acquire::Verbose",false) == true) 
286                _error->Warning("Size mismatch of index file %s: %ul was supposed to be %ul",
287                                RealURI.c_str(), Size, FSize);
288             return;
289          }
290             
291          if (MD5.empty() == false && MD5Hash != MD5)
292          {
293             Status = StatError;
294             ErrorText = _("MD5Sum mismatch");
295             Rename(DestFile,DestFile + ".FAILED");
296             if (_config->FindB("Acquire::Verbose",false) == true) 
297                _error->Warning("MD5Sum mismatch of index file %s: %s was supposed to be %s",
298                                RealURI.c_str(), MD5.c_str(), MD5Hash.c_str());
299             return;
300          }
301       }
302       else
303       {
304          // Redundant security check
305          assert(Repository == NULL || Repository->IsAuthenticated() == false);
306       }
307          
308       // Done, move it into position
309       string FinalFile = _config->FindDir("Dir::State::lists");
310       FinalFile += URItoFileName(RealURI);
311       Rename(DestFile,FinalFile);
312       chmod(FinalFile.c_str(),0644);
313       
314       /* We restore the original name to DestFile so that the clean operation
315          will work OK */
316       DestFile = _config->FindDir("Dir::State::lists") + "partial/";
317       DestFile += URItoFileName(RealURI);
318       
319       // Remove the compressed version.
320       if (Erase == true)
321          unlink(DestFile.c_str());
322       return;
323    }
324
325    Erase = false;
326    Complete = true;
327    
328    // Handle the unzipd case
329    string FileName = LookupTag(Message,"Alt-Filename");
330    if (FileName.empty() == false)
331    {
332       // The files timestamp matches
333       if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
334          return;
335       
336       Decompression = true;
337       Local = true;
338       DestFile += ".decomp";
339       Desc.URI = "copy:" + FileName;
340       QueueURI(Desc);
341       Mode = "copy";
342       return;
343    }
344
345    FileName = LookupTag(Message,"Filename");
346    if (FileName.empty() == true)
347    {
348       Status = StatError;
349       ErrorText = "Method gave a blank filename";
350    }
351    
352    // The files timestamp matches
353    if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
354       return;
355
356    if (FileName == DestFile)
357       Erase = true;
358    else
359       Local = true;
360    
361    Decompression = true;
362    DestFile += ".decomp";
363    // CNC:2002-07-03
364    Desc.URI = "bzip2:" + FileName;
365    QueueURI(Desc);
366    // CNC:2002-07-03
367    Mode = "bzip2";
368 }
369                                                                         /*}}}*/
370
371 // AcqIndexRel::pkgAcqIndexRel - Constructor                            /*{{{*/
372 // ---------------------------------------------------------------------
373 /* The Release file is added to the queue */
374 // CNC:2002-07-03
375 pkgAcqIndexRel::pkgAcqIndexRel(pkgAcquire *Owner,pkgRepository *Repository,
376                                string URI,string URIDesc,string ShortDesc,
377                                bool Master) :
378                       Item(Owner), RealURI(URI), Master(Master),
379                       Repository(Repository)
380 {
381    // CNC:2002-07-09
382    assert(Master == false || Repository != NULL);
383    Authentication = false;
384    Erase = false;
385
386    DestFile = _config->FindDir("Dir::State::lists") + "partial/";
387    DestFile += URItoFileName(URI);
388    
389    // Create the item
390    Desc.URI = URI;
391    Desc.Description = URIDesc;
392    Desc.ShortDesc = ShortDesc;
393    Desc.Owner = this;
394
395    // CNC:2002-07-09
396    string MD5Hash;
397    unsigned long Size;
398    if (Master == false && Repository != NULL)
399    {
400       if (Repository->HasRelease() == true)
401       {
402          if (Repository->FindChecksums(RealURI,Size,MD5Hash) == false)
403          {
404             if (Repository->IsAuthenticated() == true)
405             {
406                _error->Error(_("%s is not listed in the checksum list for its repository"),
407                              RealURI.c_str());
408                return;
409             }
410             else
411                _error->Warning("Release file did not contain checksum information for %s",
412                                RealURI.c_str());
413          }
414
415          string FinalFile = _config->FindDir("Dir::State::lists");
416          FinalFile += URItoFileName(RealURI);
417
418          if (VerifyChecksums(FinalFile,Size,MD5Hash) == false)
419          {
420             unlink(FinalFile.c_str());
421             unlink(DestFile.c_str()); // Necessary?
422          }
423       }
424       else if (Repository->IsAuthenticated() == true)
425       {
426          _error->Error(_("Release information not available for %s"),
427                        URI.c_str());
428          return;
429       }
430    }
431
432    QueueURI(Desc);
433 }
434                                                                         /*}}}*/
435 // AcqIndexRel::Custom600Headers - Insert custom request headers        /*{{{*/
436 // ---------------------------------------------------------------------
437 /* The only header we use is the last-modified header. */
438 string pkgAcqIndexRel::Custom600Headers()
439 {
440    string Final = _config->FindDir("Dir::State::lists");
441    Final += URItoFileName(RealURI);
442    
443    struct stat Buf;
444    if (stat(Final.c_str(),&Buf) != 0)
445       return "\nIndex-File: true";
446
447    // CNC:2002-07-11
448    string LOI = "";
449    if (Master == true)
450       LOI = "\nLocal-Only-IMS: true";
451    return LOI + "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
452 }
453                                                                         /*}}}*/
454 // AcqIndexRel::Done - Item downloaded OK                               /*{{{*/
455 // ---------------------------------------------------------------------
456 /* The release file was not placed into the download directory then
457    a copy URI is generated and it is copied there otherwise the file
458    in the partial directory is moved into .. and the URI is finished. */
459 void pkgAcqIndexRel::Done(string Message,unsigned long Size,string MD5,
460                           pkgAcquire::MethodConfig *Cfg)
461 {
462    Item::Done(Message,Size,MD5,Cfg);
463
464    // CNC:2002-07-03
465    if (Authentication == true) 
466    {
467       if (Repository->IsAuthenticated() == true)
468       {
469          // Do the fingerprint matching magic
470          string FingerPrint = LookupTag(Message,"Signature-Fingerprint");
471
472          if (FingerPrint.empty() == true)
473          {
474             Status = StatError;
475             ErrorText = _("No valid signatures found in Release file");
476             return;
477          }
478
479          // Match fingerprint of Release file
480          if (Repository->FingerPrint != FingerPrint)
481          {
482             Status = StatError;
483             ErrorText = _("Signature fingerprint of Release file does not match (expected ")
484                +Repository->FingerPrint+_(", got ")+FingerPrint+")";
485             return;
486          }
487       }
488
489       // Done, move it into position
490       string FinalFile = _config->FindDir("Dir::State::lists");
491       FinalFile += URItoFileName(RealURI);
492       Rename(DestFile,FinalFile);
493       chmod(FinalFile.c_str(),0644);
494
495       /* We restore the original name to DestFile so that the clean operation
496          will work OK */
497       DestFile = _config->FindDir("Dir::State::lists") + "partial/";
498       DestFile += URItoFileName(RealURI);
499       
500       // Remove the compressed version.
501       if (Erase == true)
502          unlink(DestFile.c_str());
503
504       // Update the hashes and file sizes for this repository
505       if (Repository->ParseRelease(FinalFile) == false && 
506           Repository->IsAuthenticated() == true)
507       {
508          Status = StatError;
509          ErrorText = _("Could not read checksum list from Release file");
510       }
511       return;
512    }
513    
514    string FileName = LookupTag(Message,"Filename");
515    if (FileName.empty() == true)
516    {
517       Status = StatError;
518       ErrorText = "Method gave a blank filename";
519       return;
520    }
521
522    // CNC:2002-07-11
523    Erase = false;
524    Complete = true;
525    
526    // The files timestamp matches
527    if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
528    {
529       // CNC:2002-07-11
530       if (Master == true)
531       {
532          // We've got a LocalOnly IMS
533          string FinalFile = _config->FindDir("Dir::State::lists");
534          FinalFile += URItoFileName(RealURI);
535          Repository->ParseRelease(FinalFile);
536       }
537       return;
538    }
539    
540    // We have to copy it into place
541    if (FileName != DestFile)
542    {
543       Local = true;
544       Desc.URI = "copy:" + FileName;
545       QueueURI(Desc);
546       return;
547    }
548    
549    // CNC:2002-07-03
550    unsigned long FSize;
551    string MD5Hash;
552    if (Master == false && Repository != NULL
553        && Repository->HasRelease() == true
554        && Repository->FindChecksums(RealURI,FSize,MD5Hash) == true)
555    {
556       if (FSize != Size)
557       {
558          Status = StatError;
559          ErrorText = _("Size mismatch");
560          Rename(DestFile,DestFile + ".FAILED");
561          if (_config->FindB("Acquire::Verbose",false) == true) 
562             _error->Warning("Size mismatch of index file %s: %ul was supposed to be %ul",
563                             RealURI.c_str(), Size, FSize);
564          return;
565       }
566       if (MD5.empty() == false && MD5Hash != MD5)
567       {
568          Status = StatError;
569          ErrorText = _("MD5Sum mismatch");
570          Rename(DestFile,DestFile + ".FAILED");
571          if (_config->FindB("Acquire::Verbose",false) == true) 
572             _error->Warning("MD5Sum mismatch of index file %s: %s was supposed to be %s",
573                             RealURI.c_str(), MD5.c_str(), MD5Hash.c_str());
574          return;
575       }
576    }
577
578    if (Master == false || Repository->IsAuthenticated() == false)
579    {
580       // Done, move it into position
581       string FinalFile = _config->FindDir("Dir::State::lists");
582       FinalFile += URItoFileName(RealURI);
583       Rename(DestFile,FinalFile);
584       chmod(FinalFile.c_str(),0644);
585
586       // extract checksums from the Release file
587       if (Master == true)
588          Repository->ParseRelease(FinalFile);
589    }
590    else 
591    {
592       if (FileName == DestFile)
593          Erase = true;
594       else
595          Local = true;
596    
597       // Still have the authentication phase
598       Authentication = true;
599       DestFile += ".auth";
600       Desc.URI = "gpg:" + FileName;
601       QueueURI(Desc);
602       Mode = "gpg";
603    }
604 }
605                                                                         /*}}}*/
606 // AcqIndexRel::Failed - Silence failure messages for missing rel files /*{{{*/
607 // ---------------------------------------------------------------------
608 /* */
609 void pkgAcqIndexRel::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
610 {
611    if (Cnf->LocalOnly == true || 
612        StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
613    {      
614       // CNC:2002-07-03
615       if (Master == false || Repository->IsAuthenticated() == false)
616       {
617          // Ignore this
618          Status = StatDone;
619          Complete = false;
620          Dequeue();
621          return;
622       }
623    }
624    
625    Item::Failed(Message,Cnf);
626 }
627                                                                         /*}}}*/
628
629 // AcqArchive::AcqArchive - Constructor                                 /*{{{*/
630 // ---------------------------------------------------------------------
631 /* This just sets up the initial fetch environment and queues the first
632    possibilitiy */
633 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
634                              pkgRecords *Recs,pkgCache::VerIterator const &Version,
635                              string &StoreFilename) :
636                Item(Owner), Version(Version), Sources(Sources), Recs(Recs), 
637                StoreFilename(StoreFilename), Vf(Version.FileList())
638 {
639    Retries = _config->FindI("Acquire::Retries",0);
640
641    if (Version.Arch() == 0)
642    {
643       _error->Error(_("I wasn't able to locate a file for the %s package. "
644                       "This might mean you need to manually fix this package. "
645                       "(due to missing arch)"),
646                     Version.ParentPkg().Name());
647       return;
648    }
649    
650    /* We need to find a filename to determine the extension. We make the
651       assumption here that all the available sources for this version share
652       the same extension.. */
653    // Skip not source sources, they do not have file fields.
654    for (; Vf.end() == false; Vf++)
655    {
656       if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
657          continue;
658       break;
659    }
660    
661    // Does not really matter here.. we are going to fail out below
662    if (Vf.end() != true)
663    {     
664       // If this fails to get a file name we will bomb out below.
665       pkgRecords::Parser &Parse = Recs->Lookup(Vf);
666       if (_error->PendingError() == true)
667          return;
668             
669       // Generate the final file name as: package_version_arch.foo
670       StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
671                       QuoteString(Version.VerStr(),"_:") + '_' +
672                       QuoteString(Version.Arch(),"_:.") + 
673                       "." + flExtension(Parse.FileName());
674    }
675       
676    // Select a source
677    if (QueueNext() == false && _error->PendingError() == false)
678       _error->Error(_("I wasn't able to locate file for the %s package. "
679                     "This might mean you need to manually fix this package."),
680                     Version.ParentPkg().Name());
681 }
682                                                                         /*}}}*/
683 // AcqArchive::QueueNext - Queue the next file source                   /*{{{*/
684 // ---------------------------------------------------------------------
685 /* This queues the next available file version for download. It checks if
686    the archive is already available in the cache and stashs the MD5 for
687    checking later. */
688 bool pkgAcqArchive::QueueNext()
689 {   
690    for (; Vf.end() == false; Vf++)
691    {
692       // Ignore not source sources
693       if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
694          continue;
695
696       // Try to cross match against the source list
697       pkgIndexFile *Index;
698       if (Sources->FindIndex(Vf.File(),Index) == false)
699             continue;
700       
701       // Grab the text package record
702       pkgRecords::Parser &Parse = Recs->Lookup(Vf);
703       if (_error->PendingError() == true)
704          return false;
705       
706       string PkgFile = Parse.FileName();
707       MD5 = Parse.MD5Hash();
708       if (PkgFile.empty() == true)
709          return _error->Error(_("The package index files are corrupted. No Filename: "
710                               "field for package %s."),
711                               Version.ParentPkg().Name());
712
713       // See if we already have the file. (Legacy filenames)
714       FileSize = Version->Size;
715       string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
716       struct stat Buf;
717       if (stat(FinalFile.c_str(),&Buf) == 0)
718       {
719          // Make sure the size matches
720          if ((unsigned)Buf.st_size == Version->Size)
721          {
722             Complete = true;
723             Local = true;
724             Status = StatDone;
725             StoreFilename = DestFile = FinalFile;
726             return true;
727          }
728          
729          /* Hmm, we have a file and its size does not match, this means it is
730             an old style mismatched arch */
731          unlink(FinalFile.c_str());
732       }
733
734       // Check it again using the new style output filenames
735       FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
736       if (stat(FinalFile.c_str(),&Buf) == 0)
737       {
738          // Make sure the size matches
739          if ((unsigned)Buf.st_size == Version->Size)
740          {
741             Complete = true;
742             Local = true;
743             Status = StatDone;
744             StoreFilename = DestFile = FinalFile;
745             return true;
746          }
747          
748          /* Hmm, we have a file and its size does not match, this shouldnt
749             happen.. */
750          unlink(FinalFile.c_str());
751       }
752
753       DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
754       
755       // Check the destination file
756       if (stat(DestFile.c_str(),&Buf) == 0)
757       {
758          // Hmm, the partial file is too big, erase it
759          if ((unsigned)Buf.st_size > Version->Size)
760             unlink(DestFile.c_str());
761          else
762             PartialSize = Buf.st_size;
763       }
764       
765       // Create the item
766       Local = false;
767       Desc.URI = Index->ArchiveURI(PkgFile);
768       Desc.Description = Index->ArchiveInfo(Version);
769       Desc.Owner = this;
770       Desc.ShortDesc = Version.ParentPkg().Name();
771       QueueURI(Desc);
772
773       Vf++;
774       return true;
775    }
776    return false;
777 }   
778                                                                         /*}}}*/
779
780 // CNC:2003-03-19
781 #ifdef WITH_LUA
782 // ScriptsAcquireDone - Script trigger.                                 /*{{{*/
783 // ---------------------------------------------------------------------
784 /* */
785 template<class T>
786 static void ScriptsAcquireDone(const char *ConfKey,
787                                string &StoreFilename,
788                                string &ErrorText,
789                                T &Status)
790 {
791    if (_lua->HasScripts(ConfKey) == true) {
792       _lua->SetGlobal("acquire_filename", StoreFilename.c_str());
793       _lua->SetGlobal("acquire_error", (const char *)NULL);
794       _lua->RunScripts(ConfKey, true);
795       const char *Error = _lua->GetGlobalStr("acquire_error");
796       if (Error != NULL && *Error != 0) {
797          Status = pkgAcquire::Item::StatError;
798          ErrorText = Error;
799       }
800       _lua->ResetGlobals();
801    }
802 }
803                                                                         /*}}}*/
804 #endif
805
806 // AcqArchive::Done - Finished fetching                                 /*{{{*/
807 // ---------------------------------------------------------------------
808 /* */
809 void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash,
810                          pkgAcquire::MethodConfig *Cfg)
811 {
812    Item::Done(Message,Size,Md5Hash,Cfg);
813    
814    // Check the size
815    if (Size != Version->Size)
816    {
817       Status = StatError;
818       ErrorText = _("Size mismatch");
819       return;
820    }
821    
822    // Check the md5
823    if (Md5Hash.empty() == false && MD5.empty() == false)
824    {
825       if (Md5Hash != MD5)
826       {
827          Status = StatError;
828          ErrorText = _("MD5Sum mismatch");
829          Rename(DestFile,DestFile + ".FAILED");
830          return;
831       }
832    }
833
834    // Grab the output filename
835    string FileName = LookupTag(Message,"Filename");
836    if (FileName.empty() == true)
837    {
838       Status = StatError;
839       ErrorText = "Method gave a blank filename";
840       return;
841    }
842
843    Complete = true;
844
845    // Reference filename
846    if (FileName != DestFile)
847    {
848       StoreFilename = DestFile = FileName;
849       Local = true;
850
851 // CNC:2003-03-19
852 #ifdef WITH_LUA
853       ScriptsAcquireDone("Scripts::Acquire::Archive::Done",
854                          StoreFilename, ErrorText, Status);
855 #endif
856
857       return;
858    }
859    
860    // Done, move it into position
861    string FinalFile = _config->FindDir("Dir::Cache::Archives");
862    FinalFile += flNotDir(StoreFilename);
863    Rename(DestFile,FinalFile);
864    
865    StoreFilename = DestFile = FinalFile;
866    Complete = true;
867
868 // CNC:2003-03-19
869 #ifdef WITH_LUA
870    ScriptsAcquireDone("Scripts::Acquire::Archive::Done",
871                       StoreFilename, ErrorText, Status);
872 #endif
873
874 }
875                                                                         /*}}}*/
876 // AcqArchive::Failed - Failure handler                                 /*{{{*/
877 // ---------------------------------------------------------------------
878 /* Here we try other sources */
879 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
880 {
881    ErrorText = LookupTag(Message,"Message");
882    
883    /* We don't really want to retry on failed media swaps, this prevents 
884       that. An interesting observation is that permanent failures are not
885       recorded. */
886    if (Cnf->Removable == true && 
887        StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
888    {
889       // Vf = Version.FileList();
890       while (Vf.end() == false) Vf++;
891       StoreFilename = string();
892       Item::Failed(Message,Cnf);
893       return;
894    }
895    
896    if (QueueNext() == false)
897    {
898       // This is the retry counter
899       if (Retries != 0 &&
900           Cnf->LocalOnly == false &&
901           StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
902       {
903          Retries--;
904          Vf = Version.FileList();
905          if (QueueNext() == true)
906             return;
907       }
908       
909       StoreFilename = string();
910       Item::Failed(Message,Cnf);
911    }
912 }
913                                                                         /*}}}*/
914 // AcqArchive::Finished - Fetching has finished, tidy up                /*{{{*/
915 // ---------------------------------------------------------------------
916 /* */
917 void pkgAcqArchive::Finished()
918 {
919    if (Status == pkgAcquire::Item::StatDone &&
920        Complete == true)
921       return;
922    StoreFilename = string();
923 }
924                                                                         /*}}}*/
925
926 // AcqFile::pkgAcqFile - Constructor                                    /*{{{*/
927 // ---------------------------------------------------------------------
928 /* The file is added to the queue */
929 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string MD5,
930                        unsigned long Size,string Dsc,string ShortDesc) :
931                        Item(Owner), Md5Hash(MD5)
932 {
933    Retries = _config->FindI("Acquire::Retries",0);
934    
935    DestFile = flNotDir(URI);
936    
937    // Create the item
938    Desc.URI = URI;
939    Desc.Description = Dsc;
940    Desc.Owner = this;
941
942    // Set the short description to the archive component
943    Desc.ShortDesc = ShortDesc;
944       
945    // Get the transfer sizes
946    FileSize = Size;
947    struct stat Buf;
948    if (stat(DestFile.c_str(),&Buf) == 0)
949    {
950       // Hmm, the partial file is too big, erase it
951       if ((unsigned)Buf.st_size > Size)
952          unlink(DestFile.c_str());
953       else
954          PartialSize = Buf.st_size;
955    }
956    
957    QueueURI(Desc);
958 }
959                                                                         /*}}}*/
960 // AcqFile::Done - Item downloaded OK                                   /*{{{*/
961 // ---------------------------------------------------------------------
962 /* */
963 void pkgAcqFile::Done(string Message,unsigned long Size,string MD5,
964                       pkgAcquire::MethodConfig *Cnf)
965 {
966    // Check the md5
967    if (Md5Hash.empty() == false && MD5.empty() == false)
968    {
969       if (Md5Hash != MD5)
970       {
971          Status = StatError;
972          ErrorText = "MD5Sum mismatch";
973          Rename(DestFile,DestFile + ".FAILED");
974          return;
975       }
976    }
977    
978    Item::Done(Message,Size,MD5,Cnf);
979
980    string FileName = LookupTag(Message,"Filename");
981    if (FileName.empty() == true)
982    {
983       Status = StatError;
984       ErrorText = "Method gave a blank filename";
985       return;
986    }
987
988    Complete = true;
989    
990    // The files timestamp matches
991    if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
992       return;
993    
994    // We have to copy it into place
995    if (FileName != DestFile)
996    {
997       Local = true;
998       if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
999           Cnf->Removable == true)
1000       {
1001          Desc.URI = "copy:" + FileName;
1002          QueueURI(Desc);
1003          return;
1004       }
1005       
1006       // Erase the file if it is a symlink so we can overwrite it
1007       struct stat St;
1008       if (lstat(DestFile.c_str(),&St) == 0)
1009       {
1010          if (S_ISLNK(St.st_mode) != 0)
1011             unlink(DestFile.c_str());
1012          // CNC:2003-12-11 - Check if FileName == DestFile
1013          else {
1014             struct stat St2;
1015             if (stat(FileName.c_str(), &St2) == 0
1016                 && St.st_ino == St2.st_ino)
1017                return;
1018          }
1019       }
1020       
1021       // Symlink the file
1022       if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
1023       {
1024          ErrorText = "Link to " + DestFile + " failure ";
1025          Status = StatError;
1026          Complete = false;
1027       }      
1028    }
1029 }
1030                                                                         /*}}}*/
1031 // AcqFile::Failed - Failure handler                                    /*{{{*/
1032 // ---------------------------------------------------------------------
1033 /* Here we try other sources */
1034 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1035 {
1036    ErrorText = LookupTag(Message,"Message");
1037    
1038    // This is the retry counter
1039    if (Retries != 0 &&
1040        Cnf->LocalOnly == false &&
1041        StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1042    {
1043       Retries--;
1044       QueueURI(Desc);
1045       return;
1046    }
1047    
1048    Item::Failed(Message,Cnf);
1049 }
1050                                                                         /*}}}*/
1051 // vim:sts=3:sw=3