]>
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 Desc
.URI
= URI
+ ".gz";
148 Desc
.Description
= URIDesc
;
150 Desc
.ShortDesc
= ShortDesc
;
155 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
156 // ---------------------------------------------------------------------
157 /* The only header we use is the last-modified header. */
158 string
pkgAcqIndex::Custom600Headers()
160 string Final
= _config
->FindDir("Dir::State::lists");
161 Final
+= URItoFileName(RealURI
);
164 if (stat(Final
.c_str(),&Buf
) != 0)
165 return "\nIndex-File: true";
167 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf
.st_mtime
);
170 // AcqIndex::Done - Finished a fetch /*{{{*/
171 // ---------------------------------------------------------------------
172 /* This goes through a number of states.. On the initial fetch the
173 method could possibly return an alternate filename which points
174 to the uncompressed version of the file. If this is so the file
175 is copied into the partial directory. In all other cases the file
176 is decompressed with a gzip uri. */
177 void pkgAcqIndex::Done(string Message
,unsigned long Size
,string MD5
,
178 pkgAcquire::MethodConfig
*Cfg
)
180 Item::Done(Message
,Size
,MD5
,Cfg
);
182 if (Decompression
== true)
184 // Done, move it into position
185 string FinalFile
= _config
->FindDir("Dir::State::lists");
186 FinalFile
+= URItoFileName(RealURI
);
187 Rename(DestFile
,FinalFile
);
188 chmod(FinalFile
.c_str(),0644);
190 /* We restore the original name to DestFile so that the clean operation
192 DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
193 DestFile
+= URItoFileName(RealURI
);
195 // Remove the compressed version.
197 unlink(DestFile
.c_str());
204 // Handle the unzipd case
205 string FileName
= LookupTag(Message
,"Alt-Filename");
206 if (FileName
.empty() == false)
208 // The files timestamp matches
209 if (StringToBool(LookupTag(Message
,"Alt-IMS-Hit"),false) == true)
212 Decompression
= true;
214 DestFile
+= ".decomp";
215 Desc
.URI
= "copy:" + FileName
;
221 FileName
= LookupTag(Message
,"Filename");
222 if (FileName
.empty() == true)
225 ErrorText
= "Method gave a blank filename";
228 // The files timestamp matches
229 if (StringToBool(LookupTag(Message
,"IMS-Hit"),false) == true)
232 if (FileName
== DestFile
)
237 Decompression
= true;
238 DestFile
+= ".decomp";
239 Desc
.URI
= "gzip:" + FileName
;
245 // AcqIndexRel::pkgAcqIndexRel - Constructor /*{{{*/
246 // ---------------------------------------------------------------------
247 /* The Release file is added to the queue */
248 pkgAcqIndexRel::pkgAcqIndexRel(pkgAcquire
*Owner
,
249 string URI
,string URIDesc
,string ShortDesc
) :
250 Item(Owner
), RealURI(URI
)
252 DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
253 DestFile
+= URItoFileName(URI
);
257 Desc
.Description
= URIDesc
;
258 Desc
.ShortDesc
= ShortDesc
;
264 // AcqIndexRel::Custom600Headers - Insert custom request headers /*{{{*/
265 // ---------------------------------------------------------------------
266 /* The only header we use is the last-modified header. */
267 string
pkgAcqIndexRel::Custom600Headers()
269 string Final
= _config
->FindDir("Dir::State::lists");
270 Final
+= URItoFileName(RealURI
);
273 if (stat(Final
.c_str(),&Buf
) != 0)
274 return "\nIndex-File: true";
276 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf
.st_mtime
);
279 // AcqIndexRel::Done - Item downloaded OK /*{{{*/
280 // ---------------------------------------------------------------------
281 /* The release file was not placed into the download directory then
282 a copy URI is generated and it is copied there otherwise the file
283 in the partial directory is moved into .. and the URI is finished. */
284 void pkgAcqIndexRel::Done(string Message
,unsigned long Size
,string MD5
,
285 pkgAcquire::MethodConfig
*Cfg
)
287 Item::Done(Message
,Size
,MD5
,Cfg
);
289 string FileName
= LookupTag(Message
,"Filename");
290 if (FileName
.empty() == true)
293 ErrorText
= "Method gave a blank filename";
299 // The files timestamp matches
300 if (StringToBool(LookupTag(Message
,"IMS-Hit"),false) == true)
303 // We have to copy it into place
304 if (FileName
!= DestFile
)
307 Desc
.URI
= "copy:" + FileName
;
312 // Done, move it into position
313 string FinalFile
= _config
->FindDir("Dir::State::lists");
314 FinalFile
+= URItoFileName(RealURI
);
315 Rename(DestFile
,FinalFile
);
317 chmod(FinalFile
.c_str(),0644);
320 // AcqIndexRel::Failed - Silence failure messages for missing rel files /*{{{*/
321 // ---------------------------------------------------------------------
323 void pkgAcqIndexRel::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)
325 if (Cnf
->LocalOnly
== true ||
326 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == false)
335 Item::Failed(Message
,Cnf
);
339 // AcqArchive::AcqArchive - Constructor /*{{{*/
340 // ---------------------------------------------------------------------
341 /* This just sets up the initial fetch environment and queues the first
343 pkgAcqArchive::pkgAcqArchive(pkgAcquire
*Owner
,pkgSourceList
*Sources
,
344 pkgRecords
*Recs
,pkgCache::VerIterator
const &Version
,
345 string
&StoreFilename
) :
346 Item(Owner
), Version(Version
), Sources(Sources
), Recs(Recs
),
347 StoreFilename(StoreFilename
), Vf(Version
.FileList())
349 Retries
= _config
->FindI("Acquire::Retries",0);
351 if (Version
.Arch() == 0)
353 _error
->Error(_("I wasn't able to locate a file for the %s package. "
354 "This might mean you need to manually fix this package. "
355 "(due to missing arch)"),
356 Version
.ParentPkg().Name());
360 /* We need to find a filename to determine the extension. We make the
361 assumption here that all the available sources for this version share
362 the same extension.. */
363 // Skip not source sources, they do not have file fields.
364 for (; Vf
.end() == false; Vf
++)
366 if ((Vf
.File()->Flags
& pkgCache::Flag::NotSource
) != 0)
371 // Does not really matter here.. we are going to fail out below
372 if (Vf
.end() != true)
374 // If this fails to get a file name we will bomb out below.
375 pkgRecords::Parser
&Parse
= Recs
->Lookup(Vf
);
376 if (_error
->PendingError() == true)
379 // Generate the final file name as: package_version_arch.foo
380 StoreFilename
= QuoteString(Version
.ParentPkg().Name(),"_:") + '_' +
381 QuoteString(Version
.VerStr(),"_:") + '_' +
382 QuoteString(Version
.Arch(),"_:.") +
383 "." + flExtension(Parse
.FileName());
387 if (QueueNext() == false && _error
->PendingError() == false)
388 _error
->Error(_("I wasn't able to locate file for the %s package. "
389 "This might mean you need to manually fix this package."),
390 Version
.ParentPkg().Name());
393 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
394 // ---------------------------------------------------------------------
395 /* This queues the next available file version for download. It checks if
396 the archive is already available in the cache and stashs the MD5 for
398 bool pkgAcqArchive::QueueNext()
400 for (; Vf
.end() == false; Vf
++)
402 // Ignore not source sources
403 if ((Vf
.File()->Flags
& pkgCache::Flag::NotSource
) != 0)
406 // Try to cross match against the source list
408 if (Sources
->FindIndex(Vf
.File(),Index
) == false)
411 // Grab the text package record
412 pkgRecords::Parser
&Parse
= Recs
->Lookup(Vf
);
413 if (_error
->PendingError() == true)
416 string PkgFile
= Parse
.FileName();
417 MD5
= Parse
.MD5Hash();
418 if (PkgFile
.empty() == true)
419 return _error
->Error(_("The package index files are corrupted. No Filename: "
420 "field for package %s."),
421 Version
.ParentPkg().Name());
423 // See if we already have the file. (Legacy filenames)
424 FileSize
= Version
->Size
;
425 string FinalFile
= _config
->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile
);
427 if (stat(FinalFile
.c_str(),&Buf
) == 0)
429 // Make sure the size matches
430 if ((unsigned)Buf
.st_size
== Version
->Size
)
435 StoreFilename
= DestFile
= FinalFile
;
439 /* Hmm, we have a file and its size does not match, this means it is
440 an old style mismatched arch */
441 unlink(FinalFile
.c_str());
444 // Check it again using the new style output filenames
445 FinalFile
= _config
->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename
);
446 if (stat(FinalFile
.c_str(),&Buf
) == 0)
448 // Make sure the size matches
449 if ((unsigned)Buf
.st_size
== Version
->Size
)
454 StoreFilename
= DestFile
= FinalFile
;
458 /* Hmm, we have a file and its size does not match, this shouldnt
460 unlink(FinalFile
.c_str());
463 DestFile
= _config
->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename
);
465 // Check the destination file
466 if (stat(DestFile
.c_str(),&Buf
) == 0)
468 // Hmm, the partial file is too big, erase it
469 if ((unsigned)Buf
.st_size
> Version
->Size
)
470 unlink(DestFile
.c_str());
472 PartialSize
= Buf
.st_size
;
477 Desc
.URI
= Index
->ArchiveURI(PkgFile
);
478 Desc
.Description
= Index
->ArchiveInfo(Version
);
480 Desc
.ShortDesc
= Version
.ParentPkg().Name();
489 // AcqArchive::Done - Finished fetching /*{{{*/
490 // ---------------------------------------------------------------------
492 void pkgAcqArchive::Done(string Message
,unsigned long Size
,string Md5Hash
,
493 pkgAcquire::MethodConfig
*Cfg
)
495 Item::Done(Message
,Size
,Md5Hash
,Cfg
);
498 if (Size
!= Version
->Size
)
501 ErrorText
= _("Size mismatch");
506 if (Md5Hash
.empty() == false && MD5
.empty() == false)
511 ErrorText
= _("MD5Sum mismatch");
512 Rename(DestFile
,DestFile
+ ".FAILED");
517 // Grab the output filename
518 string FileName
= LookupTag(Message
,"Filename");
519 if (FileName
.empty() == true)
522 ErrorText
= "Method gave a blank filename";
528 // Reference filename
529 if (FileName
!= DestFile
)
531 StoreFilename
= DestFile
= FileName
;
536 // Done, move it into position
537 string FinalFile
= _config
->FindDir("Dir::Cache::Archives");
538 FinalFile
+= flNotDir(StoreFilename
);
539 Rename(DestFile
,FinalFile
);
541 StoreFilename
= DestFile
= FinalFile
;
545 // AcqArchive::Failed - Failure handler /*{{{*/
546 // ---------------------------------------------------------------------
547 /* Here we try other sources */
548 void pkgAcqArchive::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)
550 ErrorText
= LookupTag(Message
,"Message");
552 /* We don't really want to retry on failed media swaps, this prevents
553 that. An interesting observation is that permanent failures are not
555 if (Cnf
->Removable
== true &&
556 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
558 // Vf = Version.FileList();
559 while (Vf
.end() == false) Vf
++;
560 StoreFilename
= string();
561 Item::Failed(Message
,Cnf
);
565 if (QueueNext() == false)
567 // This is the retry counter
569 Cnf
->LocalOnly
== false &&
570 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
573 Vf
= Version
.FileList();
574 if (QueueNext() == true)
578 StoreFilename
= string();
579 Item::Failed(Message
,Cnf
);
583 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
584 // ---------------------------------------------------------------------
586 void pkgAcqArchive::Finished()
588 if (Status
== pkgAcquire::Item::StatDone
&&
591 StoreFilename
= string();
595 // AcqFile::pkgAcqFile - Constructor /*{{{*/
596 // ---------------------------------------------------------------------
597 /* The file is added to the queue */
598 pkgAcqFile::pkgAcqFile(pkgAcquire
*Owner
,string URI
,string MD5
,
599 unsigned long Size
,string Dsc
,string ShortDesc
) :
600 Item(Owner
), Md5Hash(MD5
)
602 Retries
= _config
->FindI("Acquire::Retries",0);
604 DestFile
= flNotDir(URI
);
608 Desc
.Description
= Dsc
;
611 // Set the short description to the archive component
612 Desc
.ShortDesc
= ShortDesc
;
614 // Get the transfer sizes
617 if (stat(DestFile
.c_str(),&Buf
) == 0)
619 // Hmm, the partial file is too big, erase it
620 if ((unsigned)Buf
.st_size
> Size
)
621 unlink(DestFile
.c_str());
623 PartialSize
= Buf
.st_size
;
629 // AcqFile::Done - Item downloaded OK /*{{{*/
630 // ---------------------------------------------------------------------
632 void pkgAcqFile::Done(string Message
,unsigned long Size
,string MD5
,
633 pkgAcquire::MethodConfig
*Cnf
)
636 if (Md5Hash
.empty() == false && MD5
.empty() == false)
641 ErrorText
= "MD5Sum mismatch";
642 Rename(DestFile
,DestFile
+ ".FAILED");
647 Item::Done(Message
,Size
,MD5
,Cnf
);
649 string FileName
= LookupTag(Message
,"Filename");
650 if (FileName
.empty() == true)
653 ErrorText
= "Method gave a blank filename";
659 // The files timestamp matches
660 if (StringToBool(LookupTag(Message
,"IMS-Hit"),false) == true)
663 // We have to copy it into place
664 if (FileName
!= DestFile
)
667 if (_config
->FindB("Acquire::Source-Symlinks",true) == false ||
668 Cnf
->Removable
== true)
670 Desc
.URI
= "copy:" + FileName
;
675 // Erase the file if it is a symlink so we can overwrite it
677 if (lstat(DestFile
.c_str(),&St
) == 0)
679 if (S_ISLNK(St
.st_mode
) != 0)
680 unlink(DestFile
.c_str());
684 if (symlink(FileName
.c_str(),DestFile
.c_str()) != 0)
686 ErrorText
= "Link to " + DestFile
+ " failure ";
693 // AcqFile::Failed - Failure handler /*{{{*/
694 // ---------------------------------------------------------------------
695 /* Here we try other sources */
696 void pkgAcqFile::Failed(string Message
,pkgAcquire::MethodConfig
*Cnf
)
698 ErrorText
= LookupTag(Message
,"Message");
700 // This is the retry counter
702 Cnf
->LocalOnly
== false &&
703 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
710 Item::Failed(Message
,Cnf
);