Fixed another oops
[ntk/apt.git] / apt-pkg / acquire-item.cc
CommitLineData
0118833a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
7d8afa39 3// $Id: acquire-item.cc,v 1.20 1999/01/30 08:08:54 jgg Exp $
0118833a
AL
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.
b185acc2 12
0118833a
AL
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>
03e39e59 21#include <apt-pkg/error.h>
cdcc6d34 22#include <apt-pkg/strutl.h>
0a8a80e5
AL
23
24#include <sys/stat.h>
25#include <unistd.h>
c88edf1d
AL
26#include <errno.h>
27#include <string.h>
28#include <stdio.h>
0118833a
AL
29 /*}}}*/
30
31// Acquire::Item::Item - Constructor /*{{{*/
32// ---------------------------------------------------------------------
33/* */
8267fe24 34pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
a6568219
AL
35 Mode(0), ID(0), Complete(false), Local(false),
36 QueueCounter(0)
0118833a
AL
37{
38 Owner->Add(this);
c88edf1d 39 Status = StatIdle;
0118833a
AL
40}
41 /*}}}*/
42// Acquire::Item::~Item - Destructor /*{{{*/
43// ---------------------------------------------------------------------
44/* */
45pkgAcquire::Item::~Item()
46{
47 Owner->Remove(this);
48}
49 /*}}}*/
c88edf1d
AL
50// Acquire::Item::Failed - Item failed to download /*{{{*/
51// ---------------------------------------------------------------------
93bf083d
AL
52/* We return to an idle state if there are still other queues that could
53 fetch this object */
7d8afa39 54void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
c88edf1d 55{
93bf083d 56 Status = StatIdle;
db890fdb 57 ErrorText = LookupTag(Message,"Message");
c88edf1d 58 if (QueueCounter <= 1)
93bf083d 59 {
a72ace20 60 /* This indicates that the file is not available right now but might
7d8afa39 61 be sometime later. If we do a retry cycle then this should be
a72ace20 62 retried */
7d8afa39
AL
63 if (Cnf->LocalOnly == true &&
64 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
a72ace20
AL
65 {
66 Status = StatIdle;
67 Owner->Dequeue(this);
68 return;
69 }
70
93bf083d 71 Status = StatError;
c88edf1d 72 Owner->Dequeue(this);
93bf083d 73 }
c88edf1d
AL
74}
75 /*}}}*/
8267fe24
AL
76// Acquire::Item::Start - Item has begun to download /*{{{*/
77// ---------------------------------------------------------------------
78/* */
79void pkgAcquire::Item::Start(string Message,unsigned long Size)
80{
81 Status = StatFetching;
82 if (FileSize == 0 && Complete == false)
83 FileSize = Size;
84}
85 /*}}}*/
c88edf1d
AL
86// Acquire::Item::Done - Item downloaded OK /*{{{*/
87// ---------------------------------------------------------------------
88/* */
b98f2859 89void pkgAcquire::Item::Done(string Message,unsigned long Size,string)
c88edf1d 90{
b98f2859
AL
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
c88edf1d
AL
99 Status = StatDone;
100 ErrorText = string();
101 Owner->Dequeue(this);
102}
103 /*}}}*/
8b89e57f
AL
104// Acquire::Item::Rename - Rename a file /*{{{*/
105// ---------------------------------------------------------------------
106/* This helper function is used by alot of item methods as thier final
107 step */
108void 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 /*}}}*/
0118833a
AL
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 */
125pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,const pkgSourceList::Item *Location) :
126 Item(Owner), Location(Location)
127{
8b89e57f 128 Decompression = false;
bfd22fc0 129 Erase = false;
8b89e57f 130
0a8a80e5
AL
131 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
132 DestFile += URItoFileName(Location->PackagesURI());
8267fe24
AL
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);
0118833a 146
0a8a80e5 147 // Create the Release fetch class
0118833a
AL
148 new pkgAcqIndexRel(Owner,Location);
149}
150 /*}}}*/
0a8a80e5 151// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 152// ---------------------------------------------------------------------
0a8a80e5
AL
153/* The only header we use is the last-modified header. */
154string pkgAcqIndex::Custom600Headers()
0118833a 155{
0a8a80e5
AL
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)
a72ace20 161 return "\nIndex-File: true";
0118833a 162
a72ace20 163 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
0118833a
AL
164}
165 /*}}}*/
8b89e57f
AL
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. */
173void 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);
bfd22fc0 183
7a7fa5f0
AL
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
bfd22fc0
AL
189 // Remove the compressed version.
190 if (Erase == true)
bfd22fc0 191 unlink(DestFile.c_str());
8b89e57f
AL
192 return;
193 }
bfd22fc0
AL
194
195 Erase = false;
8267fe24 196 Complete = true;
bfd22fc0 197
8b89e57f
AL
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;
a6568219 207 Local = true;
8b89e57f 208 DestFile += ".decomp";
8267fe24
AL
209 Desc.URI = "copy:" + FileName;
210 QueueURI(Desc);
b98f2859 211 Mode = "copy";
8b89e57f
AL
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;
bfd22fc0
AL
225
226 if (FileName == DestFile)
227 Erase = true;
8267fe24 228 else
a6568219 229 Local = true;
8b89e57f
AL
230
231 Decompression = true;
232 DestFile += ".decomp";
8267fe24
AL
233 Desc.URI = "gzip:" + FileName,Location->PackagesInfo();
234 QueueURI(Desc);
b98f2859 235 Mode = "gzip";
8b89e57f
AL
236}
237 /*}}}*/
30e1eab5
AL
238// AcqIndex::Describe - Describe the Item /*{{{*/
239// ---------------------------------------------------------------------
240/* */
241string pkgAcqIndex::Describe()
242{
243 return Location->PackagesURI();
244}
245 /*}}}*/
8b89e57f 246
0118833a
AL
247// AcqIndexRel::pkgAcqIndexRel - Constructor /*{{{*/
248// ---------------------------------------------------------------------
249/* The Release file is added to the queue */
250pkgAcqIndexRel::pkgAcqIndexRel(pkgAcquire *Owner,
251 const pkgSourceList::Item *Location) :
252 Item(Owner), Location(Location)
253{
0a8a80e5
AL
254 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
255 DestFile += URItoFileName(Location->ReleaseURI());
256
8267fe24
AL
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);
0118833a
AL
269}
270 /*}}}*/
0a8a80e5 271// AcqIndexRel::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 272// ---------------------------------------------------------------------
0a8a80e5
AL
273/* The only header we use is the last-modified header. */
274string pkgAcqIndexRel::Custom600Headers()
0118833a 275{
0a8a80e5
AL
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)
a72ace20 281 return "\nIndex-File: true";
0118833a 282
a72ace20 283 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
0118833a
AL
284}
285 /*}}}*/
c88edf1d
AL
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. */
291void 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";
8b89e57f 300 return;
c88edf1d 301 }
8b89e57f 302
8267fe24
AL
303 Complete = true;
304
8b89e57f
AL
305 // The files timestamp matches
306 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
307 return;
c88edf1d
AL
308
309 // We have to copy it into place
310 if (FileName != DestFile)
311 {
a6568219 312 Local = true;
8267fe24
AL
313 Desc.URI = "copy:" + FileName;
314 QueueURI(Desc);
c88edf1d
AL
315 return;
316 }
317
318 // Done, move it into position
319 string FinalFile = _config->FindDir("Dir::State::lists");
320 FinalFile += URItoFileName(Location->ReleaseURI());
8b89e57f 321 Rename(DestFile,FinalFile);
c88edf1d
AL
322}
323 /*}}}*/
30e1eab5
AL
324// AcqIndexRel::Describe - Describe the Item /*{{{*/
325// ---------------------------------------------------------------------
326/* */
327string pkgAcqIndexRel::Describe()
328{
329 return Location->ReleaseURI();
330}
331 /*}}}*/
03e39e59
AL
332
333// AcqArchive::AcqArchive - Constructor /*{{{*/
334// ---------------------------------------------------------------------
335/* */
336pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
30e1eab5
AL
337 pkgRecords *Recs,pkgCache::VerIterator const &Version,
338 string &StoreFilename) :
339 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
b185acc2 340 StoreFilename(StoreFilename), Vf(Version.FileList())
03e39e59 341{
7d8afa39
AL
342 Retries = _config->FindI("Acquire::Retries",0);
343
03e39e59 344 // Select a source
b185acc2
AL
345 if (QueueNext() == false && _error->PendingError() == false)
346 _error->Error("I wasn't able to locate file for the %s package. "
347 "This might mean you need to manually fix this package.",
348 Version.ParentPkg().Name());
349}
350 /*}}}*/
351// AcqArchive::QueueNext - Queue the next file source /*{{{*/
352// ---------------------------------------------------------------------
353/* This queues the next available file version for download. */
354bool pkgAcqArchive::QueueNext()
355{
03e39e59
AL
356 for (; Vf.end() == false; Vf++)
357 {
358 // Ignore not source sources
359 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
360 continue;
361
362 // Try to cross match against the source list
363 string PkgFile = flNotDir(Vf.File().FileName());
364 pkgSourceList::const_iterator Location;
365 for (Location = Sources->begin(); Location != Sources->end(); Location++)
366 if (PkgFile == URItoFileName(Location->PackagesURI()))
367 break;
368
369 if (Location == Sources->end())
370 continue;
371
372 // Grab the text package record
373 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
374 if (_error->PendingError() == true)
b185acc2 375 return false;
03e39e59
AL
376
377 PkgFile = Parse.FileName();
378 MD5 = Parse.MD5Hash();
379 if (PkgFile.empty() == true)
b185acc2
AL
380 return _error->Error("The package index files are corrupted. No Filename: "
381 "field for package %s."
382 ,Version.ParentPkg().Name());
a6568219
AL
383
384 // See if we already have the file.
385 FileSize = Version->Size;
386 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
387 struct stat Buf;
388 if (stat(FinalFile.c_str(),&Buf) == 0)
389 {
390 // Make sure the size matches
391 if ((unsigned)Buf.st_size == Version->Size)
392 {
393 Complete = true;
394 Local = true;
395 Status = StatDone;
30e1eab5 396 StoreFilename = DestFile = FinalFile;
b185acc2 397 return true;
a6568219
AL
398 }
399
400 /* Hmm, we have a file and its size does not match, this shouldnt
401 happen.. */
402 unlink(FinalFile.c_str());
403 }
404
405 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(PkgFile);
03e39e59
AL
406
407 // Create the item
408 Desc.URI = Location->ArchiveURI(PkgFile);
409 Desc.Description = Location->ArchiveInfo(Version);
410 Desc.Owner = this;
411 Desc.ShortDesc = Version.ParentPkg().Name();
412 QueueURI(Desc);
b185acc2
AL
413
414 Vf++;
415 return true;
03e39e59 416 }
b185acc2
AL
417 return false;
418}
03e39e59
AL
419 /*}}}*/
420// AcqArchive::Done - Finished fetching /*{{{*/
421// ---------------------------------------------------------------------
422/* */
423void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash)
424{
fd9bd3dc 425 Item::Done(Message,Size,Md5Hash);
03e39e59
AL
426
427 // Check the size
428 if (Size != Version->Size)
429 {
430 _error->Error("Size mismatch for package %s",Version.ParentPkg().Name());
431 return;
432 }
433
434 // Check the md5
435 if (Md5Hash.empty() == false && MD5.empty() == false)
436 {
437 if (Md5Hash != MD5)
438 {
439 _error->Error("MD5Sum mismatch for package %s",Version.ParentPkg().Name());
440 return;
441 }
442 }
a6568219
AL
443
444 // Grab the output filename
03e39e59
AL
445 string FileName = LookupTag(Message,"Filename");
446 if (FileName.empty() == true)
447 {
448 Status = StatError;
449 ErrorText = "Method gave a blank filename";
450 return;
451 }
a6568219
AL
452
453 Complete = true;
30e1eab5
AL
454
455 // Reference filename
a6568219
AL
456 if (FileName != DestFile)
457 {
30e1eab5 458 StoreFilename = DestFile = FileName;
a6568219
AL
459 Local = true;
460 return;
461 }
462
463 // Done, move it into position
464 string FinalFile = _config->FindDir("Dir::Cache::Archives");
465 FinalFile += flNotDir(DestFile);
466 Rename(DestFile,FinalFile);
03e39e59 467
30e1eab5 468 StoreFilename = DestFile = FinalFile;
03e39e59
AL
469 Complete = true;
470}
471 /*}}}*/
30e1eab5
AL
472// AcqArchive::Describe - Describe the Item /*{{{*/
473// ---------------------------------------------------------------------
474/* */
475string pkgAcqArchive::Describe()
476{
477 return Desc.URI;
478}
479 /*}}}*/
db890fdb
AL
480// AcqArchive::Failed - Failure handler /*{{{*/
481// ---------------------------------------------------------------------
482/* Here we try other sources */
7d8afa39 483void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
db890fdb
AL
484{
485 ErrorText = LookupTag(Message,"Message");
486 if (QueueNext() == false)
7d8afa39
AL
487 {
488 // This is the retry counter
489 if (Retries != 0 &&
490 Cnf->LocalOnly == false &&
491 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
492 {
493 Retries--;
494 Vf = Version.FileList();
495 if (QueueNext() == true)
496 return;
497 }
498
499 Item::Failed(Message,Cnf);
500 }
db890fdb
AL
501}
502 /*}}}*/