Added --no-download
[ntk/apt.git] / apt-pkg / acquire-item.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-item.cc,v 1.30 1999/05/24 03:39:36 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/error.h>
22 #include <apt-pkg/strutl.h>
23 #include <apt-pkg/fileutl.h>
24
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <stdio.h>
30 /*}}}*/
31
32 // Acquire::Item::Item - Constructor /*{{{*/
33 // ---------------------------------------------------------------------
34 /* */
35 pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
36 PartialSize(0), Mode(0), ID(0), Complete(false),
37 Local(false), QueueCounter(0)
38 {
39 Owner->Add(this);
40 Status = StatIdle;
41 }
42 /*}}}*/
43 // Acquire::Item::~Item - Destructor /*{{{*/
44 // ---------------------------------------------------------------------
45 /* */
46 pkgAcquire::Item::~Item()
47 {
48 Owner->Remove(this);
49 }
50 /*}}}*/
51 // Acquire::Item::Failed - Item failed to download /*{{{*/
52 // ---------------------------------------------------------------------
53 /* We return to an idle state if there are still other queues that could
54 fetch this object */
55 void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
56 {
57 Status = StatIdle;
58 ErrorText = LookupTag(Message,"Message");
59 if (QueueCounter <= 1)
60 {
61 /* This indicates that the file is not available right now but might
62 be sometime later. If we do a retry cycle then this should be
63 retried [CDROMs] */
64 if (Cnf->LocalOnly == true &&
65 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
66 {
67 Status = StatIdle;
68 Dequeue();
69 return;
70 }
71
72 Status = StatError;
73 Dequeue();
74 }
75 }
76 /*}}}*/
77 // Acquire::Item::Start - Item has begun to download /*{{{*/
78 // ---------------------------------------------------------------------
79 /* Stash status and the file size. Note that setting Complete means
80 sub-phases of the acquire process such as decompresion are operating */
81 void pkgAcquire::Item::Start(string Message,unsigned long Size)
82 {
83 Status = StatFetching;
84 if (FileSize == 0 && Complete == false)
85 FileSize = Size;
86 }
87 /*}}}*/
88 // Acquire::Item::Done - Item downloaded OK /*{{{*/
89 // ---------------------------------------------------------------------
90 /* */
91 void pkgAcquire::Item::Done(string Message,unsigned long Size,string)
92 {
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
101 Status = StatDone;
102 ErrorText = string();
103 Owner->Dequeue(this);
104 }
105 /*}}}*/
106 // Acquire::Item::Rename - Rename a file /*{{{*/
107 // ---------------------------------------------------------------------
108 /* This helper function is used by alot of item methods as thier final
109 step */
110 void 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 /*}}}*/
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 */
127 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,const pkgSourceList::Item *Location) :
128 Item(Owner), Location(Location)
129 {
130 Decompression = false;
131 Erase = false;
132
133 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
134 DestFile += URItoFileName(Location->PackagesURI());
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);
148
149 // Create the Release fetch class
150 new pkgAcqIndexRel(Owner,Location);
151 }
152 /*}}}*/
153 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
154 // ---------------------------------------------------------------------
155 /* The only header we use is the last-modified header. */
156 string pkgAcqIndex::Custom600Headers()
157 {
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)
163 return "\nIndex-File: true";
164
165 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
166 }
167 /*}}}*/
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. */
175 void 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);
185
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
191 // Remove the compressed version.
192 if (Erase == true)
193 unlink(DestFile.c_str());
194 return;
195 }
196
197 Erase = false;
198 Complete = true;
199
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;
209 Local = true;
210 DestFile += ".decomp";
211 Desc.URI = "copy:" + FileName;
212 QueueURI(Desc);
213 Mode = "copy";
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;
227
228 if (FileName == DestFile)
229 Erase = true;
230 else
231 Local = true;
232
233 Decompression = true;
234 DestFile += ".decomp";
235 Desc.URI = "gzip:" + FileName,Location->PackagesInfo();
236 QueueURI(Desc);
237 Mode = "gzip";
238 }
239 /*}}}*/
240
241 // AcqIndexRel::pkgAcqIndexRel - Constructor /*{{{*/
242 // ---------------------------------------------------------------------
243 /* The Release file is added to the queue */
244 pkgAcqIndexRel::pkgAcqIndexRel(pkgAcquire *Owner,
245 const pkgSourceList::Item *Location) :
246 Item(Owner), Location(Location)
247 {
248 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
249 DestFile += URItoFileName(Location->ReleaseURI());
250
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);
263 }
264 /*}}}*/
265 // AcqIndexRel::Custom600Headers - Insert custom request headers /*{{{*/
266 // ---------------------------------------------------------------------
267 /* The only header we use is the last-modified header. */
268 string pkgAcqIndexRel::Custom600Headers()
269 {
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)
275 return "\nIndex-File: true";
276
277 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
278 }
279 /*}}}*/
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. */
285 void 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";
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(Location->ReleaseURI());
315 Rename(DestFile,FinalFile);
316 }
317 /*}}}*/
318 // AcqIndexRel::Failed - Silence failure messages for missing rel files /*{{{*/
319 // ---------------------------------------------------------------------
320 /* */
321 void 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 {
327 // Ignore this
328 Status = StatDone;
329 Complete = false;
330 Dequeue();
331 return;
332 }
333
334 Item::Failed(Message,Cnf);
335 }
336 /*}}}*/
337
338 // AcqArchive::AcqArchive - Constructor /*{{{*/
339 // ---------------------------------------------------------------------
340 /* This just sets up the initial fetch environment and queues the first
341 possibilitiy */
342 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
343 pkgRecords *Recs,pkgCache::VerIterator const &Version,
344 string &StoreFilename) :
345 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
346 StoreFilename(StoreFilename), Vf(Version.FileList())
347 {
348 Retries = _config->FindI("Acquire::Retries",0);
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
355 // Generate the final file name as: package_version_arch.deb
356 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
357 QuoteString(Version.VerStr(),"_:") + '_' +
358 QuoteString(Version.Arch(),"_:.") + ".deb";
359
360 // Select a source
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 // ---------------------------------------------------------------------
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. */
372 bool pkgAcqArchive::QueueNext()
373 {
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)
393 return false;
394
395 PkgFile = Parse.FileName();
396 MD5 = Parse.MD5Hash();
397 if (PkgFile.empty() == true)
398 return _error->Error("The package index files are corrupted. No Filename: "
399 "field for package %s."
400 ,Version.ParentPkg().Name());
401
402 // See if we already have the file. (Legacy filenames)
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;
414 StoreFilename = DestFile = FinalFile;
415 return true;
416 }
417
418 /* Hmm, we have a file and its size does not match, this means it is
419 an old style mismatched arch */
420 unlink(FinalFile.c_str());
421 }
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);
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
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);
460
461 Vf++;
462 return true;
463 }
464 return false;
465 }
466 /*}}}*/
467 // AcqArchive::Done - Finished fetching /*{{{*/
468 // ---------------------------------------------------------------------
469 /* */
470 void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash)
471 {
472 Item::Done(Message,Size,Md5Hash);
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());
487 Rename(DestFile + ".FAILED",DestFile);
488 return;
489 }
490 }
491
492 // Grab the output filename
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 }
500
501 Complete = true;
502
503 // Reference filename
504 if (FileName != DestFile)
505 {
506 StoreFilename = DestFile = FileName;
507 Local = true;
508 return;
509 }
510
511 // Done, move it into position
512 string FinalFile = _config->FindDir("Dir::Cache::Archives");
513 FinalFile += flNotDir(StoreFilename);
514 Rename(DestFile,FinalFile);
515
516 StoreFilename = DestFile = FinalFile;
517 Complete = true;
518 }
519 /*}}}*/
520 // AcqArchive::Failed - Failure handler /*{{{*/
521 // ---------------------------------------------------------------------
522 /* Here we try other sources */
523 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
524 {
525 ErrorText = LookupTag(Message,"Message");
526 if (QueueNext() == false)
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
539 StoreFilename = string();
540 Item::Failed(Message,Cnf);
541 }
542 }
543 /*}}}*/
544 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
545 // ---------------------------------------------------------------------
546 /* */
547 void pkgAcqArchive::Finished()
548 {
549 if (Status == pkgAcquire::Item::StatDone &&
550 Complete == true)
551 return;
552 StoreFilename = string();
553 }
554 /*}}}*/
555
556 // AcqFile::pkgAcqFile - Constructor /*{{{*/
557 // ---------------------------------------------------------------------
558 /* The file is added to the queue */
559 pkgAcqFile::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 /* */
591 void 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 /*}}}*/