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