1 // -*- mode: cpp; mode: fold -*-
3 // $Id: acquire-item.cc,v 1.46.2.9 2004/01/16 18:51:11 mdz Exp $
4 /* ######################################################################
6 Acquire Item - Item to acquire
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.
13 ##################################################################### */
15 // Include Files /*{{{*/
16 #include <apt-pkg/acquire-item.h>
17 #include <apt-pkg/configuration.h>
18 #include <apt-pkg/sourcelist.h>
19 #include <apt-pkg/vendorlist.h>
20 #include <apt-pkg/error.h>
21 #include <apt-pkg/strutl.h>
22 #include <apt-pkg/fileutl.h>
23 #include <apt-pkg/md5.h>
24 #include <apt-pkg/sha1.h>
25 #include <apt-pkg/tagfile.h>
39 // Acquire::Item::Item - Constructor /*{{{*/
40 // ---------------------------------------------------------------------
42 pkgAcquire::Item::Item(pkgAcquire
*Owner
) : Owner(Owner
), FileSize(0),
43 PartialSize(0), Mode(0), ID(0), Complete(false),
44 Local(false), QueueCounter(0)
50 // Acquire::Item::~Item - Destructor /*{{{*/
51 // ---------------------------------------------------------------------
53 pkgAcquire::Item::~Item()
58 // Acquire::Item::Failed - Item failed to download /*{{{*/
59 // ---------------------------------------------------------------------
60 /* We return to an idle state if there are still other queues that could
62 void pkgAcquire::Item::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)
65 ErrorText
= LookupTag(Message
,"Message");
66 if (QueueCounter
<= 1)
68 /* This indicates that the file is not available right now but might
69 be sometime later. If we do a retry cycle then this should be
71 if (Cnf
->LocalOnly
== true &&
72 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
84 // Acquire::Item::Start - Item has begun to download /*{{{*/
85 // ---------------------------------------------------------------------
86 /* Stash status and the file size. Note that setting Complete means
87 sub-phases of the acquire process such as decompresion are operating */
88 void pkgAcquire::Item::Start(string
/*Message*/,unsigned long Size
)
90 Status
= StatFetching
;
91 if (FileSize
== 0 && Complete
== false)
95 // Acquire::Item::Done - Item downloaded OK /*{{{*/
96 // ---------------------------------------------------------------------
98 void pkgAcquire::Item::Done(string Message
,unsigned long Size
,string Hash
,
99 pkgAcquire::MethodConfig
*Cnf
)
101 // We just downloaded something..
102 string FileName
= LookupTag(Message
,"Filename");
103 // we only inform the Log class if it was actually not a local thing
104 if (Complete
== false && !Local
&& FileName
== DestFile
)
107 Owner
->Log
->Fetched(Size
,atoi(LookupTag(Message
,"Resume-Point","0").c_str()));
114 ErrorText
= string();
115 Owner
->Dequeue(this);
118 // Acquire::Item::Rename - Rename a file /*{{{*/
119 // ---------------------------------------------------------------------
120 /* This helper function is used by alot of item methods as thier final
122 void pkgAcquire::Item::Rename(string From
,string To
)
124 if (rename(From
.c_str(),To
.c_str()) != 0)
127 snprintf(S
,sizeof(S
),_("rename failed, %s (%s -> %s)."),strerror(errno
),
128 From
.c_str(),To
.c_str());
134 // AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
135 // ---------------------------------------------------------------------
136 /* Get the DiffIndex file first and see if there are patches availabe
137 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
138 * patches. If anything goes wrong in that process, it will fall back to
139 * the original packages file
141 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire
*Owner
,
142 string URI
,string URIDesc
,string ShortDesc
,
143 HashString ExpectedHash
)
144 : Item(Owner
), RealURI(URI
), ExpectedHash(ExpectedHash
),
148 Debug
= _config
->FindB("Debug::pkgAcquire::Diffs",false);
150 Desc
.Description
= URIDesc
+ "/DiffIndex";
152 Desc
.ShortDesc
= ShortDesc
;
153 Desc
.URI
= URI
+ ".diff/Index";
155 DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
156 DestFile
+= URItoFileName(URI
) + string(".DiffIndex");
159 std::clog
<< "pkgAcqDiffIndex: " << Desc
.URI
<< std::endl
;
161 // look for the current package file
162 CurrentPackagesFile
= _config
->FindDir("Dir::State::lists");
163 CurrentPackagesFile
+= URItoFileName(RealURI
);
165 // FIXME: this file:/ check is a hack to prevent fetching
166 // from local sources. this is really silly, and
167 // should be fixed cleanly as soon as possible
168 if(!FileExists(CurrentPackagesFile
) ||
169 Desc
.URI
.substr(0,strlen("file:/")) == "file:/")
171 // we don't have a pkg file or we don't want to queue
173 std::clog
<< "No index file, local or canceld by user" << std::endl
;
179 std::clog
<< "pkgAcqIndexDiffs::pkgAcqIndexDiffs(): "
180 << CurrentPackagesFile
<< std::endl
;
186 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
187 // ---------------------------------------------------------------------
188 /* The only header we use is the last-modified header. */
189 string
pkgAcqDiffIndex::Custom600Headers()
191 string Final
= _config
->FindDir("Dir::State::lists");
192 Final
+= URItoFileName(RealURI
) + string(".IndexDiff");
195 std::clog
<< "Custom600Header-IMS: " << Final
<< std::endl
;
198 if (stat(Final
.c_str(),&Buf
) != 0)
199 return "\nIndex-File: true";
201 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf
.st_mtime
);
204 bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile
) /*{{{*/
207 std::clog
<< "pkgAcqIndexDiffs::ParseIndexDiff() " << IndexDiffFile
212 vector
<DiffInfo
> available_patches
;
214 FileFd
Fd(IndexDiffFile
,FileFd::ReadOnly
);
216 if (_error
->PendingError() == true)
219 if(TF
.Step(Tags
) == true)
226 string tmp
= Tags
.FindS("SHA1-Current");
227 std::stringstream
ss(tmp
);
230 FileFd
fd(CurrentPackagesFile
, FileFd::ReadOnly
);
232 SHA1
.AddFD(fd
.Fd(), fd
.Size());
233 local_sha1
= string(SHA1
.Result());
235 if(local_sha1
== ServerSha1
)
237 // we have the same sha1 as the server
239 std::clog
<< "Package file is up-to-date" << std::endl
;
240 // set found to true, this will queue a pkgAcqIndexDiffs with
241 // a empty availabe_patches
247 std::clog
<< "SHA1-Current: " << ServerSha1
<< std::endl
;
249 // check the historie and see what patches we need
250 string history
= Tags
.FindS("SHA1-History");
251 std::stringstream
hist(history
);
252 while(hist
>> d
.sha1
>> size
>> d
.file
)
254 d
.size
= atoi(size
.c_str());
255 // read until the first match is found
256 if(d
.sha1
== local_sha1
)
258 // from that point on, we probably need all diffs
262 std::clog
<< "Need to get diff: " << d
.file
<< std::endl
;
263 available_patches
.push_back(d
);
268 // we have something, queue the next diff
272 string::size_type last_space
= Description
.rfind(" ");
273 if(last_space
!= string::npos
)
274 Description
.erase(last_space
, Description
.size()-last_space
);
275 new pkgAcqIndexDiffs(Owner
, RealURI
, Description
, Desc
.ShortDesc
,
276 ExpectedHash
, available_patches
);
284 // Nothing found, report and return false
285 // Failing here is ok, if we return false later, the full
286 // IndexFile is queued
288 std::clog
<< "Can't find a patch in the index file" << std::endl
;
292 void pkgAcqDiffIndex::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
) /*{{{*/
295 std::clog
<< "pkgAcqDiffIndex failed: " << Desc
.URI
<< std::endl
296 << "Falling back to normal index file aquire" << std::endl
;
298 new pkgAcqIndex(Owner
, RealURI
, Description
, Desc
.ShortDesc
,
306 void pkgAcqDiffIndex::Done(string Message
,unsigned long Size
,string Md5Hash
, /*{{{*/
307 pkgAcquire::MethodConfig
*Cnf
)
310 std::clog
<< "pkgAcqDiffIndex::Done(): " << Desc
.URI
<< std::endl
;
312 Item::Done(Message
,Size
,Md5Hash
,Cnf
);
315 FinalFile
= _config
->FindDir("Dir::State::lists")+URItoFileName(RealURI
);
317 // sucess in downloading the index
319 FinalFile
+= string(".IndexDiff");
321 std::clog
<< "Renaming: " << DestFile
<< " -> " << FinalFile
323 Rename(DestFile
,FinalFile
);
324 chmod(FinalFile
.c_str(),0644);
325 DestFile
= FinalFile
;
327 if(!ParseDiffIndex(DestFile
))
328 return Failed("", NULL
);
336 // AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
337 // ---------------------------------------------------------------------
338 /* The package diff is added to the queue. one object is constructed
339 * for each diff and the index
341 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire
*Owner
,
342 string URI
,string URIDesc
,string ShortDesc
,
343 HashString ExpectedHash
,
344 vector
<DiffInfo
> diffs
)
345 : Item(Owner
), RealURI(URI
), ExpectedHash(ExpectedHash
),
346 available_patches(diffs
)
349 DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
350 DestFile
+= URItoFileName(URI
);
352 Debug
= _config
->FindB("Debug::pkgAcquire::Diffs",false);
354 Description
= URIDesc
;
356 Desc
.ShortDesc
= ShortDesc
;
358 if(available_patches
.size() == 0)
360 // we are done (yeah!)
366 State
= StateFetchDiff
;
371 void pkgAcqIndexDiffs::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
) /*{{{*/
374 std::clog
<< "pkgAcqIndexDiffs failed: " << Desc
.URI
<< std::endl
375 << "Falling back to normal index file aquire" << std::endl
;
376 new pkgAcqIndex(Owner
, RealURI
, Description
,Desc
.ShortDesc
,
381 // Finish - helper that cleans the item out of the fetcher queue /*{{{*/
382 void pkgAcqIndexDiffs::Finish(bool allDone
)
384 // we restore the original name, this is required, otherwise
385 // the file will be cleaned
388 DestFile
= _config
->FindDir("Dir::State::lists");
389 DestFile
+= URItoFileName(RealURI
);
391 if(!ExpectedHash
.empty() && !ExpectedHash
.VerifyFile(DestFile
))
393 Status
= StatAuthError
;
394 ErrorText
= _("MD5Sum mismatch");
395 Rename(DestFile
,DestFile
+ ".FAILED");
400 // this is for the "real" finish
405 std::clog
<< "\n\nallDone: " << DestFile
<< "\n" << std::endl
;
410 std::clog
<< "Finishing: " << Desc
.URI
<< std::endl
;
417 bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
420 // calc sha1 of the just patched file
421 string FinalFile
= _config
->FindDir("Dir::State::lists");
422 FinalFile
+= URItoFileName(RealURI
);
424 FileFd
fd(FinalFile
, FileFd::ReadOnly
);
426 SHA1
.AddFD(fd
.Fd(), fd
.Size());
427 string local_sha1
= string(SHA1
.Result());
429 std::clog
<< "QueueNextDiff: "
430 << FinalFile
<< " (" << local_sha1
<< ")"<<std::endl
;
432 // remove all patches until the next matching patch is found
433 // this requires the Index file to be ordered
434 for(vector
<DiffInfo
>::iterator I
=available_patches
.begin();
435 available_patches
.size() > 0 &&
436 I
!= available_patches
.end() &&
437 (*I
).sha1
!= local_sha1
;
440 available_patches
.erase(I
);
443 // error checking and falling back if no patch was found
444 if(available_patches
.size() == 0)
450 // queue the right diff
451 Desc
.URI
= string(RealURI
) + ".diff/" + available_patches
[0].file
+ ".gz";
452 Desc
.Description
= Description
+ " " + available_patches
[0].file
+ string(".pdiff");
453 DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
454 DestFile
+= URItoFileName(RealURI
+ ".diff/" + available_patches
[0].file
);
457 std::clog
<< "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc
.URI
<< std::endl
;
464 void pkgAcqIndexDiffs::Done(string Message
,unsigned long Size
,string Md5Hash
, /*{{{*/
465 pkgAcquire::MethodConfig
*Cnf
)
468 std::clog
<< "pkgAcqIndexDiffs::Done(): " << Desc
.URI
<< std::endl
;
470 Item::Done(Message
,Size
,Md5Hash
,Cnf
);
473 FinalFile
= _config
->FindDir("Dir::State::lists")+URItoFileName(RealURI
);
475 // sucess in downloading a diff, enter ApplyDiff state
476 if(State
== StateFetchDiff
)
480 std::clog
<< "Sending to gzip method: " << FinalFile
<< std::endl
;
482 string FileName
= LookupTag(Message
,"Filename");
483 State
= StateUnzipDiff
;
485 Desc
.URI
= "gzip:" + FileName
;
486 DestFile
+= ".decomp";
492 // sucess in downloading a diff, enter ApplyDiff state
493 if(State
== StateUnzipDiff
)
496 // rred excepts the patch as $FinalFile.ed
497 Rename(DestFile
,FinalFile
+".ed");
500 std::clog
<< "Sending to rred method: " << FinalFile
<< std::endl
;
502 State
= StateApplyDiff
;
504 Desc
.URI
= "rred:" + FinalFile
;
511 // success in download/apply a diff, queue next (if needed)
512 if(State
== StateApplyDiff
)
514 // remove the just applied patch
515 available_patches
.erase(available_patches
.begin());
520 std::clog
<< "Moving patched file in place: " << std::endl
521 << DestFile
<< " -> " << FinalFile
<< std::endl
;
523 Rename(DestFile
,FinalFile
);
524 chmod(FinalFile
.c_str(),0644);
526 // see if there is more to download
527 if(available_patches
.size() > 0) {
528 new pkgAcqIndexDiffs(Owner
, RealURI
, Description
, Desc
.ShortDesc
,
529 ExpectedHash
, available_patches
);
536 // AcqIndex::AcqIndex - Constructor /*{{{*/
537 // ---------------------------------------------------------------------
538 /* The package file is added to the queue and a second class is
539 instantiated to fetch the revision file */
540 pkgAcqIndex::pkgAcqIndex(pkgAcquire
*Owner
,
541 string URI
,string URIDesc
,string ShortDesc
,
542 HashString ExpectedHash
, string comprExt
)
543 : Item(Owner
), RealURI(URI
), ExpectedHash(ExpectedHash
)
545 Decompression
= false;
548 DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
549 DestFile
+= URItoFileName(URI
);
553 // autoselect the compression method
554 if(FileExists("/bin/bzip2"))
555 CompressionExtension
= ".bz2";
557 CompressionExtension
= ".gz";
559 CompressionExtension
= (comprExt
== "plain" ? "" : comprExt
);
561 Desc
.URI
= URI
+ CompressionExtension
;
563 Desc
.Description
= URIDesc
;
565 Desc
.ShortDesc
= ShortDesc
;
570 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
571 // ---------------------------------------------------------------------
572 /* The only header we use is the last-modified header. */
573 string
pkgAcqIndex::Custom600Headers()
575 string Final
= _config
->FindDir("Dir::State::lists");
576 Final
+= URItoFileName(RealURI
);
579 if (stat(Final
.c_str(),&Buf
) != 0)
580 return "\nIndex-File: true";
582 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf
.st_mtime
);
585 void pkgAcqIndex::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
) /*{{{*/
587 bool descChanged
= false;
588 // no .bz2 found, retry with .gz
589 if(Desc
.URI
.substr(Desc
.URI
.size()-3) == "bz2") {
590 Desc
.URI
= Desc
.URI
.substr(0,Desc
.URI
.size()-3) + "gz";
592 new pkgAcqIndex(Owner
, RealURI
, Desc
.Description
,Desc
.ShortDesc
,
593 ExpectedHash
, string(".gz"));
596 // no .gz found, retry with uncompressed
597 else if(Desc
.URI
.substr(Desc
.URI
.size()-2) == "gz") {
598 Desc
.URI
= Desc
.URI
.substr(0,Desc
.URI
.size()-2);
600 new pkgAcqIndex(Owner
, RealURI
, Desc
.Description
,Desc
.ShortDesc
,
601 ExpectedHash
, string("plain"));
611 // on decompression failure, remove bad versions in partial/
612 if(Decompression
&& Erase
) {
613 string s
= _config
->FindDir("Dir::State::lists") + "partial/";
614 s
+= URItoFileName(RealURI
);
618 Item::Failed(Message
,Cnf
);
621 // AcqIndex::Done - Finished a fetch /*{{{*/
622 // ---------------------------------------------------------------------
623 /* This goes through a number of states.. On the initial fetch the
624 method could possibly return an alternate filename which points
625 to the uncompressed version of the file. If this is so the file
626 is copied into the partial directory. In all other cases the file
627 is decompressed with a gzip uri. */
628 void pkgAcqIndex::Done(string Message
,unsigned long Size
,string Hash
,
629 pkgAcquire::MethodConfig
*Cfg
)
631 Item::Done(Message
,Size
,Hash
,Cfg
);
633 if (Decompression
== true)
635 if (_config
->FindB("Debug::pkgAcquire::Auth", false))
637 std::cerr
<< std::endl
<< RealURI
<< ": Computed Hash: " << Hash
;
638 std::cerr
<< " Expected Hash: " << ExpectedHash
.toStr() << std::endl
;
641 if (!ExpectedHash
.empty() && ExpectedHash
.toStr() != Hash
)
643 Status
= StatAuthError
;
644 ErrorText
= _("Hash Sum mismatch");
645 Rename(DestFile
,DestFile
+ ".FAILED");
648 // Done, move it into position
649 string FinalFile
= _config
->FindDir("Dir::State::lists");
650 FinalFile
+= URItoFileName(RealURI
);
651 Rename(DestFile
,FinalFile
);
652 chmod(FinalFile
.c_str(),0644);
654 /* We restore the original name to DestFile so that the clean operation
656 DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
657 DestFile
+= URItoFileName(RealURI
);
659 // Remove the compressed version.
661 unlink(DestFile
.c_str());
668 // Handle the unzipd case
669 string FileName
= LookupTag(Message
,"Alt-Filename");
670 if (FileName
.empty() == false)
672 // The files timestamp matches
673 if (StringToBool(LookupTag(Message
,"Alt-IMS-Hit"),false) == true)
675 Decompression
= true;
677 DestFile
+= ".decomp";
678 Desc
.URI
= "copy:" + FileName
;
684 FileName
= LookupTag(Message
,"Filename");
685 if (FileName
.empty() == true)
688 ErrorText
= "Method gave a blank filename";
691 // The files timestamp matches
692 if (StringToBool(LookupTag(Message
,"IMS-Hit"),false) == true)
695 if (FileName
== DestFile
)
700 string compExt
= flExtension(flNotDir(URI(Desc
.URI
).Path
));
701 const char *decompProg
;
703 decompProg
= "bzip2";
704 else if(compExt
== "gz")
706 // flExtensions returns the full name if no extension is found
707 // this is why we have this complicated compare operation here
708 // FIMXE: add a new flJustExtension() that return "" if no
709 // extension is found and use that above so that it can
710 // be tested against ""
711 else if(compExt
== flNotDir(URI(Desc
.URI
).Path
))
714 _error
->Error("Unsupported extension: %s", compExt
.c_str());
718 Decompression
= true;
719 DestFile
+= ".decomp";
720 Desc
.URI
= string(decompProg
) + ":" + FileName
;
725 // AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
726 // ---------------------------------------------------------------------
727 /* The Translation file is added to the queue */
728 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire
*Owner
,
729 string URI
,string URIDesc
,string ShortDesc
)
730 : pkgAcqIndex(Owner
, URI
, URIDesc
, ShortDesc
, HashString(), "")
734 // AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
735 // ---------------------------------------------------------------------
737 void pkgAcqIndexTrans::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)
739 if (Cnf
->LocalOnly
== true ||
740 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == false)
749 Item::Failed(Message
,Cnf
);
752 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire
*Owner
, /*{{{*/
753 string URI
,string URIDesc
,string ShortDesc
,
754 string MetaIndexURI
, string MetaIndexURIDesc
,
755 string MetaIndexShortDesc
,
756 const vector
<IndexTarget
*>* IndexTargets
,
757 indexRecords
* MetaIndexParser
) :
758 Item(Owner
), RealURI(URI
), MetaIndexURI(MetaIndexURI
),
759 MetaIndexURIDesc(MetaIndexURIDesc
), MetaIndexShortDesc(MetaIndexShortDesc
),
760 MetaIndexParser(MetaIndexParser
), IndexTargets(IndexTargets
)
762 DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
763 DestFile
+= URItoFileName(URI
);
765 // remove any partial downloaded sig-file in partial/.
766 // it may confuse proxies and is too small to warrant a
767 // partial download anyway
768 unlink(DestFile
.c_str());
771 Desc
.Description
= URIDesc
;
773 Desc
.ShortDesc
= ShortDesc
;
776 string Final
= _config
->FindDir("Dir::State::lists");
777 Final
+= URItoFileName(RealURI
);
779 if (stat(Final
.c_str(),&Buf
) == 0)
781 // File was already in place. It needs to be re-downloaded/verified
782 // because Release might have changed, we do give it a differnt
783 // name than DestFile because otherwise the http method will
784 // send If-Range requests and there are too many broken servers
785 // out there that do not understand them
786 LastGoodSig
= DestFile
+".reverify";
787 Rename(Final
,LastGoodSig
);
793 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
794 // ---------------------------------------------------------------------
795 /* The only header we use is the last-modified header. */
796 string
pkgAcqMetaSig::Custom600Headers()
799 if (stat(LastGoodSig
.c_str(),&Buf
) != 0)
800 return "\nIndex-File: true";
802 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf
.st_mtime
);
805 void pkgAcqMetaSig::Done(string Message
,unsigned long Size
,string MD5
,
806 pkgAcquire::MethodConfig
*Cfg
)
808 Item::Done(Message
,Size
,MD5
,Cfg
);
810 string FileName
= LookupTag(Message
,"Filename");
811 if (FileName
.empty() == true)
814 ErrorText
= "Method gave a blank filename";
818 if (FileName
!= DestFile
)
820 // We have to copy it into place
822 Desc
.URI
= "copy:" + FileName
;
829 // put the last known good file back on i-m-s hit (it will
830 // be re-verified again)
831 // Else do nothing, we have the new file in DestFile then
832 if(StringToBool(LookupTag(Message
,"IMS-Hit"),false) == true)
833 Rename(LastGoodSig
, DestFile
);
835 // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
836 new pkgAcqMetaIndex(Owner
, MetaIndexURI
, MetaIndexURIDesc
, MetaIndexShortDesc
,
837 DestFile
, IndexTargets
, MetaIndexParser
);
841 void pkgAcqMetaSig::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)/*{{{*/
843 string Final
= _config
->FindDir("Dir::State::lists") + URItoFileName(RealURI
);
845 // if we get a network error we fail gracefully
846 if(Status
== StatTransientNetworkError
)
848 Item::Failed(Message
,Cnf
);
849 // move the sigfile back on transient network failures
850 if(FileExists(DestFile
))
851 Rename(LastGoodSig
,Final
);
853 // set the status back to , Item::Failed likes to reset it
854 Status
= pkgAcquire::Item::StatTransientNetworkError
;
858 // Delete any existing sigfile when the acquire failed
859 unlink(Final
.c_str());
861 // queue a pkgAcqMetaIndex with no sigfile
862 new pkgAcqMetaIndex(Owner
, MetaIndexURI
, MetaIndexURIDesc
, MetaIndexShortDesc
,
863 "", IndexTargets
, MetaIndexParser
);
865 if (Cnf
->LocalOnly
== true ||
866 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == false)
875 Item::Failed(Message
,Cnf
);
878 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire
*Owner
, /*{{{*/
879 string URI
,string URIDesc
,string ShortDesc
,
881 const vector
<struct IndexTarget
*>* IndexTargets
,
882 indexRecords
* MetaIndexParser
) :
883 Item(Owner
), RealURI(URI
), SigFile(SigFile
), IndexTargets(IndexTargets
),
884 MetaIndexParser(MetaIndexParser
), AuthPass(false), IMSHit(false)
886 DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
887 DestFile
+= URItoFileName(URI
);
890 Desc
.Description
= URIDesc
;
892 Desc
.ShortDesc
= ShortDesc
;
898 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
899 // ---------------------------------------------------------------------
900 /* The only header we use is the last-modified header. */
901 string
pkgAcqMetaIndex::Custom600Headers()
903 string Final
= _config
->FindDir("Dir::State::lists");
904 Final
+= URItoFileName(RealURI
);
907 if (stat(Final
.c_str(),&Buf
) != 0)
908 return "\nIndex-File: true";
910 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf
.st_mtime
);
913 void pkgAcqMetaIndex::Done(string Message
,unsigned long Size
,string Hash
, /*{{{*/
914 pkgAcquire::MethodConfig
*Cfg
)
916 Item::Done(Message
,Size
,Hash
,Cfg
);
918 // MetaIndexes are done in two passes: one to download the
919 // metaindex with an appropriate method, and a second to verify it
920 // with the gpgv method
922 if (AuthPass
== true)
928 RetrievalDone(Message
);
930 // Still more retrieving to do
935 // There was no signature file, so we are finished. Download
936 // the indexes without verification.
941 // There was a signature file, so pass it to gpgv for
944 if (_config
->FindB("Debug::pkgAcquire::Auth", false))
945 std::cerr
<< "Metaindex acquired, queueing gpg verification ("
946 << SigFile
<< "," << DestFile
<< ")\n";
948 Desc
.URI
= "gpgv:" + SigFile
;
955 void pkgAcqMetaIndex::RetrievalDone(string Message
) /*{{{*/
957 // We have just finished downloading a Release file (it is not
960 string FileName
= LookupTag(Message
,"Filename");
961 if (FileName
.empty() == true)
964 ErrorText
= "Method gave a blank filename";
968 if (FileName
!= DestFile
)
971 Desc
.URI
= "copy:" + FileName
;
976 // see if the download was a IMSHit
977 IMSHit
= StringToBool(LookupTag(Message
,"IMS-Hit"),false);
980 string FinalFile
= _config
->FindDir("Dir::State::lists");
981 FinalFile
+= URItoFileName(RealURI
);
983 // If we get a IMS hit we can remove the empty file in partial
984 // othersie we move the file in place
986 unlink(DestFile
.c_str());
988 Rename(DestFile
,FinalFile
);
990 chmod(FinalFile
.c_str(),0644);
991 DestFile
= FinalFile
;
994 void pkgAcqMetaIndex::AuthDone(string Message
) /*{{{*/
996 // At this point, the gpgv method has succeeded, so there is a
997 // valid signature from a key in the trusted keyring. We
998 // perform additional verification of its contents, and use them
999 // to verify the indexes we are about to download
1001 if (!MetaIndexParser
->Load(DestFile
))
1003 Status
= StatAuthError
;
1004 ErrorText
= MetaIndexParser
->ErrorText
;
1008 if (!VerifyVendor(Message
))
1013 if (_config
->FindB("Debug::pkgAcquire::Auth", false))
1014 std::cerr
<< "Signature verification succeeded: "
1015 << DestFile
<< std::endl
;
1017 // Download further indexes with verification
1020 // Done, move signature file into position
1022 string VerifiedSigFile
= _config
->FindDir("Dir::State::lists") +
1023 URItoFileName(RealURI
) + ".gpg";
1024 Rename(SigFile
,VerifiedSigFile
);
1025 chmod(VerifiedSigFile
.c_str(),0644);
1028 void pkgAcqMetaIndex::QueueIndexes(bool verify
) /*{{{*/
1030 for (vector
<struct IndexTarget
*>::const_iterator Target
= IndexTargets
->begin();
1031 Target
!= IndexTargets
->end();
1034 HashString ExpectedIndexHash
;
1037 const indexRecords::checkSum
*Record
= MetaIndexParser
->Lookup((*Target
)->MetaKey
);
1040 Status
= StatAuthError
;
1041 ErrorText
= "Unable to find expected entry "
1042 + (*Target
)->MetaKey
+ " in Meta-index file (malformed Release file?)";
1045 ExpectedIndexHash
= Record
->Hash
;
1046 if (_config
->FindB("Debug::pkgAcquire::Auth", false))
1048 std::cerr
<< "Queueing: " << (*Target
)->URI
<< std::endl
;
1049 std::cerr
<< "Expected Hash: " << ExpectedIndexHash
.toStr() << std::endl
;
1051 if (ExpectedIndexHash
.empty())
1053 Status
= StatAuthError
;
1054 ErrorText
= "Unable to find hash sum for "
1055 + (*Target
)->MetaKey
+ " in Meta-index file";
1060 // Queue Packages file (either diff or full packages files, depending
1061 // on the users option)
1062 if(_config
->FindB("Acquire::PDiffs",true) == true)
1063 new pkgAcqDiffIndex(Owner
, (*Target
)->URI
, (*Target
)->Description
,
1064 (*Target
)->ShortDesc
, ExpectedIndexHash
);
1066 new pkgAcqIndex(Owner
, (*Target
)->URI
, (*Target
)->Description
,
1067 (*Target
)->ShortDesc
, ExpectedIndexHash
);
1071 bool pkgAcqMetaIndex::VerifyVendor(string Message
) /*{{{*/
1073 // // Maybe this should be made available from above so we don't have
1074 // // to read and parse it every time?
1075 // pkgVendorList List;
1076 // List.ReadMainList();
1078 // const Vendor* Vndr = NULL;
1079 // for (std::vector<string>::const_iterator I = GPGVOutput.begin(); I != GPGVOutput.end(); I++)
1081 // string::size_type pos = (*I).find("VALIDSIG ");
1082 // if (_config->FindB("Debug::Vendor", false))
1083 // std::cerr << "Looking for VALIDSIG in \"" << (*I) << "\": pos " << pos
1085 // if (pos != std::string::npos)
1087 // string Fingerprint = (*I).substr(pos+sizeof("VALIDSIG"));
1088 // if (_config->FindB("Debug::Vendor", false))
1089 // std::cerr << "Looking for \"" << Fingerprint << "\" in vendor..." <<
1091 // Vndr = List.FindVendor(Fingerprint) != "";
1092 // if (Vndr != NULL);
1096 string::size_type pos
;
1098 // check for missing sigs (that where not fatal because otherwise we had
1101 string msg
= _("There is no public key available for the "
1102 "following key IDs:\n");
1103 pos
= Message
.find("NO_PUBKEY ");
1104 if (pos
!= std::string::npos
)
1106 string::size_type start
= pos
+strlen("NO_PUBKEY ");
1107 string Fingerprint
= Message
.substr(start
, Message
.find("\n")-start
);
1108 missingkeys
+= (Fingerprint
);
1110 if(!missingkeys
.empty())
1111 _error
->Warning("%s", string(msg
+missingkeys
).c_str());
1113 string Transformed
= MetaIndexParser
->GetExpectedDist();
1115 if (Transformed
== "../project/experimental")
1117 Transformed
= "experimental";
1120 pos
= Transformed
.rfind('/');
1121 if (pos
!= string::npos
)
1123 Transformed
= Transformed
.substr(0, pos
);
1126 if (Transformed
== ".")
1131 if (_config
->FindB("Debug::pkgAcquire::Auth", false))
1133 std::cerr
<< "Got Codename: " << MetaIndexParser
->GetDist() << std::endl
;
1134 std::cerr
<< "Expecting Dist: " << MetaIndexParser
->GetExpectedDist() << std::endl
;
1135 std::cerr
<< "Transformed Dist: " << Transformed
<< std::endl
;
1138 if (MetaIndexParser
->CheckDist(Transformed
) == false)
1140 // This might become fatal one day
1141 // Status = StatAuthError;
1142 // ErrorText = "Conflicting distribution; expected "
1143 // + MetaIndexParser->GetExpectedDist() + " but got "
1144 // + MetaIndexParser->GetDist();
1146 if (!Transformed
.empty())
1148 _error
->Warning("Conflicting distribution: %s (expected %s but got %s)",
1149 Desc
.Description
.c_str(),
1150 Transformed
.c_str(),
1151 MetaIndexParser
->GetDist().c_str());
1158 // pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
1159 // ---------------------------------------------------------------------
1161 void pkgAcqMetaIndex::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)
1163 if (AuthPass
== true)
1165 // if we fail the authentication but got the file via a IMS-Hit
1166 // this means that the file wasn't downloaded and that it might be
1167 // just stale (server problem, proxy etc). we delete what we have
1168 // queue it again without i-m-s
1169 // alternatively we could just unlink the file and let the user try again
1175 unlink(DestFile
.c_str());
1177 DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
1178 DestFile
+= URItoFileName(RealURI
);
1184 // gpgv method failed
1185 _error
->Warning("GPG error: %s: %s",
1186 Desc
.Description
.c_str(),
1187 LookupTag(Message
,"Message").c_str());
1191 // No Release file was present, or verification failed, so fall
1192 // back to queueing Packages files without verification
1193 QueueIndexes(false);
1196 // AcqArchive::AcqArchive - Constructor /*{{{*/
1197 // ---------------------------------------------------------------------
1198 /* This just sets up the initial fetch environment and queues the first
1200 pkgAcqArchive::pkgAcqArchive(pkgAcquire
*Owner
,pkgSourceList
*Sources
,
1201 pkgRecords
*Recs
,pkgCache::VerIterator
const &Version
,
1202 string
&StoreFilename
) :
1203 Item(Owner
), Version(Version
), Sources(Sources
), Recs(Recs
),
1204 StoreFilename(StoreFilename
), Vf(Version
.FileList()),
1207 Retries
= _config
->FindI("Acquire::Retries",0);
1209 if (Version
.Arch() == 0)
1211 _error
->Error(_("I wasn't able to locate a file for the %s package. "
1212 "This might mean you need to manually fix this package. "
1213 "(due to missing arch)"),
1214 Version
.ParentPkg().Name());
1218 /* We need to find a filename to determine the extension. We make the
1219 assumption here that all the available sources for this version share
1220 the same extension.. */
1221 // Skip not source sources, they do not have file fields.
1222 for (; Vf
.end() == false; Vf
++)
1224 if ((Vf
.File()->Flags
& pkgCache::Flag::NotSource
) != 0)
1229 // Does not really matter here.. we are going to fail out below
1230 if (Vf
.end() != true)
1232 // If this fails to get a file name we will bomb out below.
1233 pkgRecords::Parser
&Parse
= Recs
->Lookup(Vf
);
1234 if (_error
->PendingError() == true)
1237 // Generate the final file name as: package_version_arch.foo
1238 StoreFilename
= QuoteString(Version
.ParentPkg().Name(),"_:") + '_' +
1239 QuoteString(Version
.VerStr(),"_:") + '_' +
1240 QuoteString(Version
.Arch(),"_:.") +
1241 "." + flExtension(Parse
.FileName());
1244 // check if we have one trusted source for the package. if so, switch
1245 // to "TrustedOnly" mode
1246 for (pkgCache::VerFileIterator i
= Version
.FileList(); i
.end() == false; i
++)
1248 pkgIndexFile
*Index
;
1249 if (Sources
->FindIndex(i
.File(),Index
) == false)
1251 if (_config
->FindB("Debug::pkgAcquire::Auth", false))
1253 std::cerr
<< "Checking index: " << Index
->Describe()
1254 << "(Trusted=" << Index
->IsTrusted() << ")\n";
1256 if (Index
->IsTrusted()) {
1262 // "allow-unauthenticated" restores apts old fetching behaviour
1263 // that means that e.g. unauthenticated file:// uris are higher
1264 // priority than authenticated http:// uris
1265 if (_config
->FindB("APT::Get::AllowUnauthenticated",false) == true)
1269 if (QueueNext() == false && _error
->PendingError() == false)
1270 _error
->Error(_("I wasn't able to locate file for the %s package. "
1271 "This might mean you need to manually fix this package."),
1272 Version
.ParentPkg().Name());
1275 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
1276 // ---------------------------------------------------------------------
1277 /* This queues the next available file version for download. It checks if
1278 the archive is already available in the cache and stashs the MD5 for
1280 bool pkgAcqArchive::QueueNext()
1282 for (; Vf
.end() == false; Vf
++)
1284 // Ignore not source sources
1285 if ((Vf
.File()->Flags
& pkgCache::Flag::NotSource
) != 0)
1288 // Try to cross match against the source list
1289 pkgIndexFile
*Index
;
1290 if (Sources
->FindIndex(Vf
.File(),Index
) == false)
1293 // only try to get a trusted package from another source if that source
1295 if(Trusted
&& !Index
->IsTrusted())
1298 // Grab the text package record
1299 pkgRecords::Parser
&Parse
= Recs
->Lookup(Vf
);
1300 if (_error
->PendingError() == true)
1303 string PkgFile
= Parse
.FileName();
1304 if(Parse
.SHA256Hash() != "")
1305 ExpectedHash
= HashString("SHA256", Parse
.SHA256Hash());
1306 else if (Parse
.SHA1Hash() != "")
1307 ExpectedHash
= HashString("SHA1", Parse
.SHA1Hash());
1309 ExpectedHash
= HashString("MD5Sum", Parse
.MD5Hash());
1310 if (PkgFile
.empty() == true)
1311 return _error
->Error(_("The package index files are corrupted. No Filename: "
1312 "field for package %s."),
1313 Version
.ParentPkg().Name());
1315 Desc
.URI
= Index
->ArchiveURI(PkgFile
);
1316 Desc
.Description
= Index
->ArchiveInfo(Version
);
1318 Desc
.ShortDesc
= Version
.ParentPkg().Name();
1320 // See if we already have the file. (Legacy filenames)
1321 FileSize
= Version
->Size
;
1322 string FinalFile
= _config
->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile
);
1324 if (stat(FinalFile
.c_str(),&Buf
) == 0)
1326 // Make sure the size matches
1327 if ((unsigned)Buf
.st_size
== Version
->Size
)
1332 StoreFilename
= DestFile
= FinalFile
;
1336 /* Hmm, we have a file and its size does not match, this means it is
1337 an old style mismatched arch */
1338 unlink(FinalFile
.c_str());
1341 // Check it again using the new style output filenames
1342 FinalFile
= _config
->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename
);
1343 if (stat(FinalFile
.c_str(),&Buf
) == 0)
1345 // Make sure the size matches
1346 if ((unsigned)Buf
.st_size
== Version
->Size
)
1351 StoreFilename
= DestFile
= FinalFile
;
1355 /* Hmm, we have a file and its size does not match, this shouldnt
1357 unlink(FinalFile
.c_str());
1360 DestFile
= _config
->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename
);
1362 // Check the destination file
1363 if (stat(DestFile
.c_str(),&Buf
) == 0)
1365 // Hmm, the partial file is too big, erase it
1366 if ((unsigned)Buf
.st_size
> Version
->Size
)
1367 unlink(DestFile
.c_str());
1369 PartialSize
= Buf
.st_size
;
1374 Desc
.URI
= Index
->ArchiveURI(PkgFile
);
1375 Desc
.Description
= Index
->ArchiveInfo(Version
);
1377 Desc
.ShortDesc
= Version
.ParentPkg().Name();
1386 // AcqArchive::Done - Finished fetching /*{{{*/
1387 // ---------------------------------------------------------------------
1389 void pkgAcqArchive::Done(string Message
,unsigned long Size
,string CalcHash
,
1390 pkgAcquire::MethodConfig
*Cfg
)
1392 Item::Done(Message
,Size
,CalcHash
,Cfg
);
1395 if (Size
!= Version
->Size
)
1398 ErrorText
= _("Size mismatch");
1403 if(ExpectedHash
.toStr() != CalcHash
)
1406 ErrorText
= _("Hash Sum mismatch");
1407 if(FileExists(DestFile
))
1408 Rename(DestFile
,DestFile
+ ".FAILED");
1412 // Grab the output filename
1413 string FileName
= LookupTag(Message
,"Filename");
1414 if (FileName
.empty() == true)
1417 ErrorText
= "Method gave a blank filename";
1423 // Reference filename
1424 if (FileName
!= DestFile
)
1426 StoreFilename
= DestFile
= FileName
;
1431 // Done, move it into position
1432 string FinalFile
= _config
->FindDir("Dir::Cache::Archives");
1433 FinalFile
+= flNotDir(StoreFilename
);
1434 Rename(DestFile
,FinalFile
);
1436 StoreFilename
= DestFile
= FinalFile
;
1440 // AcqArchive::Failed - Failure handler /*{{{*/
1441 // ---------------------------------------------------------------------
1442 /* Here we try other sources */
1443 void pkgAcqArchive::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)
1445 ErrorText
= LookupTag(Message
,"Message");
1447 /* We don't really want to retry on failed media swaps, this prevents
1448 that. An interesting observation is that permanent failures are not
1450 if (Cnf
->Removable
== true &&
1451 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
1453 // Vf = Version.FileList();
1454 while (Vf
.end() == false) Vf
++;
1455 StoreFilename
= string();
1456 Item::Failed(Message
,Cnf
);
1460 if (QueueNext() == false)
1462 // This is the retry counter
1464 Cnf
->LocalOnly
== false &&
1465 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
1468 Vf
= Version
.FileList();
1469 if (QueueNext() == true)
1473 StoreFilename
= string();
1474 Item::Failed(Message
,Cnf
);
1478 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
1479 // ---------------------------------------------------------------------
1480 bool pkgAcqArchive::IsTrusted()
1485 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
1486 // ---------------------------------------------------------------------
1488 void pkgAcqArchive::Finished()
1490 if (Status
== pkgAcquire::Item::StatDone
&&
1493 StoreFilename
= string();
1496 // AcqFile::pkgAcqFile - Constructor /*{{{*/
1497 // ---------------------------------------------------------------------
1498 /* The file is added to the queue */
1499 pkgAcqFile::pkgAcqFile(pkgAcquire
*Owner
,string URI
,string Hash
,
1500 unsigned long Size
,string Dsc
,string ShortDesc
,
1501 const string
&DestDir
, const string
&DestFilename
) :
1502 Item(Owner
), ExpectedHash(Hash
)
1504 Retries
= _config
->FindI("Acquire::Retries",0);
1506 if(!DestFilename
.empty())
1507 DestFile
= DestFilename
;
1508 else if(!DestDir
.empty())
1509 DestFile
= DestDir
+ "/" + flNotDir(URI
);
1511 DestFile
= flNotDir(URI
);
1515 Desc
.Description
= Dsc
;
1518 // Set the short description to the archive component
1519 Desc
.ShortDesc
= ShortDesc
;
1521 // Get the transfer sizes
1524 if (stat(DestFile
.c_str(),&Buf
) == 0)
1526 // Hmm, the partial file is too big, erase it
1527 if ((unsigned)Buf
.st_size
> Size
)
1528 unlink(DestFile
.c_str());
1530 PartialSize
= Buf
.st_size
;
1536 // AcqFile::Done - Item downloaded OK /*{{{*/
1537 // ---------------------------------------------------------------------
1539 void pkgAcqFile::Done(string Message
,unsigned long Size
,string CalcHash
,
1540 pkgAcquire::MethodConfig
*Cnf
)
1542 Item::Done(Message
,Size
,CalcHash
,Cnf
);
1545 if(!ExpectedHash
.empty() && ExpectedHash
.toStr() != CalcHash
)
1548 ErrorText
= "Hash Sum mismatch";
1549 Rename(DestFile
,DestFile
+ ".FAILED");
1553 string FileName
= LookupTag(Message
,"Filename");
1554 if (FileName
.empty() == true)
1557 ErrorText
= "Method gave a blank filename";
1563 // The files timestamp matches
1564 if (StringToBool(LookupTag(Message
,"IMS-Hit"),false) == true)
1567 // We have to copy it into place
1568 if (FileName
!= DestFile
)
1571 if (_config
->FindB("Acquire::Source-Symlinks",true) == false ||
1572 Cnf
->Removable
== true)
1574 Desc
.URI
= "copy:" + FileName
;
1579 // Erase the file if it is a symlink so we can overwrite it
1581 if (lstat(DestFile
.c_str(),&St
) == 0)
1583 if (S_ISLNK(St
.st_mode
) != 0)
1584 unlink(DestFile
.c_str());
1588 if (symlink(FileName
.c_str(),DestFile
.c_str()) != 0)
1590 ErrorText
= "Link to " + DestFile
+ " failure ";
1597 // AcqFile::Failed - Failure handler /*{{{*/
1598 // ---------------------------------------------------------------------
1599 /* Here we try other sources */
1600 void pkgAcqFile::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)
1602 ErrorText
= LookupTag(Message
,"Message");
1604 // This is the retry counter
1606 Cnf
->LocalOnly
== false &&
1607 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
1614 Item::Failed(Message
,Cnf
);