]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
working apt-get source
[apt.git] / apt-pkg / acquire-item.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-item.cc,v 1.27 1999/04/07 05:30: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/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 // Generate the final file name as: package_version_arch.deb
351 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
352 QuoteString(Version.VerStr(),"_:") + '_' +
353 QuoteString(Version.Arch(),"_:.") + ".deb";
354
355 // Select a source
356 if (QueueNext() == false && _error->PendingError() == false)
357 _error->Error("I wasn't able to locate file for the %s package. "
358 "This might mean you need to manually fix this package.",
359 Version.ParentPkg().Name());
360 }
361 /*}}}*/
362 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
363 // ---------------------------------------------------------------------
364 /* This queues the next available file version for download. It checks if
365 the archive is already available in the cache and stashs the MD5 for
366 checking later. */
367 bool pkgAcqArchive::QueueNext()
368 {
369 for (; Vf.end() == false; Vf++)
370 {
371 // Ignore not source sources
372 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
373 continue;
374
375 // Try to cross match against the source list
376 string PkgFile = flNotDir(Vf.File().FileName());
377 pkgSourceList::const_iterator Location;
378 for (Location = Sources->begin(); Location != Sources->end(); Location++)
379 if (PkgFile == URItoFileName(Location->PackagesURI()))
380 break;
381
382 if (Location == Sources->end())
383 continue;
384
385 // Grab the text package record
386 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
387 if (_error->PendingError() == true)
388 return false;
389
390 PkgFile = Parse.FileName();
391 MD5 = Parse.MD5Hash();
392 if (PkgFile.empty() == true)
393 return _error->Error("The package index files are corrupted. No Filename: "
394 "field for package %s."
395 ,Version.ParentPkg().Name());
396
397 // See if we already have the file. (Legacy filenames)
398 FileSize = Version->Size;
399 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
400 struct stat Buf;
401 if (stat(FinalFile.c_str(),&Buf) == 0)
402 {
403 // Make sure the size matches
404 if ((unsigned)Buf.st_size == Version->Size)
405 {
406 Complete = true;
407 Local = true;
408 Status = StatDone;
409 StoreFilename = DestFile = FinalFile;
410 return true;
411 }
412
413 /* Hmm, we have a file and its size does not match, this means it is
414 an old style mismatched arch */
415 unlink(FinalFile.c_str());
416 }
417
418 // Check it again using the new style output filenames
419 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
420 if (stat(FinalFile.c_str(),&Buf) == 0)
421 {
422 // Make sure the size matches
423 if ((unsigned)Buf.st_size == Version->Size)
424 {
425 Complete = true;
426 Local = true;
427 Status = StatDone;
428 StoreFilename = DestFile = FinalFile;
429 return true;
430 }
431
432 /* Hmm, we have a file and its size does not match, this shouldnt
433 happen.. */
434 unlink(FinalFile.c_str());
435 }
436
437 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
438
439 // Check the destination file
440 if (stat(DestFile.c_str(),&Buf) == 0)
441 {
442 // Hmm, the partial file is too big, erase it
443 if ((unsigned)Buf.st_size > Version->Size)
444 unlink(DestFile.c_str());
445 else
446 PartialSize = Buf.st_size;
447 }
448
449 // Create the item
450 Desc.URI = Location->ArchiveURI(PkgFile);
451 Desc.Description = Location->ArchiveInfo(Version);
452 Desc.Owner = this;
453 Desc.ShortDesc = Version.ParentPkg().Name();
454 QueueURI(Desc);
455
456 Vf++;
457 return true;
458 }
459 return false;
460 }
461 /*}}}*/
462 // AcqArchive::Done - Finished fetching /*{{{*/
463 // ---------------------------------------------------------------------
464 /* */
465 void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash)
466 {
467 Item::Done(Message,Size,Md5Hash);
468
469 // Check the size
470 if (Size != Version->Size)
471 {
472 _error->Error("Size mismatch for package %s",Version.ParentPkg().Name());
473 return;
474 }
475
476 // Check the md5
477 if (Md5Hash.empty() == false && MD5.empty() == false)
478 {
479 if (Md5Hash != MD5)
480 {
481 _error->Error("MD5Sum mismatch for package %s",Version.ParentPkg().Name());
482 return;
483 }
484 }
485
486 // Grab the output filename
487 string FileName = LookupTag(Message,"Filename");
488 if (FileName.empty() == true)
489 {
490 Status = StatError;
491 ErrorText = "Method gave a blank filename";
492 return;
493 }
494
495 Complete = true;
496
497 // Reference filename
498 if (FileName != DestFile)
499 {
500 StoreFilename = DestFile = FileName;
501 Local = true;
502 return;
503 }
504
505 // Done, move it into position
506 string FinalFile = _config->FindDir("Dir::Cache::Archives");
507 FinalFile += flNotDir(StoreFilename);
508 Rename(DestFile,FinalFile);
509
510 StoreFilename = DestFile = FinalFile;
511 Complete = true;
512 }
513 /*}}}*/
514 // AcqArchive::Failed - Failure handler /*{{{*/
515 // ---------------------------------------------------------------------
516 /* Here we try other sources */
517 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
518 {
519 ErrorText = LookupTag(Message,"Message");
520 if (QueueNext() == false)
521 {
522 // This is the retry counter
523 if (Retries != 0 &&
524 Cnf->LocalOnly == false &&
525 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
526 {
527 Retries--;
528 Vf = Version.FileList();
529 if (QueueNext() == true)
530 return;
531 }
532
533 StoreFilename = string();
534 Item::Failed(Message,Cnf);
535 }
536 }
537 /*}}}*/
538
539 // AcqFile::pkgAcqFile - Constructor /*{{{*/
540 // ---------------------------------------------------------------------
541 /* The file is added to the queue */
542 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string MD5,
543 unsigned long Size,string Dsc,string ShortDesc) :
544 Item(Owner), MD5(MD5)
545 {
546 DestFile = flNotDir(URI);
547
548 // Create the item
549 Desc.URI = URI;
550 Desc.Description = Dsc;
551 Desc.Owner = this;
552
553 // Set the short description to the archive component
554 Desc.ShortDesc = ShortDesc;
555
556 // Get the transfer sizes
557 FileSize = Size;
558 struct stat Buf;
559 if (stat(DestFile.c_str(),&Buf) == 0)
560 {
561 // Hmm, the partial file is too big, erase it
562 if ((unsigned)Buf.st_size > Size)
563 unlink(DestFile.c_str());
564 else
565 PartialSize = Buf.st_size;
566 }
567
568 QueueURI(Desc);
569 }
570 /*}}}*/
571 // AcqFile::Done - Item downloaded OK /*{{{*/
572 // ---------------------------------------------------------------------
573 /* */
574 void pkgAcqFile::Done(string Message,unsigned long Size,string MD5)
575 {
576 Item::Done(Message,Size,MD5);
577
578 string FileName = LookupTag(Message,"Filename");
579 if (FileName.empty() == true)
580 {
581 Status = StatError;
582 ErrorText = "Method gave a blank filename";
583 return;
584 }
585
586 Complete = true;
587
588 // The files timestamp matches
589 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
590 return;
591
592 // We have to copy it into place
593 if (FileName != DestFile)
594 {
595 Local = true;
596 Desc.URI = "copy:" + FileName;
597 QueueURI(Desc);
598 return;
599 }
600 }
601 /*}}}*/