]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
Back out fix for now; not sure I want to bump the lib v...
[apt.git] / apt-pkg / acquire-item.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-item.cc,v 1.46 2003/02/02 22:19:17 jgg Exp $
4 /* ######################################################################
5
6 Acquire Item - Item to acquire
7
8 Each item can download to exactly one file at a time. This means you
9 cannot create an item that fetches two uri's to two files at the same
10 time. The pkgAcqIndex class creates a second class upon instantiation
11 to fetch the other index files because of this.
12
13 ##################################################################### */
14 /*}}}*/
15 // Include Files /*{{{*/
16 #ifdef __GNUG__
17 #pragma implementation "apt-pkg/acquire-item.h"
18 #endif
19 #include <apt-pkg/acquire-item.h>
20 #include <apt-pkg/configuration.h>
21 #include <apt-pkg/sourcelist.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/strutl.h>
24 #include <apt-pkg/fileutl.h>
25
26 #include <apti18n.h>
27
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <string>
32 #include <stdio.h>
33 /*}}}*/
34
35 using std::string;
36
37 // Acquire::Item::Item - Constructor /*{{{*/
38 // ---------------------------------------------------------------------
39 /* */
40 pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
41 PartialSize(0), Mode(0), ID(0), Complete(false),
42 Local(false), QueueCounter(0)
43 {
44 Owner->Add(this);
45 Status = StatIdle;
46 }
47 /*}}}*/
48 // Acquire::Item::~Item - Destructor /*{{{*/
49 // ---------------------------------------------------------------------
50 /* */
51 pkgAcquire::Item::~Item()
52 {
53 Owner->Remove(this);
54 }
55 /*}}}*/
56 // Acquire::Item::Failed - Item failed to download /*{{{*/
57 // ---------------------------------------------------------------------
58 /* We return to an idle state if there are still other queues that could
59 fetch this object */
60 void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
61 {
62 Status = StatIdle;
63 ErrorText = LookupTag(Message,"Message");
64 if (QueueCounter <= 1)
65 {
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
68 retried [CDROMs] */
69 if (Cnf->LocalOnly == true &&
70 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
71 {
72 Status = StatIdle;
73 Dequeue();
74 return;
75 }
76
77 Status = StatError;
78 Dequeue();
79 }
80 }
81 /*}}}*/
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)
87 {
88 Status = StatFetching;
89 if (FileSize == 0 && Complete == false)
90 FileSize = Size;
91 }
92 /*}}}*/
93 // Acquire::Item::Done - Item downloaded OK /*{{{*/
94 // ---------------------------------------------------------------------
95 /* */
96 void pkgAcquire::Item::Done(string Message,unsigned long Size,string,
97 pkgAcquire::MethodConfig *Cnf)
98 {
99 // We just downloaded something..
100 string FileName = LookupTag(Message,"Filename");
101 if (Complete == false && FileName == DestFile)
102 {
103 if (Owner->Log != 0)
104 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
105 }
106
107 if (FileSize == 0)
108 FileSize= Size;
109
110 Status = StatDone;
111 ErrorText = string();
112 Owner->Dequeue(this);
113 }
114 /*}}}*/
115 // Acquire::Item::Rename - Rename a file /*{{{*/
116 // ---------------------------------------------------------------------
117 /* This helper function is used by alot of item methods as thier final
118 step */
119 void pkgAcquire::Item::Rename(string From,string To)
120 {
121 if (rename(From.c_str(),To.c_str()) != 0)
122 {
123 char S[300];
124 snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
125 From.c_str(),To.c_str());
126 Status = StatError;
127 ErrorText = S;
128 }
129 }
130 /*}}}*/
131
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)
139 {
140 Decompression = false;
141 Erase = false;
142
143 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
144 DestFile += URItoFileName(URI);
145
146 // Create the item
147 Desc.URI = URI + ".gz";
148 Desc.Description = URIDesc;
149 Desc.Owner = this;
150 Desc.ShortDesc = ShortDesc;
151
152 QueueURI(Desc);
153 }
154 /*}}}*/
155 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
156 // ---------------------------------------------------------------------
157 /* The only header we use is the last-modified header. */
158 string pkgAcqIndex::Custom600Headers()
159 {
160 string Final = _config->FindDir("Dir::State::lists");
161 Final += URItoFileName(RealURI);
162
163 struct stat Buf;
164 if (stat(Final.c_str(),&Buf) != 0)
165 return "\nIndex-File: true";
166
167 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
168 }
169 /*}}}*/
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)
179 {
180 Item::Done(Message,Size,MD5,Cfg);
181
182 if (Decompression == true)
183 {
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);
189
190 /* We restore the original name to DestFile so that the clean operation
191 will work OK */
192 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
193 DestFile += URItoFileName(RealURI);
194
195 // Remove the compressed version.
196 if (Erase == true)
197 unlink(DestFile.c_str());
198 return;
199 }
200
201 Erase = false;
202 Complete = true;
203
204 // Handle the unzipd case
205 string FileName = LookupTag(Message,"Alt-Filename");
206 if (FileName.empty() == false)
207 {
208 // The files timestamp matches
209 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
210 return;
211
212 Decompression = true;
213 Local = true;
214 DestFile += ".decomp";
215 Desc.URI = "copy:" + FileName;
216 QueueURI(Desc);
217 Mode = "copy";
218 return;
219 }
220
221 FileName = LookupTag(Message,"Filename");
222 if (FileName.empty() == true)
223 {
224 Status = StatError;
225 ErrorText = "Method gave a blank filename";
226 }
227
228 // The files timestamp matches
229 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
230 return;
231
232 if (FileName == DestFile)
233 Erase = true;
234 else
235 Local = true;
236
237 Decompression = true;
238 DestFile += ".decomp";
239 Desc.URI = "gzip:" + FileName;
240 QueueURI(Desc);
241 Mode = "gzip";
242 }
243 /*}}}*/
244
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)
251 {
252 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
253 DestFile += URItoFileName(URI);
254
255 // Create the item
256 Desc.URI = URI;
257 Desc.Description = URIDesc;
258 Desc.ShortDesc = ShortDesc;
259 Desc.Owner = this;
260
261 QueueURI(Desc);
262 }
263 /*}}}*/
264 // AcqIndexRel::Custom600Headers - Insert custom request headers /*{{{*/
265 // ---------------------------------------------------------------------
266 /* The only header we use is the last-modified header. */
267 string pkgAcqIndexRel::Custom600Headers()
268 {
269 string Final = _config->FindDir("Dir::State::lists");
270 Final += URItoFileName(RealURI);
271
272 struct stat Buf;
273 if (stat(Final.c_str(),&Buf) != 0)
274 return "\nIndex-File: true";
275
276 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
277 }
278 /*}}}*/
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)
286 {
287 Item::Done(Message,Size,MD5,Cfg);
288
289 string FileName = LookupTag(Message,"Filename");
290 if (FileName.empty() == true)
291 {
292 Status = StatError;
293 ErrorText = "Method gave a blank filename";
294 return;
295 }
296
297 Complete = true;
298
299 // The files timestamp matches
300 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
301 return;
302
303 // We have to copy it into place
304 if (FileName != DestFile)
305 {
306 Local = true;
307 Desc.URI = "copy:" + FileName;
308 QueueURI(Desc);
309 return;
310 }
311
312 // Done, move it into position
313 string FinalFile = _config->FindDir("Dir::State::lists");
314 FinalFile += URItoFileName(RealURI);
315 Rename(DestFile,FinalFile);
316
317 chmod(FinalFile.c_str(),0644);
318 }
319 /*}}}*/
320 // AcqIndexRel::Failed - Silence failure messages for missing rel files /*{{{*/
321 // ---------------------------------------------------------------------
322 /* */
323 void pkgAcqIndexRel::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
324 {
325 if (Cnf->LocalOnly == true ||
326 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
327 {
328 // Ignore this
329 Status = StatDone;
330 Complete = false;
331 Dequeue();
332 return;
333 }
334
335 Item::Failed(Message,Cnf);
336 }
337 /*}}}*/
338
339 // AcqArchive::AcqArchive - Constructor /*{{{*/
340 // ---------------------------------------------------------------------
341 /* This just sets up the initial fetch environment and queues the first
342 possibilitiy */
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())
348 {
349 Retries = _config->FindI("Acquire::Retries",0);
350
351 if (Version.Arch() == 0)
352 {
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());
357 return;
358 }
359
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++)
365 {
366 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
367 continue;
368 break;
369 }
370
371 // Does not really matter here.. we are going to fail out below
372 if (Vf.end() != true)
373 {
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)
377 return;
378
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());
384 }
385
386 // Select a source
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());
391 }
392 /*}}}*/
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
397 checking later. */
398 bool pkgAcqArchive::QueueNext()
399 {
400 for (; Vf.end() == false; Vf++)
401 {
402 // Ignore not source sources
403 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
404 continue;
405
406 // Try to cross match against the source list
407 pkgIndexFile *Index;
408 if (Sources->FindIndex(Vf.File(),Index) == false)
409 continue;
410
411 // Grab the text package record
412 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
413 if (_error->PendingError() == true)
414 return false;
415
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());
422
423 // See if we already have the file. (Legacy filenames)
424 FileSize = Version->Size;
425 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
426 struct stat Buf;
427 if (stat(FinalFile.c_str(),&Buf) == 0)
428 {
429 // Make sure the size matches
430 if ((unsigned)Buf.st_size == Version->Size)
431 {
432 Complete = true;
433 Local = true;
434 Status = StatDone;
435 StoreFilename = DestFile = FinalFile;
436 return true;
437 }
438
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());
442 }
443
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)
447 {
448 // Make sure the size matches
449 if ((unsigned)Buf.st_size == Version->Size)
450 {
451 Complete = true;
452 Local = true;
453 Status = StatDone;
454 StoreFilename = DestFile = FinalFile;
455 return true;
456 }
457
458 /* Hmm, we have a file and its size does not match, this shouldnt
459 happen.. */
460 unlink(FinalFile.c_str());
461 }
462
463 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
464
465 // Check the destination file
466 if (stat(DestFile.c_str(),&Buf) == 0)
467 {
468 // Hmm, the partial file is too big, erase it
469 if ((unsigned)Buf.st_size > Version->Size)
470 unlink(DestFile.c_str());
471 else
472 PartialSize = Buf.st_size;
473 }
474
475 // Create the item
476 Local = false;
477 Desc.URI = Index->ArchiveURI(PkgFile);
478 Desc.Description = Index->ArchiveInfo(Version);
479 Desc.Owner = this;
480 Desc.ShortDesc = Version.ParentPkg().Name();
481 QueueURI(Desc);
482
483 Vf++;
484 return true;
485 }
486 return false;
487 }
488 /*}}}*/
489 // AcqArchive::Done - Finished fetching /*{{{*/
490 // ---------------------------------------------------------------------
491 /* */
492 void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash,
493 pkgAcquire::MethodConfig *Cfg)
494 {
495 Item::Done(Message,Size,Md5Hash,Cfg);
496
497 // Check the size
498 if (Size != Version->Size)
499 {
500 Status = StatError;
501 ErrorText = _("Size mismatch");
502 return;
503 }
504
505 // Check the md5
506 if (Md5Hash.empty() == false && MD5.empty() == false)
507 {
508 if (Md5Hash != MD5)
509 {
510 Status = StatError;
511 ErrorText = _("MD5Sum mismatch");
512 Rename(DestFile,DestFile + ".FAILED");
513 return;
514 }
515 }
516
517 // Grab the output filename
518 string FileName = LookupTag(Message,"Filename");
519 if (FileName.empty() == true)
520 {
521 Status = StatError;
522 ErrorText = "Method gave a blank filename";
523 return;
524 }
525
526 Complete = true;
527
528 // Reference filename
529 if (FileName != DestFile)
530 {
531 StoreFilename = DestFile = FileName;
532 Local = true;
533 return;
534 }
535
536 // Done, move it into position
537 string FinalFile = _config->FindDir("Dir::Cache::Archives");
538 FinalFile += flNotDir(StoreFilename);
539 Rename(DestFile,FinalFile);
540
541 StoreFilename = DestFile = FinalFile;
542 Complete = true;
543 }
544 /*}}}*/
545 // AcqArchive::Failed - Failure handler /*{{{*/
546 // ---------------------------------------------------------------------
547 /* Here we try other sources */
548 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
549 {
550 ErrorText = LookupTag(Message,"Message");
551
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
554 recorded. */
555 if (Cnf->Removable == true &&
556 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
557 {
558 // Vf = Version.FileList();
559 while (Vf.end() == false) Vf++;
560 StoreFilename = string();
561 Item::Failed(Message,Cnf);
562 return;
563 }
564
565 if (QueueNext() == false)
566 {
567 // This is the retry counter
568 if (Retries != 0 &&
569 Cnf->LocalOnly == false &&
570 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
571 {
572 Retries--;
573 Vf = Version.FileList();
574 if (QueueNext() == true)
575 return;
576 }
577
578 StoreFilename = string();
579 Item::Failed(Message,Cnf);
580 }
581 }
582 /*}}}*/
583 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
584 // ---------------------------------------------------------------------
585 /* */
586 void pkgAcqArchive::Finished()
587 {
588 if (Status == pkgAcquire::Item::StatDone &&
589 Complete == true)
590 return;
591 StoreFilename = string();
592 }
593 /*}}}*/
594
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)
601 {
602 Retries = _config->FindI("Acquire::Retries",0);
603
604 DestFile = flNotDir(URI);
605
606 // Create the item
607 Desc.URI = URI;
608 Desc.Description = Dsc;
609 Desc.Owner = this;
610
611 // Set the short description to the archive component
612 Desc.ShortDesc = ShortDesc;
613
614 // Get the transfer sizes
615 FileSize = Size;
616 struct stat Buf;
617 if (stat(DestFile.c_str(),&Buf) == 0)
618 {
619 // Hmm, the partial file is too big, erase it
620 if ((unsigned)Buf.st_size > Size)
621 unlink(DestFile.c_str());
622 else
623 PartialSize = Buf.st_size;
624 }
625
626 QueueURI(Desc);
627 }
628 /*}}}*/
629 // AcqFile::Done - Item downloaded OK /*{{{*/
630 // ---------------------------------------------------------------------
631 /* */
632 void pkgAcqFile::Done(string Message,unsigned long Size,string MD5,
633 pkgAcquire::MethodConfig *Cnf)
634 {
635 // Check the md5
636 if (Md5Hash.empty() == false && MD5.empty() == false)
637 {
638 if (Md5Hash != MD5)
639 {
640 Status = StatError;
641 ErrorText = "MD5Sum mismatch";
642 Rename(DestFile,DestFile + ".FAILED");
643 return;
644 }
645 }
646
647 Item::Done(Message,Size,MD5,Cnf);
648
649 string FileName = LookupTag(Message,"Filename");
650 if (FileName.empty() == true)
651 {
652 Status = StatError;
653 ErrorText = "Method gave a blank filename";
654 return;
655 }
656
657 Complete = true;
658
659 // The files timestamp matches
660 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
661 return;
662
663 // We have to copy it into place
664 if (FileName != DestFile)
665 {
666 Local = true;
667 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
668 Cnf->Removable == true)
669 {
670 Desc.URI = "copy:" + FileName;
671 QueueURI(Desc);
672 return;
673 }
674
675 // Erase the file if it is a symlink so we can overwrite it
676 struct stat St;
677 if (lstat(DestFile.c_str(),&St) == 0)
678 {
679 if (S_ISLNK(St.st_mode) != 0)
680 unlink(DestFile.c_str());
681 }
682
683 // Symlink the file
684 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
685 {
686 ErrorText = "Link to " + DestFile + " failure ";
687 Status = StatError;
688 Complete = false;
689 }
690 }
691 }
692 /*}}}*/
693 // AcqFile::Failed - Failure handler /*{{{*/
694 // ---------------------------------------------------------------------
695 /* Here we try other sources */
696 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
697 {
698 ErrorText = LookupTag(Message,"Message");
699
700 // This is the retry counter
701 if (Retries != 0 &&
702 Cnf->LocalOnly == false &&
703 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
704 {
705 Retries--;
706 QueueURI(Desc);
707 return;
708 }
709
710 Item::Failed(Message,Cnf);
711 }
712 /*}}}*/