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