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