]>
git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: acquire-item.cc,v 1.46 2003/02/02 22:19:17 jgg 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 /*{{{*/
17 #pragma implementation "apt-pkg/acquire-item.h"
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>
37 // Acquire::Item::Item - Constructor /*{{{*/
38 // ---------------------------------------------------------------------
40 pkgAcquire::Item::Item(pkgAcquire
*Owner
) : Owner(Owner
), FileSize(0),
41 PartialSize(0), Mode(0), ID(0), Complete(false),
42 Local(false), QueueCounter(0)
48 // Acquire::Item::~Item - Destructor /*{{{*/
49 // ---------------------------------------------------------------------
51 pkgAcquire::Item::~Item()
56 // Acquire::Item::Failed - Item failed to download /*{{{*/
57 // ---------------------------------------------------------------------
58 /* We return to an idle state if there are still other queues that could
60 void pkgAcquire::Item::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)
63 ErrorText
= LookupTag(Message
,"Message");
64 if (QueueCounter
<= 1)
66 /* This indicates that the file is not available right now but might
67 be sometime later. If we do a retry cycle then this should be
69 if (Cnf
->LocalOnly
== true &&
70 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
82 // Acquire::Item::Start - Item has begun to download /*{{{*/
83 // ---------------------------------------------------------------------
84 /* Stash status and the file size. Note that setting Complete means
85 sub-phases of the acquire process such as decompresion are operating */
86 void pkgAcquire::Item::Start(string
/*Message*/,unsigned long Size
)
88 Status
= StatFetching
;
89 if (FileSize
== 0 && Complete
== false)
93 // Acquire::Item::Done - Item downloaded OK /*{{{*/
94 // ---------------------------------------------------------------------
96 void pkgAcquire::Item::Done(string Message
,unsigned long Size
,string
,
97 pkgAcquire::MethodConfig
*Cnf
)
99 // We just downloaded something..
100 string FileName
= LookupTag(Message
,"Filename");
101 if (Complete
== false && FileName
== DestFile
)
104 Owner
->Log
->Fetched(Size
,atoi(LookupTag(Message
,"Resume-Point","0").c_str()));
111 ErrorText
= string();
112 Owner
->Dequeue(this);
115 // Acquire::Item::Rename - Rename a file /*{{{*/
116 // ---------------------------------------------------------------------
117 /* This helper function is used by alot of item methods as thier final
119 void pkgAcquire::Item::Rename(string From
,string To
)
121 if (rename(From
.c_str(),To
.c_str()) != 0)
124 snprintf(S
,sizeof(S
),_("rename failed, %s (%s -> %s)."),strerror(errno
),
125 From
.c_str(),To
.c_str());
132 // AcqIndex::AcqIndex - Constructor /*{{{*/
133 // ---------------------------------------------------------------------
134 /* The package file is added to the queue and a second class is
135 instantiated to fetch the revision file */
136 pkgAcqIndex::pkgAcqIndex(pkgAcquire
*Owner
,
137 string URI
,string URIDesc
,string ShortDesc
) :
138 Item(Owner
), RealURI(URI
)
140 Decompression
= false;
143 DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
144 DestFile
+= URItoFileName(URI
);
147 if(FileExists("/usr/bin/bzip2"))
148 Desc
.URI
= URI
+ ".bz2";
150 Desc
.URI
= URI
+ ".gz";
151 Desc
.Description
= URIDesc
;
153 Desc
.ShortDesc
= ShortDesc
;
158 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
159 // ---------------------------------------------------------------------
160 /* The only header we use is the last-modified header. */
161 string
pkgAcqIndex::Custom600Headers()
163 string Final
= _config
->FindDir("Dir::State::lists");
164 Final
+= URItoFileName(RealURI
);
167 if (stat(Final
.c_str(),&Buf
) != 0)
168 return "\nIndex-File: true";
170 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf
.st_mtime
);
174 void pkgAcqIndex::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)
176 // no .bz2 found, retry with .gz
177 if(Desc
.URI
.substr(Desc
.URI
.size()-3,Desc
.URI
.size()-1) == "bz2") {
178 Desc
.URI
= Desc
.URI
.substr(0,Desc
.URI
.size()-3) + "gz";
184 Item::Failed(Message
,Cnf
);
188 // AcqIndex::Done - Finished a fetch /*{{{*/
189 // ---------------------------------------------------------------------
190 /* This goes through a number of states.. On the initial fetch the
191 method could possibly return an alternate filename which points
192 to the uncompressed version of the file. If this is so the file
193 is copied into the partial directory. In all other cases the file
194 is decompressed with a gzip uri. */
195 void pkgAcqIndex::Done(string Message
,unsigned long Size
,string MD5
,
196 pkgAcquire::MethodConfig
*Cfg
)
198 Item::Done(Message
,Size
,MD5
,Cfg
);
200 if (Decompression
== true)
202 // Done, move it into position
203 string FinalFile
= _config
->FindDir("Dir::State::lists");
204 FinalFile
+= URItoFileName(RealURI
);
205 Rename(DestFile
,FinalFile
);
206 chmod(FinalFile
.c_str(),0644);
208 /* We restore the original name to DestFile so that the clean operation
210 DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
211 DestFile
+= URItoFileName(RealURI
);
213 // Remove the compressed version.
215 unlink(DestFile
.c_str());
222 // Handle the unzipd case
223 string FileName
= LookupTag(Message
,"Alt-Filename");
224 if (FileName
.empty() == false)
226 // The files timestamp matches
227 if (StringToBool(LookupTag(Message
,"Alt-IMS-Hit"),false) == true)
230 Decompression
= true;
232 DestFile
+= ".decomp";
233 Desc
.URI
= "copy:" + FileName
;
239 FileName
= LookupTag(Message
,"Filename");
240 if (FileName
.empty() == true)
243 ErrorText
= "Method gave a blank filename";
246 // The files timestamp matches
247 if (StringToBool(LookupTag(Message
,"IMS-Hit"),false) == true)
250 if (FileName
== DestFile
)
255 string compExt
= Desc
.URI
.substr(Desc
.URI
.size()-3,Desc
.URI
.size()-1);
258 decompProg
= "bzip2";
259 else if(compExt
== ".gz")
262 _error
->Error("Unsupported extension: %s", compExt
.c_str());
266 Decompression
= true;
267 DestFile
+= ".decomp";
268 Desc
.URI
= string(decompProg
) + ":" + FileName
;
274 // AcqIndexRel::pkgAcqIndexRel - Constructor /*{{{*/
275 // ---------------------------------------------------------------------
276 /* The Release file is added to the queue */
277 pkgAcqIndexRel::pkgAcqIndexRel(pkgAcquire
*Owner
,
278 string URI
,string URIDesc
,string ShortDesc
) :
279 Item(Owner
), RealURI(URI
)
281 DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
282 DestFile
+= URItoFileName(URI
);
286 Desc
.Description
= URIDesc
;
287 Desc
.ShortDesc
= ShortDesc
;
293 // AcqIndexRel::Custom600Headers - Insert custom request headers /*{{{*/
294 // ---------------------------------------------------------------------
295 /* The only header we use is the last-modified header. */
296 string
pkgAcqIndexRel::Custom600Headers()
298 string Final
= _config
->FindDir("Dir::State::lists");
299 Final
+= URItoFileName(RealURI
);
302 if (stat(Final
.c_str(),&Buf
) != 0)
303 return "\nIndex-File: true";
305 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf
.st_mtime
);
308 // AcqIndexRel::Done - Item downloaded OK /*{{{*/
309 // ---------------------------------------------------------------------
310 /* The release file was not placed into the download directory then
311 a copy URI is generated and it is copied there otherwise the file
312 in the partial directory is moved into .. and the URI is finished. */
313 void pkgAcqIndexRel::Done(string Message
,unsigned long Size
,string MD5
,
314 pkgAcquire::MethodConfig
*Cfg
)
316 Item::Done(Message
,Size
,MD5
,Cfg
);
318 string FileName
= LookupTag(Message
,"Filename");
319 if (FileName
.empty() == true)
322 ErrorText
= "Method gave a blank filename";
328 // The files timestamp matches
329 if (StringToBool(LookupTag(Message
,"IMS-Hit"),false) == true)
332 // We have to copy it into place
333 if (FileName
!= DestFile
)
336 Desc
.URI
= "copy:" + FileName
;
341 // Done, move it into position
342 string FinalFile
= _config
->FindDir("Dir::State::lists");
343 FinalFile
+= URItoFileName(RealURI
);
344 Rename(DestFile
,FinalFile
);
346 chmod(FinalFile
.c_str(),0644);
349 // AcqIndexRel::Failed - Silence failure messages for missing rel files /*{{{*/
350 // ---------------------------------------------------------------------
352 void pkgAcqIndexRel::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)
354 if (Cnf
->LocalOnly
== true ||
355 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == false)
364 Item::Failed(Message
,Cnf
);
368 // AcqArchive::AcqArchive - Constructor /*{{{*/
369 // ---------------------------------------------------------------------
370 /* This just sets up the initial fetch environment and queues the first
372 pkgAcqArchive::pkgAcqArchive(pkgAcquire
*Owner
,pkgSourceList
*Sources
,
373 pkgRecords
*Recs
,pkgCache::VerIterator
const &Version
,
374 string
&StoreFilename
) :
375 Item(Owner
), Version(Version
), Sources(Sources
), Recs(Recs
),
376 StoreFilename(StoreFilename
), Vf(Version
.FileList())
378 Retries
= _config
->FindI("Acquire::Retries",0);
380 if (Version
.Arch() == 0)
382 _error
->Error(_("I wasn't able to locate a file for the %s package. "
383 "This might mean you need to manually fix this package. "
384 "(due to missing arch)"),
385 Version
.ParentPkg().Name());
389 /* We need to find a filename to determine the extension. We make the
390 assumption here that all the available sources for this version share
391 the same extension.. */
392 // Skip not source sources, they do not have file fields.
393 for (; Vf
.end() == false; Vf
++)
395 if ((Vf
.File()->Flags
& pkgCache::Flag::NotSource
) != 0)
400 // Does not really matter here.. we are going to fail out below
401 if (Vf
.end() != true)
403 // If this fails to get a file name we will bomb out below.
404 pkgRecords::Parser
&Parse
= Recs
->Lookup(Vf
);
405 if (_error
->PendingError() == true)
408 // Generate the final file name as: package_version_arch.foo
409 StoreFilename
= QuoteString(Version
.ParentPkg().Name(),"_:") + '_' +
410 QuoteString(Version
.VerStr(),"_:") + '_' +
411 QuoteString(Version
.Arch(),"_:.") +
412 "." + flExtension(Parse
.FileName());
416 if (QueueNext() == false && _error
->PendingError() == false)
417 _error
->Error(_("I wasn't able to locate file for the %s package. "
418 "This might mean you need to manually fix this package."),
419 Version
.ParentPkg().Name());
422 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
423 // ---------------------------------------------------------------------
424 /* This queues the next available file version for download. It checks if
425 the archive is already available in the cache and stashs the MD5 for
427 bool pkgAcqArchive::QueueNext()
429 for (; Vf
.end() == false; Vf
++)
431 // Ignore not source sources
432 if ((Vf
.File()->Flags
& pkgCache::Flag::NotSource
) != 0)
435 // Try to cross match against the source list
437 if (Sources
->FindIndex(Vf
.File(),Index
) == false)
440 // Grab the text package record
441 pkgRecords::Parser
&Parse
= Recs
->Lookup(Vf
);
442 if (_error
->PendingError() == true)
445 string PkgFile
= Parse
.FileName();
446 MD5
= Parse
.MD5Hash();
447 if (PkgFile
.empty() == true)
448 return _error
->Error(_("The package index files are corrupted. No Filename: "
449 "field for package %s."),
450 Version
.ParentPkg().Name());
452 // See if we already have the file. (Legacy filenames)
453 FileSize
= Version
->Size
;
454 string FinalFile
= _config
->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile
);
456 if (stat(FinalFile
.c_str(),&Buf
) == 0)
458 // Make sure the size matches
459 if ((unsigned)Buf
.st_size
== Version
->Size
)
464 StoreFilename
= DestFile
= FinalFile
;
468 /* Hmm, we have a file and its size does not match, this means it is
469 an old style mismatched arch */
470 unlink(FinalFile
.c_str());
473 // Check it again using the new style output filenames
474 FinalFile
= _config
->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename
);
475 if (stat(FinalFile
.c_str(),&Buf
) == 0)
477 // Make sure the size matches
478 if ((unsigned)Buf
.st_size
== Version
->Size
)
483 StoreFilename
= DestFile
= FinalFile
;
487 /* Hmm, we have a file and its size does not match, this shouldnt
489 unlink(FinalFile
.c_str());
492 DestFile
= _config
->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename
);
494 // Check the destination file
495 if (stat(DestFile
.c_str(),&Buf
) == 0)
497 // Hmm, the partial file is too big, erase it
498 if ((unsigned)Buf
.st_size
> Version
->Size
)
499 unlink(DestFile
.c_str());
501 PartialSize
= Buf
.st_size
;
506 Desc
.URI
= Index
->ArchiveURI(PkgFile
);
507 Desc
.Description
= Index
->ArchiveInfo(Version
);
509 Desc
.ShortDesc
= Version
.ParentPkg().Name();
518 // AcqArchive::Done - Finished fetching /*{{{*/
519 // ---------------------------------------------------------------------
521 void pkgAcqArchive::Done(string Message
,unsigned long Size
,string Md5Hash
,
522 pkgAcquire::MethodConfig
*Cfg
)
524 Item::Done(Message
,Size
,Md5Hash
,Cfg
);
527 if (Size
!= Version
->Size
)
530 ErrorText
= _("Size mismatch");
535 if (Md5Hash
.empty() == false && MD5
.empty() == false)
540 ErrorText
= _("MD5Sum mismatch");
541 Rename(DestFile
,DestFile
+ ".FAILED");
546 // Grab the output filename
547 string FileName
= LookupTag(Message
,"Filename");
548 if (FileName
.empty() == true)
551 ErrorText
= "Method gave a blank filename";
557 // Reference filename
558 if (FileName
!= DestFile
)
560 StoreFilename
= DestFile
= FileName
;
565 // Done, move it into position
566 string FinalFile
= _config
->FindDir("Dir::Cache::Archives");
567 FinalFile
+= flNotDir(StoreFilename
);
568 Rename(DestFile
,FinalFile
);
570 StoreFilename
= DestFile
= FinalFile
;
574 // AcqArchive::Failed - Failure handler /*{{{*/
575 // ---------------------------------------------------------------------
576 /* Here we try other sources */
577 void pkgAcqArchive::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)
579 ErrorText
= LookupTag(Message
,"Message");
581 /* We don't really want to retry on failed media swaps, this prevents
582 that. An interesting observation is that permanent failures are not
584 if (Cnf
->Removable
== true &&
585 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
587 // Vf = Version.FileList();
588 while (Vf
.end() == false) Vf
++;
589 StoreFilename
= string();
590 Item::Failed(Message
,Cnf
);
594 if (QueueNext() == false)
596 // This is the retry counter
598 Cnf
->LocalOnly
== false &&
599 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
602 Vf
= Version
.FileList();
603 if (QueueNext() == true)
607 StoreFilename
= string();
608 Item::Failed(Message
,Cnf
);
612 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
613 // ---------------------------------------------------------------------
615 void pkgAcqArchive::Finished()
617 if (Status
== pkgAcquire::Item::StatDone
&&
620 StoreFilename
= string();
624 // AcqFile::pkgAcqFile - Constructor /*{{{*/
625 // ---------------------------------------------------------------------
626 /* The file is added to the queue */
627 pkgAcqFile::pkgAcqFile(pkgAcquire
*Owner
,string URI
,string MD5
,
628 unsigned long Size
,string Dsc
,string ShortDesc
) :
629 Item(Owner
), Md5Hash(MD5
)
631 Retries
= _config
->FindI("Acquire::Retries",0);
633 DestFile
= flNotDir(URI
);
637 Desc
.Description
= Dsc
;
640 // Set the short description to the archive component
641 Desc
.ShortDesc
= ShortDesc
;
643 // Get the transfer sizes
646 if (stat(DestFile
.c_str(),&Buf
) == 0)
648 // Hmm, the partial file is too big, erase it
649 if ((unsigned)Buf
.st_size
> Size
)
650 unlink(DestFile
.c_str());
652 PartialSize
= Buf
.st_size
;
658 // AcqFile::Done - Item downloaded OK /*{{{*/
659 // ---------------------------------------------------------------------
661 void pkgAcqFile::Done(string Message
,unsigned long Size
,string MD5
,
662 pkgAcquire::MethodConfig
*Cnf
)
665 if (Md5Hash
.empty() == false && MD5
.empty() == false)
670 ErrorText
= "MD5Sum mismatch";
671 Rename(DestFile
,DestFile
+ ".FAILED");
676 Item::Done(Message
,Size
,MD5
,Cnf
);
678 string FileName
= LookupTag(Message
,"Filename");
679 if (FileName
.empty() == true)
682 ErrorText
= "Method gave a blank filename";
688 // The files timestamp matches
689 if (StringToBool(LookupTag(Message
,"IMS-Hit"),false) == true)
692 // We have to copy it into place
693 if (FileName
!= DestFile
)
696 if (_config
->FindB("Acquire::Source-Symlinks",true) == false ||
697 Cnf
->Removable
== true)
699 Desc
.URI
= "copy:" + FileName
;
704 // Erase the file if it is a symlink so we can overwrite it
706 if (lstat(DestFile
.c_str(),&St
) == 0)
708 if (S_ISLNK(St
.st_mode
) != 0)
709 unlink(DestFile
.c_str());
713 if (symlink(FileName
.c_str(),DestFile
.c_str()) != 0)
715 ErrorText
= "Link to " + DestFile
+ " failure ";
722 // AcqFile::Failed - Failure handler /*{{{*/
723 // ---------------------------------------------------------------------
724 /* Here we try other sources */
725 void pkgAcqFile::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)
727 ErrorText
= LookupTag(Message
,"Message");
729 // This is the retry counter
731 Cnf
->LocalOnly
== false &&
732 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
739 Item::Failed(Message
,Cnf
);