]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
Fixed typo
[apt.git] / apt-pkg / acquire-item.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-item.cc,v 1.44 2001/05/07 05:49:43 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 sprintf(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
189 /* We restore the original name to DestFile so that the clean operation
190 will work OK */
191 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
192 DestFile += URItoFileName(RealURI);
193
194 // Remove the compressed version.
195 if (Erase == true)
196 unlink(DestFile.c_str());
197 return;
198 }
199
200 Erase = false;
201 Complete = true;
202
203 // Handle the unzipd case
204 string FileName = LookupTag(Message,"Alt-Filename");
205 if (FileName.empty() == false)
206 {
207 // The files timestamp matches
208 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
209 return;
210
211 Decompression = true;
212 Local = true;
213 DestFile += ".decomp";
214 Desc.URI = "copy:" + FileName;
215 QueueURI(Desc);
216 Mode = "copy";
217 return;
218 }
219
220 FileName = LookupTag(Message,"Filename");
221 if (FileName.empty() == true)
222 {
223 Status = StatError;
224 ErrorText = "Method gave a blank filename";
225 }
226
227 // The files timestamp matches
228 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
229 return;
230
231 if (FileName == DestFile)
232 Erase = true;
233 else
234 Local = true;
235
236 Decompression = true;
237 DestFile += ".decomp";
238 Desc.URI = "gzip:" + FileName;
239 QueueURI(Desc);
240 Mode = "gzip";
241 }
242 /*}}}*/
243
244 // AcqIndexRel::pkgAcqIndexRel - Constructor /*{{{*/
245 // ---------------------------------------------------------------------
246 /* The Release file is added to the queue */
247 pkgAcqIndexRel::pkgAcqIndexRel(pkgAcquire *Owner,
248 string URI,string URIDesc,string ShortDesc) :
249 Item(Owner), RealURI(URI)
250 {
251 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
252 DestFile += URItoFileName(URI);
253
254 // Create the item
255 Desc.URI = URI;
256 Desc.Description = URIDesc;
257 Desc.ShortDesc = ShortDesc;
258 Desc.Owner = this;
259
260 QueueURI(Desc);
261 }
262 /*}}}*/
263 // AcqIndexRel::Custom600Headers - Insert custom request headers /*{{{*/
264 // ---------------------------------------------------------------------
265 /* The only header we use is the last-modified header. */
266 string pkgAcqIndexRel::Custom600Headers()
267 {
268 string Final = _config->FindDir("Dir::State::lists");
269 Final += URItoFileName(RealURI);
270
271 struct stat Buf;
272 if (stat(Final.c_str(),&Buf) != 0)
273 return "\nIndex-File: true";
274
275 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
276 }
277 /*}}}*/
278 // AcqIndexRel::Done - Item downloaded OK /*{{{*/
279 // ---------------------------------------------------------------------
280 /* The release file was not placed into the download directory then
281 a copy URI is generated and it is copied there otherwise the file
282 in the partial directory is moved into .. and the URI is finished. */
283 void pkgAcqIndexRel::Done(string Message,unsigned long Size,string MD5,
284 pkgAcquire::MethodConfig *Cfg)
285 {
286 Item::Done(Message,Size,MD5,Cfg);
287
288 string FileName = LookupTag(Message,"Filename");
289 if (FileName.empty() == true)
290 {
291 Status = StatError;
292 ErrorText = "Method gave a blank filename";
293 return;
294 }
295
296 Complete = true;
297
298 // The files timestamp matches
299 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
300 return;
301
302 // We have to copy it into place
303 if (FileName != DestFile)
304 {
305 Local = true;
306 Desc.URI = "copy:" + FileName;
307 QueueURI(Desc);
308 return;
309 }
310
311 // Done, move it into position
312 string FinalFile = _config->FindDir("Dir::State::lists");
313 FinalFile += URItoFileName(RealURI);
314 Rename(DestFile,FinalFile);
315 }
316 /*}}}*/
317 // AcqIndexRel::Failed - Silence failure messages for missing rel files /*{{{*/
318 // ---------------------------------------------------------------------
319 /* */
320 void pkgAcqIndexRel::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
321 {
322 if (Cnf->LocalOnly == true ||
323 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
324 {
325 // Ignore this
326 Status = StatDone;
327 Complete = false;
328 Dequeue();
329 return;
330 }
331
332 Item::Failed(Message,Cnf);
333 }
334 /*}}}*/
335
336 // AcqArchive::AcqArchive - Constructor /*{{{*/
337 // ---------------------------------------------------------------------
338 /* This just sets up the initial fetch environment and queues the first
339 possibilitiy */
340 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
341 pkgRecords *Recs,pkgCache::VerIterator const &Version,
342 string &StoreFilename) :
343 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
344 StoreFilename(StoreFilename), Vf(Version.FileList())
345 {
346 Retries = _config->FindI("Acquire::Retries",0);
347
348 if (Version.Arch() == 0)
349 {
350 _error->Error(_("I wasn't able to locate a file for the %s package. "
351 "This might mean you need to manually fix this package. (due to missing arch)"),
352 Version.ParentPkg().Name());
353 return;
354 }
355
356 /* We need to find a filename to determine the extension. We make the
357 assumption here that all the available sources for this version share
358 the same extension.. */
359 // Skip not source sources, they do not have file fields.
360 for (; Vf.end() == false; Vf++)
361 {
362 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
363 continue;
364 break;
365 }
366
367 // Does not really matter here.. we are going to fail out below
368 if (Vf.end() != true)
369 {
370 // If this fails to get a file name we will bomb out below.
371 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
372 if (_error->PendingError() == true)
373 return;
374
375 // Generate the final file name as: package_version_arch.foo
376 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
377 QuoteString(Version.VerStr(),"_:") + '_' +
378 QuoteString(Version.Arch(),"_:.") +
379 "." + flExtension(Parse.FileName());
380 }
381
382 // Select a source
383 if (QueueNext() == false && _error->PendingError() == false)
384 _error->Error(_("I wasn't able to locate file for the %s package. "
385 "This might mean you need to manually fix this package."),
386 Version.ParentPkg().Name());
387 }
388 /*}}}*/
389 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
390 // ---------------------------------------------------------------------
391 /* This queues the next available file version for download. It checks if
392 the archive is already available in the cache and stashs the MD5 for
393 checking later. */
394 bool pkgAcqArchive::QueueNext()
395 {
396 for (; Vf.end() == false; Vf++)
397 {
398 // Ignore not source sources
399 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
400 continue;
401
402 // Try to cross match against the source list
403 pkgIndexFile *Index;
404 if (Sources->FindIndex(Vf.File(),Index) == false)
405 continue;
406
407 // Grab the text package record
408 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
409 if (_error->PendingError() == true)
410 return false;
411
412 string PkgFile = Parse.FileName();
413 MD5 = Parse.MD5Hash();
414 if (PkgFile.empty() == true)
415 return _error->Error(_("The package index files are corrupted. No Filename: "
416 "field for package %s."),
417 Version.ParentPkg().Name());
418
419 // See if we already have the file. (Legacy filenames)
420 FileSize = Version->Size;
421 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
422 struct stat Buf;
423 if (stat(FinalFile.c_str(),&Buf) == 0)
424 {
425 // Make sure the size matches
426 if ((unsigned)Buf.st_size == Version->Size)
427 {
428 Complete = true;
429 Local = true;
430 Status = StatDone;
431 StoreFilename = DestFile = FinalFile;
432 return true;
433 }
434
435 /* Hmm, we have a file and its size does not match, this means it is
436 an old style mismatched arch */
437 unlink(FinalFile.c_str());
438 }
439
440 // Check it again using the new style output filenames
441 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
442 if (stat(FinalFile.c_str(),&Buf) == 0)
443 {
444 // Make sure the size matches
445 if ((unsigned)Buf.st_size == Version->Size)
446 {
447 Complete = true;
448 Local = true;
449 Status = StatDone;
450 StoreFilename = DestFile = FinalFile;
451 return true;
452 }
453
454 /* Hmm, we have a file and its size does not match, this shouldnt
455 happen.. */
456 unlink(FinalFile.c_str());
457 }
458
459 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
460
461 // Check the destination file
462 if (stat(DestFile.c_str(),&Buf) == 0)
463 {
464 // Hmm, the partial file is too big, erase it
465 if ((unsigned)Buf.st_size > Version->Size)
466 unlink(DestFile.c_str());
467 else
468 PartialSize = Buf.st_size;
469 }
470
471 // Create the item
472 Local = false;
473 Desc.URI = Index->ArchiveURI(PkgFile);
474 Desc.Description = Index->ArchiveInfo(Version);
475 Desc.Owner = this;
476 Desc.ShortDesc = Version.ParentPkg().Name();
477 QueueURI(Desc);
478
479 Vf++;
480 return true;
481 }
482 return false;
483 }
484 /*}}}*/
485 // AcqArchive::Done - Finished fetching /*{{{*/
486 // ---------------------------------------------------------------------
487 /* */
488 void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash,
489 pkgAcquire::MethodConfig *Cfg)
490 {
491 Item::Done(Message,Size,Md5Hash,Cfg);
492
493 // Check the size
494 if (Size != Version->Size)
495 {
496 Status = StatError;
497 ErrorText = _("Size mismatch");
498 return;
499 }
500
501 // Check the md5
502 if (Md5Hash.empty() == false && MD5.empty() == false)
503 {
504 if (Md5Hash != MD5)
505 {
506 Status = StatError;
507 ErrorText = _("MD5Sum mismatch");
508 Rename(DestFile,DestFile + ".FAILED");
509 return;
510 }
511 }
512
513 // Grab the output filename
514 string FileName = LookupTag(Message,"Filename");
515 if (FileName.empty() == true)
516 {
517 Status = StatError;
518 ErrorText = "Method gave a blank filename";
519 return;
520 }
521
522 Complete = true;
523
524 // Reference filename
525 if (FileName != DestFile)
526 {
527 StoreFilename = DestFile = FileName;
528 Local = true;
529 return;
530 }
531
532 // Done, move it into position
533 string FinalFile = _config->FindDir("Dir::Cache::Archives");
534 FinalFile += flNotDir(StoreFilename);
535 Rename(DestFile,FinalFile);
536
537 StoreFilename = DestFile = FinalFile;
538 Complete = true;
539 }
540 /*}}}*/
541 // AcqArchive::Failed - Failure handler /*{{{*/
542 // ---------------------------------------------------------------------
543 /* Here we try other sources */
544 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
545 {
546 ErrorText = LookupTag(Message,"Message");
547
548 /* We don't really want to retry on failed media swaps, this prevents
549 that. An interesting observation is that permanent failures are not
550 recorded. */
551 if (Cnf->Removable == true &&
552 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
553 {
554 // Vf = Version.FileList();
555 while (Vf.end() == false) Vf++;
556 StoreFilename = string();
557 Item::Failed(Message,Cnf);
558 return;
559 }
560
561 if (QueueNext() == false)
562 {
563 // This is the retry counter
564 if (Retries != 0 &&
565 Cnf->LocalOnly == false &&
566 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
567 {
568 Retries--;
569 Vf = Version.FileList();
570 if (QueueNext() == true)
571 return;
572 }
573
574 StoreFilename = string();
575 Item::Failed(Message,Cnf);
576 }
577 }
578 /*}}}*/
579 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
580 // ---------------------------------------------------------------------
581 /* */
582 void pkgAcqArchive::Finished()
583 {
584 if (Status == pkgAcquire::Item::StatDone &&
585 Complete == true)
586 return;
587 StoreFilename = string();
588 }
589 /*}}}*/
590
591 // AcqFile::pkgAcqFile - Constructor /*{{{*/
592 // ---------------------------------------------------------------------
593 /* The file is added to the queue */
594 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string MD5,
595 unsigned long Size,string Dsc,string ShortDesc) :
596 Item(Owner), Md5Hash(MD5)
597 {
598 Retries = _config->FindI("Acquire::Retries",0);
599
600 DestFile = flNotDir(URI);
601
602 // Create the item
603 Desc.URI = URI;
604 Desc.Description = Dsc;
605 Desc.Owner = this;
606
607 // Set the short description to the archive component
608 Desc.ShortDesc = ShortDesc;
609
610 // Get the transfer sizes
611 FileSize = Size;
612 struct stat Buf;
613 if (stat(DestFile.c_str(),&Buf) == 0)
614 {
615 // Hmm, the partial file is too big, erase it
616 if ((unsigned)Buf.st_size > Size)
617 unlink(DestFile.c_str());
618 else
619 PartialSize = Buf.st_size;
620 }
621
622 QueueURI(Desc);
623 }
624 /*}}}*/
625 // AcqFile::Done - Item downloaded OK /*{{{*/
626 // ---------------------------------------------------------------------
627 /* */
628 void pkgAcqFile::Done(string Message,unsigned long Size,string MD5,
629 pkgAcquire::MethodConfig *Cnf)
630 {
631 // Check the md5
632 if (Md5Hash.empty() == false && MD5.empty() == false)
633 {
634 if (Md5Hash != MD5)
635 {
636 Status = StatError;
637 ErrorText = "MD5Sum mismatch";
638 Rename(DestFile,DestFile + ".FAILED");
639 return;
640 }
641 }
642
643 Item::Done(Message,Size,MD5,Cnf);
644
645 string FileName = LookupTag(Message,"Filename");
646 if (FileName.empty() == true)
647 {
648 Status = StatError;
649 ErrorText = "Method gave a blank filename";
650 return;
651 }
652
653 Complete = true;
654
655 // The files timestamp matches
656 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
657 return;
658
659 // We have to copy it into place
660 if (FileName != DestFile)
661 {
662 Local = true;
663 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
664 Cnf->Removable == true)
665 {
666 Desc.URI = "copy:" + FileName;
667 QueueURI(Desc);
668 return;
669 }
670
671 // Erase the file if it is a symlink so we can overwrite it
672 struct stat St;
673 if (lstat(DestFile.c_str(),&St) == 0)
674 {
675 if (S_ISLNK(St.st_mode) != 0)
676 unlink(DestFile.c_str());
677 }
678
679 // Symlink the file
680 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
681 {
682 ErrorText = "Link to " + DestFile + " failure ";
683 Status = StatError;
684 Complete = false;
685 }
686 }
687 }
688 /*}}}*/
689 // AcqFile::Failed - Failure handler /*{{{*/
690 // ---------------------------------------------------------------------
691 /* Here we try other sources */
692 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
693 {
694 ErrorText = LookupTag(Message,"Message");
695
696 // This is the retry counter
697 if (Retries != 0 &&
698 Cnf->LocalOnly == false &&
699 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
700 {
701 Retries--;
702 QueueURI(Desc);
703 return;
704 }
705
706 Item::Failed(Message,Cnf);
707 }
708 /*}}}*/