]> git.saurik.com Git - apt.git/blame - apt-pkg/acquire-item.cc
Fixed list notation doc bug and 'b' vs 'B'
[apt.git] / apt-pkg / acquire-item.cc
CommitLineData
0118833a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
9978c7b0 3// $Id: acquire-item.cc,v 1.31 1999/06/05 04:33:29 jgg Exp $
0118833a
AL
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.
b185acc2 12
0118833a
AL
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>
03e39e59 21#include <apt-pkg/error.h>
cdcc6d34 22#include <apt-pkg/strutl.h>
36375005 23#include <apt-pkg/fileutl.h>
0a8a80e5
AL
24
25#include <sys/stat.h>
26#include <unistd.h>
c88edf1d
AL
27#include <errno.h>
28#include <string.h>
29#include <stdio.h>
0118833a
AL
30 /*}}}*/
31
32// Acquire::Item::Item - Constructor /*{{{*/
33// ---------------------------------------------------------------------
34/* */
8267fe24 35pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
6b1ff003
AL
36 PartialSize(0), Mode(0), ID(0), Complete(false),
37 Local(false), QueueCounter(0)
0118833a
AL
38{
39 Owner->Add(this);
c88edf1d 40 Status = StatIdle;
0118833a
AL
41}
42 /*}}}*/
43// Acquire::Item::~Item - Destructor /*{{{*/
44// ---------------------------------------------------------------------
45/* */
46pkgAcquire::Item::~Item()
47{
48 Owner->Remove(this);
49}
50 /*}}}*/
c88edf1d
AL
51// Acquire::Item::Failed - Item failed to download /*{{{*/
52// ---------------------------------------------------------------------
93bf083d
AL
53/* We return to an idle state if there are still other queues that could
54 fetch this object */
7d8afa39 55void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
c88edf1d 56{
93bf083d 57 Status = StatIdle;
db890fdb 58 ErrorText = LookupTag(Message,"Message");
c88edf1d 59 if (QueueCounter <= 1)
93bf083d 60 {
a72ace20 61 /* This indicates that the file is not available right now but might
7d8afa39 62 be sometime later. If we do a retry cycle then this should be
17caf1b1 63 retried [CDROMs] */
7d8afa39
AL
64 if (Cnf->LocalOnly == true &&
65 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
a72ace20
AL
66 {
67 Status = StatIdle;
681d76d0 68 Dequeue();
a72ace20
AL
69 return;
70 }
71
93bf083d 72 Status = StatError;
681d76d0 73 Dequeue();
93bf083d 74 }
c88edf1d
AL
75}
76 /*}}}*/
8267fe24
AL
77// Acquire::Item::Start - Item has begun to download /*{{{*/
78// ---------------------------------------------------------------------
17caf1b1
AL
79/* Stash status and the file size. Note that setting Complete means
80 sub-phases of the acquire process such as decompresion are operating */
8267fe24
AL
81void pkgAcquire::Item::Start(string Message,unsigned long Size)
82{
83 Status = StatFetching;
84 if (FileSize == 0 && Complete == false)
85 FileSize = Size;
86}
87 /*}}}*/
c88edf1d
AL
88// Acquire::Item::Done - Item downloaded OK /*{{{*/
89// ---------------------------------------------------------------------
90/* */
b98f2859 91void pkgAcquire::Item::Done(string Message,unsigned long Size,string)
c88edf1d 92{
b98f2859
AL
93 // We just downloaded something..
94 string FileName = LookupTag(Message,"Filename");
95 if (Complete == false && FileName == DestFile)
96 {
97 if (Owner->Log != 0)
98 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
99 }
100
c88edf1d
AL
101 Status = StatDone;
102 ErrorText = string();
103 Owner->Dequeue(this);
104}
105 /*}}}*/
8b89e57f
AL
106// Acquire::Item::Rename - Rename a file /*{{{*/
107// ---------------------------------------------------------------------
108/* This helper function is used by alot of item methods as thier final
109 step */
110void pkgAcquire::Item::Rename(string From,string To)
111{
112 if (rename(From.c_str(),To.c_str()) != 0)
113 {
114 char S[300];
115 sprintf(S,"rename failed, %s (%s -> %s).",strerror(errno),
116 From.c_str(),To.c_str());
117 Status = StatError;
118 ErrorText = S;
119 }
120}
121 /*}}}*/
0118833a
AL
122
123// AcqIndex::AcqIndex - Constructor /*{{{*/
124// ---------------------------------------------------------------------
125/* The package file is added to the queue and a second class is
126 instantiated to fetch the revision file */
127pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,const pkgSourceList::Item *Location) :
128 Item(Owner), Location(Location)
129{
8b89e57f 130 Decompression = false;
bfd22fc0 131 Erase = false;
8b89e57f 132
0a8a80e5
AL
133 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
134 DestFile += URItoFileName(Location->PackagesURI());
8267fe24
AL
135
136 // Create the item
137 Desc.URI = Location->PackagesURI() + ".gz";
138 Desc.Description = Location->PackagesInfo();
139 Desc.Owner = this;
140
141 // Set the short description to the archive component
142 if (Location->Dist[Location->Dist.size() - 1] == '/')
143 Desc.ShortDesc = Location->Dist;
144 else
145 Desc.ShortDesc = Location->Dist + '/' + Location->Section;
146
147 QueueURI(Desc);
0118833a 148
0a8a80e5 149 // Create the Release fetch class
0118833a
AL
150 new pkgAcqIndexRel(Owner,Location);
151}
152 /*}}}*/
0a8a80e5 153// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 154// ---------------------------------------------------------------------
0a8a80e5
AL
155/* The only header we use is the last-modified header. */
156string pkgAcqIndex::Custom600Headers()
0118833a 157{
0a8a80e5
AL
158 string Final = _config->FindDir("Dir::State::lists");
159 Final += URItoFileName(Location->PackagesURI());
160
161 struct stat Buf;
162 if (stat(Final.c_str(),&Buf) != 0)
a72ace20 163 return "\nIndex-File: true";
0118833a 164
a72ace20 165 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
0118833a
AL
166}
167 /*}}}*/
8b89e57f
AL
168// AcqIndex::Done - Finished a fetch /*{{{*/
169// ---------------------------------------------------------------------
170/* This goes through a number of states.. On the initial fetch the
171 method could possibly return an alternate filename which points
172 to the uncompressed version of the file. If this is so the file
173 is copied into the partial directory. In all other cases the file
174 is decompressed with a gzip uri. */
175void pkgAcqIndex::Done(string Message,unsigned long Size,string MD5)
176{
177 Item::Done(Message,Size,MD5);
178
179 if (Decompression == true)
180 {
181 // Done, move it into position
182 string FinalFile = _config->FindDir("Dir::State::lists");
183 FinalFile += URItoFileName(Location->PackagesURI());
184 Rename(DestFile,FinalFile);
bfd22fc0 185
7a7fa5f0
AL
186 /* We restore the original name to DestFile so that the clean operation
187 will work OK */
188 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
189 DestFile += URItoFileName(Location->PackagesURI());
190
bfd22fc0
AL
191 // Remove the compressed version.
192 if (Erase == true)
bfd22fc0 193 unlink(DestFile.c_str());
8b89e57f
AL
194 return;
195 }
bfd22fc0
AL
196
197 Erase = false;
8267fe24 198 Complete = true;
bfd22fc0 199
8b89e57f
AL
200 // Handle the unzipd case
201 string FileName = LookupTag(Message,"Alt-Filename");
202 if (FileName.empty() == false)
203 {
204 // The files timestamp matches
205 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
206 return;
207
208 Decompression = true;
a6568219 209 Local = true;
8b89e57f 210 DestFile += ".decomp";
8267fe24
AL
211 Desc.URI = "copy:" + FileName;
212 QueueURI(Desc);
b98f2859 213 Mode = "copy";
8b89e57f
AL
214 return;
215 }
216
217 FileName = LookupTag(Message,"Filename");
218 if (FileName.empty() == true)
219 {
220 Status = StatError;
221 ErrorText = "Method gave a blank filename";
222 }
223
224 // The files timestamp matches
225 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
226 return;
bfd22fc0
AL
227
228 if (FileName == DestFile)
229 Erase = true;
8267fe24 230 else
a6568219 231 Local = true;
8b89e57f
AL
232
233 Decompression = true;
234 DestFile += ".decomp";
8267fe24
AL
235 Desc.URI = "gzip:" + FileName,Location->PackagesInfo();
236 QueueURI(Desc);
b98f2859 237 Mode = "gzip";
8b89e57f
AL
238}
239 /*}}}*/
240
0118833a
AL
241// AcqIndexRel::pkgAcqIndexRel - Constructor /*{{{*/
242// ---------------------------------------------------------------------
243/* The Release file is added to the queue */
244pkgAcqIndexRel::pkgAcqIndexRel(pkgAcquire *Owner,
245 const pkgSourceList::Item *Location) :
246 Item(Owner), Location(Location)
247{
0a8a80e5
AL
248 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
249 DestFile += URItoFileName(Location->ReleaseURI());
250
8267fe24
AL
251 // Create the item
252 Desc.URI = Location->ReleaseURI();
253 Desc.Description = Location->ReleaseInfo();
254 Desc.Owner = this;
255
256 // Set the short description to the archive component
257 if (Location->Dist[Location->Dist.size() - 1] == '/')
258 Desc.ShortDesc = Location->Dist;
259 else
260 Desc.ShortDesc = Location->Dist + '/' + Location->Section;
261
262 QueueURI(Desc);
0118833a
AL
263}
264 /*}}}*/
0a8a80e5 265// AcqIndexRel::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 266// ---------------------------------------------------------------------
0a8a80e5
AL
267/* The only header we use is the last-modified header. */
268string pkgAcqIndexRel::Custom600Headers()
0118833a 269{
0a8a80e5
AL
270 string Final = _config->FindDir("Dir::State::lists");
271 Final += URItoFileName(Location->ReleaseURI());
272
273 struct stat Buf;
274 if (stat(Final.c_str(),&Buf) != 0)
a72ace20 275 return "\nIndex-File: true";
0118833a 276
a72ace20 277 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
0118833a
AL
278}
279 /*}}}*/
c88edf1d
AL
280// AcqIndexRel::Done - Item downloaded OK /*{{{*/
281// ---------------------------------------------------------------------
282/* The release file was not placed into the download directory then
283 a copy URI is generated and it is copied there otherwise the file
284 in the partial directory is moved into .. and the URI is finished. */
285void pkgAcqIndexRel::Done(string Message,unsigned long Size,string MD5)
286{
287 Item::Done(Message,Size,MD5);
288
289 string FileName = LookupTag(Message,"Filename");
290 if (FileName.empty() == true)
291 {
292 Status = StatError;
293 ErrorText = "Method gave a blank filename";
8b89e57f 294 return;
c88edf1d 295 }
8b89e57f 296
8267fe24
AL
297 Complete = true;
298
8b89e57f
AL
299 // The files timestamp matches
300 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
301 return;
c88edf1d
AL
302
303 // We have to copy it into place
304 if (FileName != DestFile)
305 {
a6568219 306 Local = true;
8267fe24
AL
307 Desc.URI = "copy:" + FileName;
308 QueueURI(Desc);
c88edf1d
AL
309 return;
310 }
311
312 // Done, move it into position
313 string FinalFile = _config->FindDir("Dir::State::lists");
314 FinalFile += URItoFileName(Location->ReleaseURI());
8b89e57f 315 Rename(DestFile,FinalFile);
c88edf1d
AL
316}
317 /*}}}*/
681d76d0
AL
318// AcqIndexRel::Failed - Silence failure messages for missing rel files /*{{{*/
319// ---------------------------------------------------------------------
320/* */
321void pkgAcqIndexRel::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
322{
323 // This is the retry counter
324 if (Cnf->LocalOnly == true ||
325 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
326 {
2b154e53
AL
327 // Ignore this
328 Status = StatDone;
329 Complete = false;
681d76d0
AL
330 Dequeue();
331 return;
332 }
333
334 Item::Failed(Message,Cnf);
335}
336 /*}}}*/
03e39e59
AL
337
338// AcqArchive::AcqArchive - Constructor /*{{{*/
339// ---------------------------------------------------------------------
17caf1b1
AL
340/* This just sets up the initial fetch environment and queues the first
341 possibilitiy */
03e39e59 342pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
30e1eab5
AL
343 pkgRecords *Recs,pkgCache::VerIterator const &Version,
344 string &StoreFilename) :
345 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
b185acc2 346 StoreFilename(StoreFilename), Vf(Version.FileList())
03e39e59 347{
7d8afa39 348 Retries = _config->FindI("Acquire::Retries",0);
813c8eea
AL
349
350 if (Version.Arch() == 0)
351 _error->Error("I wasn't able to locate file for the %s package. "
352 "This might mean you need to manually fix this package. (due to missing arch)",
353 Version.ParentPkg().Name());
354
17caf1b1
AL
355 // Generate the final file name as: package_version_arch.deb
356 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
357 QuoteString(Version.VerStr(),"_:") + '_' +
1bc849af 358 QuoteString(Version.Arch(),"_:.") + ".deb";
17caf1b1 359
03e39e59 360 // Select a source
b185acc2
AL
361 if (QueueNext() == false && _error->PendingError() == false)
362 _error->Error("I wasn't able to locate file for the %s package. "
363 "This might mean you need to manually fix this package.",
364 Version.ParentPkg().Name());
365}
366 /*}}}*/
367// AcqArchive::QueueNext - Queue the next file source /*{{{*/
368// ---------------------------------------------------------------------
17caf1b1
AL
369/* This queues the next available file version for download. It checks if
370 the archive is already available in the cache and stashs the MD5 for
371 checking later. */
b185acc2
AL
372bool pkgAcqArchive::QueueNext()
373{
03e39e59
AL
374 for (; Vf.end() == false; Vf++)
375 {
376 // Ignore not source sources
377 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
378 continue;
379
380 // Try to cross match against the source list
381 string PkgFile = flNotDir(Vf.File().FileName());
382 pkgSourceList::const_iterator Location;
383 for (Location = Sources->begin(); Location != Sources->end(); Location++)
384 if (PkgFile == URItoFileName(Location->PackagesURI()))
385 break;
386
387 if (Location == Sources->end())
388 continue;
389
390 // Grab the text package record
391 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
392 if (_error->PendingError() == true)
b185acc2 393 return false;
03e39e59
AL
394
395 PkgFile = Parse.FileName();
396 MD5 = Parse.MD5Hash();
397 if (PkgFile.empty() == true)
b185acc2
AL
398 return _error->Error("The package index files are corrupted. No Filename: "
399 "field for package %s."
400 ,Version.ParentPkg().Name());
a6568219 401
17caf1b1 402 // See if we already have the file. (Legacy filenames)
a6568219
AL
403 FileSize = Version->Size;
404 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
405 struct stat Buf;
406 if (stat(FinalFile.c_str(),&Buf) == 0)
407 {
408 // Make sure the size matches
409 if ((unsigned)Buf.st_size == Version->Size)
410 {
411 Complete = true;
412 Local = true;
413 Status = StatDone;
30e1eab5 414 StoreFilename = DestFile = FinalFile;
b185acc2 415 return true;
a6568219
AL
416 }
417
6b1ff003
AL
418 /* Hmm, we have a file and its size does not match, this means it is
419 an old style mismatched arch */
a6568219
AL
420 unlink(FinalFile.c_str());
421 }
17caf1b1
AL
422
423 // Check it again using the new style output filenames
424 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
425 if (stat(FinalFile.c_str(),&Buf) == 0)
426 {
427 // Make sure the size matches
428 if ((unsigned)Buf.st_size == Version->Size)
429 {
430 Complete = true;
431 Local = true;
432 Status = StatDone;
433 StoreFilename = DestFile = FinalFile;
434 return true;
435 }
436
437 /* Hmm, we have a file and its size does not match, this shouldnt
438 happen.. */
439 unlink(FinalFile.c_str());
440 }
441
442 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
6b1ff003
AL
443
444 // Check the destination file
445 if (stat(DestFile.c_str(),&Buf) == 0)
446 {
447 // Hmm, the partial file is too big, erase it
448 if ((unsigned)Buf.st_size > Version->Size)
449 unlink(DestFile.c_str());
450 else
451 PartialSize = Buf.st_size;
452 }
453
03e39e59
AL
454 // Create the item
455 Desc.URI = Location->ArchiveURI(PkgFile);
456 Desc.Description = Location->ArchiveInfo(Version);
457 Desc.Owner = this;
458 Desc.ShortDesc = Version.ParentPkg().Name();
459 QueueURI(Desc);
b185acc2
AL
460
461 Vf++;
462 return true;
03e39e59 463 }
b185acc2
AL
464 return false;
465}
03e39e59
AL
466 /*}}}*/
467// AcqArchive::Done - Finished fetching /*{{{*/
468// ---------------------------------------------------------------------
469/* */
470void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash)
471{
fd9bd3dc 472 Item::Done(Message,Size,Md5Hash);
03e39e59
AL
473
474 // Check the size
475 if (Size != Version->Size)
476 {
477 _error->Error("Size mismatch for package %s",Version.ParentPkg().Name());
478 return;
479 }
480
481 // Check the md5
482 if (Md5Hash.empty() == false && MD5.empty() == false)
483 {
484 if (Md5Hash != MD5)
485 {
486 _error->Error("MD5Sum mismatch for package %s",Version.ParentPkg().Name());
9978c7b0 487 Rename(DestFile,DestFile + ".FAILED");
03e39e59
AL
488 return;
489 }
490 }
a6568219
AL
491
492 // Grab the output filename
03e39e59
AL
493 string FileName = LookupTag(Message,"Filename");
494 if (FileName.empty() == true)
495 {
496 Status = StatError;
497 ErrorText = "Method gave a blank filename";
498 return;
499 }
a6568219
AL
500
501 Complete = true;
30e1eab5
AL
502
503 // Reference filename
a6568219
AL
504 if (FileName != DestFile)
505 {
30e1eab5 506 StoreFilename = DestFile = FileName;
a6568219
AL
507 Local = true;
508 return;
509 }
510
511 // Done, move it into position
512 string FinalFile = _config->FindDir("Dir::Cache::Archives");
17caf1b1 513 FinalFile += flNotDir(StoreFilename);
a6568219 514 Rename(DestFile,FinalFile);
03e39e59 515
30e1eab5 516 StoreFilename = DestFile = FinalFile;
03e39e59
AL
517 Complete = true;
518}
519 /*}}}*/
db890fdb
AL
520// AcqArchive::Failed - Failure handler /*{{{*/
521// ---------------------------------------------------------------------
522/* Here we try other sources */
7d8afa39 523void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
db890fdb
AL
524{
525 ErrorText = LookupTag(Message,"Message");
526 if (QueueNext() == false)
7d8afa39
AL
527 {
528 // This is the retry counter
529 if (Retries != 0 &&
530 Cnf->LocalOnly == false &&
531 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
532 {
533 Retries--;
534 Vf = Version.FileList();
535 if (QueueNext() == true)
536 return;
537 }
538
9dbb421f 539 StoreFilename = string();
7d8afa39
AL
540 Item::Failed(Message,Cnf);
541 }
db890fdb
AL
542}
543 /*}}}*/
ab559b35
AL
544// AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
545// ---------------------------------------------------------------------
546/* */
547void pkgAcqArchive::Finished()
548{
549 if (Status == pkgAcquire::Item::StatDone &&
550 Complete == true)
551 return;
552 StoreFilename = string();
553}
554 /*}}}*/
36375005
AL
555
556// AcqFile::pkgAcqFile - Constructor /*{{{*/
557// ---------------------------------------------------------------------
558/* The file is added to the queue */
559pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string MD5,
560 unsigned long Size,string Dsc,string ShortDesc) :
561 Item(Owner), MD5(MD5)
562{
563 DestFile = flNotDir(URI);
564
565 // Create the item
566 Desc.URI = URI;
567 Desc.Description = Dsc;
568 Desc.Owner = this;
569
570 // Set the short description to the archive component
571 Desc.ShortDesc = ShortDesc;
572
573 // Get the transfer sizes
574 FileSize = Size;
575 struct stat Buf;
576 if (stat(DestFile.c_str(),&Buf) == 0)
577 {
578 // Hmm, the partial file is too big, erase it
579 if ((unsigned)Buf.st_size > Size)
580 unlink(DestFile.c_str());
581 else
582 PartialSize = Buf.st_size;
583 }
584
585 QueueURI(Desc);
586}
587 /*}}}*/
588// AcqFile::Done - Item downloaded OK /*{{{*/
589// ---------------------------------------------------------------------
590/* */
591void pkgAcqFile::Done(string Message,unsigned long Size,string MD5)
592{
593 Item::Done(Message,Size,MD5);
594
595 string FileName = LookupTag(Message,"Filename");
596 if (FileName.empty() == true)
597 {
598 Status = StatError;
599 ErrorText = "Method gave a blank filename";
600 return;
601 }
602
603 Complete = true;
604
605 // The files timestamp matches
606 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
607 return;
608
609 // We have to copy it into place
610 if (FileName != DestFile)
611 {
612 Local = true;
613 Desc.URI = "copy:" + FileName;
614 QueueURI(Desc);
615 return;
616 }
617}
618 /*}}}*/