]> git.saurik.com Git - apt.git/blame - apt-pkg/acquire-item.cc
merged debian/sid
[apt.git] / apt-pkg / acquire-item.cc
CommitLineData
0118833a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
b3d44315 3// $Id: acquire-item.cc,v 1.46.2.9 2004/01/16 18:51:11 mdz 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 /*{{{*/
ea542140
DK
16#include <config.h>
17
0118833a
AL
18#include <apt-pkg/acquire-item.h>
19#include <apt-pkg/configuration.h>
e878aedb 20#include <apt-pkg/aptconfiguration.h>
b2e465d6 21#include <apt-pkg/sourcelist.h>
03e39e59 22#include <apt-pkg/error.h>
cdcc6d34 23#include <apt-pkg/strutl.h>
36375005 24#include <apt-pkg/fileutl.h>
b3d44315 25#include <apt-pkg/md5.h>
ac5b205a
MV
26#include <apt-pkg/sha1.h>
27#include <apt-pkg/tagfile.h>
472ff00e
DK
28#include <apt-pkg/indexrecords.h>
29#include <apt-pkg/metaindex.h>
0a8a80e5
AL
30
31#include <sys/stat.h>
32#include <unistd.h>
c88edf1d 33#include <errno.h>
5819a761 34#include <string>
ac5b205a 35#include <sstream>
c88edf1d 36#include <stdio.h>
1ddb8596 37#include <ctime>
ea542140
DK
38
39#include <apti18n.h>
0118833a
AL
40 /*}}}*/
41
b3d44315 42using namespace std;
5819a761 43
0118833a
AL
44// Acquire::Item::Item - Constructor /*{{{*/
45// ---------------------------------------------------------------------
46/* */
8267fe24 47pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
6b1ff003
AL
48 PartialSize(0), Mode(0), ID(0), Complete(false),
49 Local(false), QueueCounter(0)
0118833a
AL
50{
51 Owner->Add(this);
c88edf1d 52 Status = StatIdle;
0118833a
AL
53}
54 /*}}}*/
55// Acquire::Item::~Item - Destructor /*{{{*/
56// ---------------------------------------------------------------------
57/* */
58pkgAcquire::Item::~Item()
59{
60 Owner->Remove(this);
61}
62 /*}}}*/
c88edf1d
AL
63// Acquire::Item::Failed - Item failed to download /*{{{*/
64// ---------------------------------------------------------------------
93bf083d
AL
65/* We return to an idle state if there are still other queues that could
66 fetch this object */
7d8afa39 67void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
c88edf1d 68{
93bf083d 69 Status = StatIdle;
db890fdb 70 ErrorText = LookupTag(Message,"Message");
361593e9 71 UsedMirror = LookupTag(Message,"UsedMirror");
c88edf1d 72 if (QueueCounter <= 1)
93bf083d 73 {
a72ace20 74 /* This indicates that the file is not available right now but might
7d8afa39 75 be sometime later. If we do a retry cycle then this should be
17caf1b1 76 retried [CDROMs] */
7d8afa39
AL
77 if (Cnf->LocalOnly == true &&
78 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
a72ace20
AL
79 {
80 Status = StatIdle;
681d76d0 81 Dequeue();
a72ace20
AL
82 return;
83 }
7e5f33eb 84
93bf083d 85 Status = StatError;
681d76d0 86 Dequeue();
93bf083d 87 }
23c5897c 88
36280399 89 // report mirror failure back to LP if we actually use a mirror
f0b509cd
MV
90 string FailReason = LookupTag(Message, "FailReason");
91 if(FailReason.size() != 0)
92 ReportMirrorFailure(FailReason);
93 else
94 ReportMirrorFailure(ErrorText);
c88edf1d
AL
95}
96 /*}}}*/
8267fe24
AL
97// Acquire::Item::Start - Item has begun to download /*{{{*/
98// ---------------------------------------------------------------------
17caf1b1
AL
99/* Stash status and the file size. Note that setting Complete means
100 sub-phases of the acquire process such as decompresion are operating */
73da43e9 101void pkgAcquire::Item::Start(string /*Message*/,unsigned long long Size)
8267fe24
AL
102{
103 Status = StatFetching;
104 if (FileSize == 0 && Complete == false)
105 FileSize = Size;
106}
107 /*}}}*/
c88edf1d
AL
108// Acquire::Item::Done - Item downloaded OK /*{{{*/
109// ---------------------------------------------------------------------
110/* */
73da43e9 111void pkgAcquire::Item::Done(string Message,unsigned long long Size,string Hash,
459681d3 112 pkgAcquire::MethodConfig *Cnf)
c88edf1d 113{
b98f2859
AL
114 // We just downloaded something..
115 string FileName = LookupTag(Message,"Filename");
36280399 116 UsedMirror = LookupTag(Message,"UsedMirror");
8f30ca30 117 if (Complete == false && !Local && FileName == DestFile)
b98f2859
AL
118 {
119 if (Owner->Log != 0)
120 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
121 }
aa0e1101
AL
122
123 if (FileSize == 0)
124 FileSize= Size;
c88edf1d
AL
125 Status = StatDone;
126 ErrorText = string();
127 Owner->Dequeue(this);
128}
129 /*}}}*/
8b89e57f
AL
130// Acquire::Item::Rename - Rename a file /*{{{*/
131// ---------------------------------------------------------------------
132/* This helper function is used by alot of item methods as thier final
133 step */
134void pkgAcquire::Item::Rename(string From,string To)
135{
136 if (rename(From.c_str(),To.c_str()) != 0)
137 {
138 char S[300];
0fcd01de 139 snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
8b89e57f
AL
140 From.c_str(),To.c_str());
141 Status = StatError;
142 ErrorText = S;
7a3c2ab0 143 }
8b89e57f
AL
144}
145 /*}}}*/
3c8030a4
DK
146bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const error)/*{{{*/
147{
148 if(FileExists(DestFile))
149 Rename(DestFile, DestFile + ".FAILED");
150
151 switch (error)
152 {
153 case HashSumMismatch:
154 ErrorText = _("Hash Sum mismatch");
155 Status = StatAuthError;
156 ReportMirrorFailure("HashChecksumFailure");
157 break;
158 case SizeMismatch:
159 ErrorText = _("Size mismatch");
160 Status = StatAuthError;
161 ReportMirrorFailure("SizeFailure");
162 break;
163 case InvalidFormat:
164 ErrorText = _("Invalid file format");
165 Status = StatError;
166 // do not report as usually its not the mirrors fault, but Portal/Proxy
167 break;
168 }
169 return false;
170}
171 /*}}}*/
c91d9a63
DK
172// Acquire::Item::ReportMirrorFailure /*{{{*/
173// ---------------------------------------------------------------------
36280399
MV
174void pkgAcquire::Item::ReportMirrorFailure(string FailCode)
175{
59271f62
MV
176 // we only act if a mirror was used at all
177 if(UsedMirror.empty())
178 return;
36280399
MV
179#if 0
180 std::cerr << "\nReportMirrorFailure: "
181 << UsedMirror
59271f62 182 << " Uri: " << DescURI()
36280399
MV
183 << " FailCode: "
184 << FailCode << std::endl;
185#endif
186 const char *Args[40];
187 unsigned int i = 0;
188 string report = _config->Find("Methods::Mirror::ProblemReporting",
3f599bb7 189 "/usr/lib/apt/apt-report-mirror-failure");
36280399
MV
190 if(!FileExists(report))
191 return;
192 Args[i++] = report.c_str();
193 Args[i++] = UsedMirror.c_str();
f0b509cd 194 Args[i++] = DescURI().c_str();
36280399 195 Args[i++] = FailCode.c_str();
361593e9 196 Args[i++] = NULL;
36280399
MV
197 pid_t pid = ExecFork();
198 if(pid < 0)
199 {
200 _error->Error("ReportMirrorFailure Fork failed");
201 return;
202 }
203 else if(pid == 0)
204 {
361593e9
MV
205 execvp(Args[0], (char**)Args);
206 std::cerr << "Could not exec " << Args[0] << std::endl;
207 _exit(100);
36280399
MV
208 }
209 if(!ExecWait(pid, "report-mirror-failure"))
210 {
211 _error->Warning("Couldn't report problem to '%s'",
361593e9 212 _config->Find("Methods::Mirror::ProblemReporting").c_str());
36280399
MV
213 }
214}
c91d9a63 215 /*}}}*/
ab53c018
DK
216// AcqSubIndex::AcqSubIndex - Constructor /*{{{*/
217// ---------------------------------------------------------------------
8e3900d0
DK
218/* Get a sub-index file based on checksums from a 'master' file and
219 possibly query additional files */
ab53c018
DK
220pkgAcqSubIndex::pkgAcqSubIndex(pkgAcquire *Owner, string const &URI,
221 string const &URIDesc, string const &ShortDesc,
222 HashString const &ExpectedHash)
223 : Item(Owner), ExpectedHash(ExpectedHash)
224{
8e3900d0 225 /* XXX: Beware: Currently this class does nothing (of value) anymore ! */
ab53c018
DK
226 Debug = _config->FindB("Debug::pkgAcquire::SubIndex",false);
227
228 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
229 DestFile += URItoFileName(URI);
230
231 Desc.URI = URI;
232 Desc.Description = URIDesc;
233 Desc.Owner = this;
234 Desc.ShortDesc = ShortDesc;
235
236 QueueURI(Desc);
237
238 if(Debug)
239 std::clog << "pkgAcqSubIndex: " << Desc.URI << std::endl;
240}
241 /*}}}*/
242// AcqSubIndex::Custom600Headers - Insert custom request headers /*{{{*/
243// ---------------------------------------------------------------------
244/* The only header we use is the last-modified header. */
245string pkgAcqSubIndex::Custom600Headers()
246{
247 string Final = _config->FindDir("Dir::State::lists");
248 Final += URItoFileName(Desc.URI);
249
250 struct stat Buf;
251 if (stat(Final.c_str(),&Buf) != 0)
97b65b10
MV
252 return "\nIndex-File: true\nFail-Ignore: true\n";
253 return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
ab53c018
DK
254}
255 /*}}}*/
256void pkgAcqSubIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
257{
258 if(Debug)
259 std::clog << "pkgAcqSubIndex failed: " << Desc.URI << std::endl;
260
261 Complete = false;
262 Status = StatDone;
263 Dequeue();
264
8e3900d0 265 // No good Index is provided
ab53c018
DK
266}
267 /*}}}*/
73da43e9 268void pkgAcqSubIndex::Done(string Message,unsigned long long Size,string Md5Hash, /*{{{*/
ab53c018
DK
269 pkgAcquire::MethodConfig *Cnf)
270{
271 if(Debug)
272 std::clog << "pkgAcqSubIndex::Done(): " << Desc.URI << std::endl;
273
274 string FileName = LookupTag(Message,"Filename");
275 if (FileName.empty() == true)
276 {
277 Status = StatError;
278 ErrorText = "Method gave a blank filename";
279 return;
280 }
281
282 if (FileName != DestFile)
283 {
284 Local = true;
285 Desc.URI = "copy:" + FileName;
286 QueueURI(Desc);
287 return;
288 }
289
290 Item::Done(Message,Size,Md5Hash,Cnf);
291
292 string FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(Desc.URI);
293
4fdb6123 294 /* Downloaded invalid transindex => Error (LP: #346386) (Closes: #627642) */
0901c5d0
JAK
295 indexRecords SubIndexParser;
296 if (FileExists(DestFile) == true && !SubIndexParser.Load(DestFile)) {
297 Status = StatError;
298 ErrorText = SubIndexParser.ErrorText;
299 return;
300 }
301
ab53c018
DK
302 // sucess in downloading the index
303 // rename the index
304 if(Debug)
305 std::clog << "Renaming: " << DestFile << " -> " << FinalFile << std::endl;
306 Rename(DestFile,FinalFile);
307 chmod(FinalFile.c_str(),0644);
308 DestFile = FinalFile;
309
310 if(ParseIndex(DestFile) == false)
311 return Failed("", NULL);
312
313 Complete = true;
314 Status = StatDone;
315 Dequeue();
316 return;
317}
318 /*}}}*/
319bool pkgAcqSubIndex::ParseIndex(string const &IndexFile) /*{{{*/
320{
321 indexRecords SubIndexParser;
322 if (FileExists(IndexFile) == false || SubIndexParser.Load(IndexFile) == false)
323 return false;
8e3900d0 324 // so something with the downloaded index
ab53c018
DK
325 return true;
326}
327 /*}}}*/
92fcbfc1 328// AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
ac5b205a 329// ---------------------------------------------------------------------
2237bd01
MV
330/* Get the DiffIndex file first and see if there are patches availabe
331 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
332 * patches. If anything goes wrong in that process, it will fall back to
333 * the original packages file
ac5b205a 334 */
2237bd01
MV
335pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
336 string URI,string URIDesc,string ShortDesc,
495e5cb2
MV
337 HashString ExpectedHash)
338 : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash),
339 Description(URIDesc)
ac5b205a
MV
340{
341
ac5b205a
MV
342 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
343
2237bd01 344 Desc.Description = URIDesc + "/DiffIndex";
ac5b205a
MV
345 Desc.Owner = this;
346 Desc.ShortDesc = ShortDesc;
2237bd01
MV
347 Desc.URI = URI + ".diff/Index";
348
349 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
350 DestFile += URItoFileName(URI) + string(".DiffIndex");
351
352 if(Debug)
353 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
ac5b205a 354
2237bd01 355 // look for the current package file
ac5b205a
MV
356 CurrentPackagesFile = _config->FindDir("Dir::State::lists");
357 CurrentPackagesFile += URItoFileName(RealURI);
358
b4e57d2d
MV
359 // FIXME: this file:/ check is a hack to prevent fetching
360 // from local sources. this is really silly, and
361 // should be fixed cleanly as soon as possible
ac5b205a 362 if(!FileExists(CurrentPackagesFile) ||
81fcf9e2 363 Desc.URI.substr(0,strlen("file:/")) == "file:/")
2ac3eeb6 364 {
ac5b205a
MV
365 // we don't have a pkg file or we don't want to queue
366 if(Debug)
b4e57d2d 367 std::clog << "No index file, local or canceld by user" << std::endl;
ac5b205a
MV
368 Failed("", NULL);
369 return;
370 }
371
2ac3eeb6 372 if(Debug)
2237bd01
MV
373 std::clog << "pkgAcqIndexDiffs::pkgAcqIndexDiffs(): "
374 << CurrentPackagesFile << std::endl;
2ac3eeb6 375
ac5b205a 376 QueueURI(Desc);
2237bd01 377
ac5b205a 378}
92fcbfc1 379 /*}}}*/
6cb30d01
MV
380// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
381// ---------------------------------------------------------------------
382/* The only header we use is the last-modified header. */
2237bd01 383string pkgAcqDiffIndex::Custom600Headers()
6cb30d01 384{
6cb30d01
MV
385 string Final = _config->FindDir("Dir::State::lists");
386 Final += URItoFileName(RealURI) + string(".IndexDiff");
387
388 if(Debug)
389 std::clog << "Custom600Header-IMS: " << Final << std::endl;
390
391 struct stat Buf;
392 if (stat(Final.c_str(),&Buf) != 0)
393 return "\nIndex-File: true";
394
395 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
396}
92fcbfc1
DK
397 /*}}}*/
398bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) /*{{{*/
2237bd01
MV
399{
400 if(Debug)
401 std::clog << "pkgAcqIndexDiffs::ParseIndexDiff() " << IndexDiffFile
402 << std::endl;
403
404 pkgTagSection Tags;
405 string ServerSha1;
406 vector<DiffInfo> available_patches;
407
408 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
409 pkgTagFile TF(&Fd);
410 if (_error->PendingError() == true)
411 return false;
412
413 if(TF.Step(Tags) == true)
414 {
002d9943
MV
415 bool found = false;
416 DiffInfo d;
417 string size;
418
02dceb31 419 string const tmp = Tags.FindS("SHA1-Current");
2237bd01 420 std::stringstream ss(tmp);
02dceb31
DK
421 ss >> ServerSha1 >> size;
422 unsigned long const ServerSize = atol(size.c_str());
2237bd01 423
f213b6ea 424 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
2237bd01 425 SHA1Summation SHA1;
109eb151 426 SHA1.AddFD(fd);
02dceb31 427 string const local_sha1 = SHA1.Result();
2237bd01 428
2ac3eeb6
MV
429 if(local_sha1 == ServerSha1)
430 {
431 // we have the same sha1 as the server
2237bd01
MV
432 if(Debug)
433 std::clog << "Package file is up-to-date" << std::endl;
002d9943
MV
434 // set found to true, this will queue a pkgAcqIndexDiffs with
435 // a empty availabe_patches
436 found = true;
2ac3eeb6
MV
437 }
438 else
439 {
002d9943 440 if(Debug)
f213b6ea 441 std::clog << "SHA1-Current: " << ServerSha1 << " and we start at "<< fd.Name() << " " << fd.Size() << " " << local_sha1 << std::endl;
002d9943
MV
442
443 // check the historie and see what patches we need
02dceb31 444 string const history = Tags.FindS("SHA1-History");
002d9943 445 std::stringstream hist(history);
02dceb31 446 while(hist >> d.sha1 >> size >> d.file)
2ac3eeb6 447 {
002d9943 448 // read until the first match is found
02dceb31 449 // from that point on, we probably need all diffs
002d9943
MV
450 if(d.sha1 == local_sha1)
451 found=true;
02dceb31
DK
452 else if (found == false)
453 continue;
454
455 if(Debug)
456 std::clog << "Need to get diff: " << d.file << std::endl;
457 available_patches.push_back(d);
458 }
459
460 if (available_patches.empty() == false)
461 {
462 // patching with too many files is rather slow compared to a fast download
51fc6def 463 unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 20);
02dceb31
DK
464 if (fileLimit != 0 && fileLimit < available_patches.size())
465 {
466 if (Debug)
467 std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
468 << ") so fallback to complete download" << std::endl;
469 return false;
470 }
471
472 // see if the patches are too big
473 found = false; // it was true and it will be true again at the end
474 d = *available_patches.begin();
475 string const firstPatch = d.file;
476 unsigned long patchesSize = 0;
477 std::stringstream patches(Tags.FindS("SHA1-Patches"));
478 while(patches >> d.sha1 >> size >> d.file)
479 {
480 if (firstPatch == d.file)
481 found = true;
482 else if (found == false)
483 continue;
484
485 patchesSize += atol(size.c_str());
486 }
487 unsigned long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100);
488 if (sizeLimit > 0 && (sizeLimit/100) < patchesSize)
2ac3eeb6 489 {
02dceb31
DK
490 if (Debug)
491 std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100
492 << ") so fallback to complete download" << std::endl;
493 return false;
002d9943 494 }
2237bd01
MV
495 }
496 }
497
05aab406
MV
498 // we have something, queue the next diff
499 if(found)
2ac3eeb6 500 {
2237bd01 501 // queue the diffs
02dceb31 502 string::size_type const last_space = Description.rfind(" ");
05aab406
MV
503 if(last_space != string::npos)
504 Description.erase(last_space, Description.size()-last_space);
2237bd01 505 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
8a3207f4 506 ExpectedHash, ServerSha1, available_patches);
2237bd01
MV
507 Complete = false;
508 Status = StatDone;
509 Dequeue();
510 return true;
511 }
512 }
05aab406
MV
513
514 // Nothing found, report and return false
515 // Failing here is ok, if we return false later, the full
516 // IndexFile is queued
517 if(Debug)
518 std::clog << "Can't find a patch in the index file" << std::endl;
2237bd01
MV
519 return false;
520}
92fcbfc1
DK
521 /*}}}*/
522void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
2237bd01
MV
523{
524 if(Debug)
525 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << std::endl
526 << "Falling back to normal index file aquire" << std::endl;
527
002d9943 528 new pkgAcqIndex(Owner, RealURI, Description, Desc.ShortDesc,
495e5cb2 529 ExpectedHash);
2237bd01
MV
530
531 Complete = false;
532 Status = StatDone;
533 Dequeue();
534}
92fcbfc1 535 /*}}}*/
73da43e9 536void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,string Md5Hash, /*{{{*/
2237bd01
MV
537 pkgAcquire::MethodConfig *Cnf)
538{
539 if(Debug)
540 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
541
542 Item::Done(Message,Size,Md5Hash,Cnf);
543
544 string FinalFile;
545 FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
546
547 // sucess in downloading the index
548 // rename the index
549 FinalFile += string(".IndexDiff");
550 if(Debug)
551 std::clog << "Renaming: " << DestFile << " -> " << FinalFile
552 << std::endl;
553 Rename(DestFile,FinalFile);
554 chmod(FinalFile.c_str(),0644);
555 DestFile = FinalFile;
556
557 if(!ParseDiffIndex(DestFile))
558 return Failed("", NULL);
559
560 Complete = true;
561 Status = StatDone;
562 Dequeue();
563 return;
564}
92fcbfc1
DK
565 /*}}}*/
566// AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
2237bd01
MV
567// ---------------------------------------------------------------------
568/* The package diff is added to the queue. one object is constructed
569 * for each diff and the index
570 */
571pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
572 string URI,string URIDesc,string ShortDesc,
59d5cc74 573 HashString ExpectedHash,
8a3207f4 574 string ServerSha1,
495e5cb2
MV
575 vector<DiffInfo> diffs)
576 : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash),
8a3207f4 577 available_patches(diffs), ServerSha1(ServerSha1)
2237bd01
MV
578{
579
580 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
581 DestFile += URItoFileName(URI);
582
583 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
584
05aab406 585 Description = URIDesc;
2237bd01
MV
586 Desc.Owner = this;
587 Desc.ShortDesc = ShortDesc;
588
69c2ecbd 589 if(available_patches.empty() == true)
2ac3eeb6 590 {
2237bd01
MV
591 // we are done (yeah!)
592 Finish(true);
2ac3eeb6
MV
593 }
594 else
595 {
2237bd01
MV
596 // get the next diff
597 State = StateFetchDiff;
598 QueueNextDiff();
599 }
600}
92fcbfc1
DK
601 /*}}}*/
602void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
ac5b205a 603{
2237bd01
MV
604 if(Debug)
605 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << std::endl
606 << "Falling back to normal index file aquire" << std::endl;
607 new pkgAcqIndex(Owner, RealURI, Description,Desc.ShortDesc,
495e5cb2 608 ExpectedHash);
ac5b205a
MV
609 Finish();
610}
92fcbfc1
DK
611 /*}}}*/
612// Finish - helper that cleans the item out of the fetcher queue /*{{{*/
ac5b205a
MV
613void pkgAcqIndexDiffs::Finish(bool allDone)
614{
615 // we restore the original name, this is required, otherwise
616 // the file will be cleaned
2ac3eeb6
MV
617 if(allDone)
618 {
ac5b205a
MV
619 DestFile = _config->FindDir("Dir::State::lists");
620 DestFile += URItoFileName(RealURI);
2d4722e2 621
495e5cb2 622 if(!ExpectedHash.empty() && !ExpectedHash.VerifyFile(DestFile))
2d4722e2 623 {
3c8030a4 624 RenameOnError(HashSumMismatch);
2d4722e2
MV
625 Dequeue();
626 return;
627 }
628
629 // this is for the "real" finish
ac5b205a 630 Complete = true;
cffc2ddd 631 Status = StatDone;
ac5b205a
MV
632 Dequeue();
633 if(Debug)
634 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
635 return;
ac5b205a
MV
636 }
637
638 if(Debug)
639 std::clog << "Finishing: " << Desc.URI << std::endl;
640 Complete = false;
641 Status = StatDone;
642 Dequeue();
643 return;
644}
92fcbfc1
DK
645 /*}}}*/
646bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
ac5b205a 647{
3de9ff77 648
94dc9d7d
MV
649 // calc sha1 of the just patched file
650 string FinalFile = _config->FindDir("Dir::State::lists");
651 FinalFile += URItoFileName(RealURI);
652
f213b6ea 653 FileFd fd(FinalFile, FileFd::ReadOnly);
94dc9d7d 654 SHA1Summation SHA1;
109eb151 655 SHA1.AddFD(fd);
94dc9d7d 656 string local_sha1 = string(SHA1.Result());
3de9ff77
MV
657 if(Debug)
658 std::clog << "QueueNextDiff: "
659 << FinalFile << " (" << local_sha1 << ")"<<std::endl;
94dc9d7d 660
8a3207f4
DK
661 // final file reached before all patches are applied
662 if(local_sha1 == ServerSha1)
663 {
664 Finish(true);
665 return true;
666 }
667
26d27645
MV
668 // remove all patches until the next matching patch is found
669 // this requires the Index file to be ordered
94dc9d7d 670 for(vector<DiffInfo>::iterator I=available_patches.begin();
f7f0d6c7 671 available_patches.empty() == false &&
2ac3eeb6 672 I != available_patches.end() &&
f7f0d6c7
DK
673 I->sha1 != local_sha1;
674 ++I)
2ac3eeb6 675 {
26d27645 676 available_patches.erase(I);
59a704f0 677 }
94dc9d7d
MV
678
679 // error checking and falling back if no patch was found
f7f0d6c7
DK
680 if(available_patches.empty() == true)
681 {
94dc9d7d
MV
682 Failed("", NULL);
683 return false;
684 }
6cb30d01 685
94dc9d7d 686 // queue the right diff
2237bd01 687 Desc.URI = string(RealURI) + ".diff/" + available_patches[0].file + ".gz";
05aab406 688 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
ac5b205a 689 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
2237bd01 690 DestFile += URItoFileName(RealURI + ".diff/" + available_patches[0].file);
ac5b205a
MV
691
692 if(Debug)
693 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
694
695 QueueURI(Desc);
696
697 return true;
698}
92fcbfc1 699 /*}}}*/
73da43e9 700void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size,string Md5Hash, /*{{{*/
ac5b205a
MV
701 pkgAcquire::MethodConfig *Cnf)
702{
703 if(Debug)
704 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
705
706 Item::Done(Message,Size,Md5Hash,Cnf);
707
4a0a786f
MV
708 string FinalFile;
709 FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
6cb30d01 710
4a0a786f 711 // sucess in downloading a diff, enter ApplyDiff state
caffd480 712 if(State == StateFetchDiff)
4a0a786f
MV
713 {
714
715 // rred excepts the patch as $FinalFile.ed
716 Rename(DestFile,FinalFile+".ed");
717
718 if(Debug)
719 std::clog << "Sending to rred method: " << FinalFile << std::endl;
720
721 State = StateApplyDiff;
b7347826 722 Local = true;
4a0a786f
MV
723 Desc.URI = "rred:" + FinalFile;
724 QueueURI(Desc);
725 Mode = "rred";
726 return;
727 }
728
729
730 // success in download/apply a diff, queue next (if needed)
731 if(State == StateApplyDiff)
732 {
733 // remove the just applied patch
94dc9d7d 734 available_patches.erase(available_patches.begin());
ac5b205a 735
4a0a786f 736 // move into place
59a704f0
MV
737 if(Debug)
738 {
4a0a786f
MV
739 std::clog << "Moving patched file in place: " << std::endl
740 << DestFile << " -> " << FinalFile << std::endl;
59a704f0 741 }
4a0a786f 742 Rename(DestFile,FinalFile);
1790e0cf 743 chmod(FinalFile.c_str(),0644);
4a0a786f
MV
744
745 // see if there is more to download
f7f0d6c7 746 if(available_patches.empty() == false) {
ac5b205a 747 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
8a3207f4 748 ExpectedHash, ServerSha1, available_patches);
4a0a786f
MV
749 return Finish();
750 } else
751 return Finish(true);
ac5b205a 752 }
ac5b205a 753}
92fcbfc1 754 /*}}}*/
0118833a
AL
755// AcqIndex::AcqIndex - Constructor /*{{{*/
756// ---------------------------------------------------------------------
757/* The package file is added to the queue and a second class is
b2e465d6
AL
758 instantiated to fetch the revision file */
759pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
b3d44315 760 string URI,string URIDesc,string ShortDesc,
495e5cb2
MV
761 HashString ExpectedHash, string comprExt)
762 : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash)
0118833a 763{
5d885723
DK
764 if(comprExt.empty() == true)
765 {
766 // autoselect the compression method
767 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
768 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
769 comprExt.append(*t).append(" ");
770 if (comprExt.empty() == false)
771 comprExt.erase(comprExt.end()-1);
772 }
773 CompressionExtension = comprExt;
774
775 Init(URI, URIDesc, ShortDesc);
776}
777pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, IndexTarget const *Target,
778 HashString const &ExpectedHash, indexRecords const *MetaIndexParser)
779 : Item(Owner), RealURI(Target->URI), ExpectedHash(ExpectedHash)
780{
781 // autoselect the compression method
782 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
783 CompressionExtension = "";
784 if (ExpectedHash.empty() == false)
785 {
786 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
787 if (*t == "uncompressed" || MetaIndexParser->Exists(string(Target->MetaKey).append(".").append(*t)) == true)
788 CompressionExtension.append(*t).append(" ");
789 }
790 else
791 {
792 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
793 CompressionExtension.append(*t).append(" ");
794 }
795 if (CompressionExtension.empty() == false)
796 CompressionExtension.erase(CompressionExtension.end()-1);
797
97efc27f
MV
798 // only verify non-optional targets, see acquire-item.h for a FIXME
799 // to make this more flexible
c5f661b7
MV
800 if (Target->IsOptional())
801 Verify = false;
97efc27f
MV
802 else
803 Verify = true;
c5f661b7 804
5d885723
DK
805 Init(Target->URI, Target->Description, Target->ShortDesc);
806}
807 /*}}}*/
808// AcqIndex::Init - defered Constructor /*{{{*/
809void pkgAcqIndex::Init(string const &URI, string const &URIDesc, string const &ShortDesc) {
8b89e57f 810 Decompression = false;
bfd22fc0 811 Erase = false;
13e8426f 812
0a8a80e5 813 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
b2e465d6 814 DestFile += URItoFileName(URI);
8267fe24 815
5d885723
DK
816 std::string const comprExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
817 if (comprExt == "uncompressed")
818 Desc.URI = URI;
819 else
820 Desc.URI = URI + '.' + comprExt;
b3d44315 821
b2e465d6 822 Desc.Description = URIDesc;
8267fe24 823 Desc.Owner = this;
b2e465d6 824 Desc.ShortDesc = ShortDesc;
5d885723 825
8267fe24 826 QueueURI(Desc);
0118833a
AL
827}
828 /*}}}*/
0a8a80e5 829// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 830// ---------------------------------------------------------------------
0a8a80e5
AL
831/* The only header we use is the last-modified header. */
832string pkgAcqIndex::Custom600Headers()
0118833a 833{
0a8a80e5 834 string Final = _config->FindDir("Dir::State::lists");
b2e465d6 835 Final += URItoFileName(RealURI);
01606def 836 if (_config->FindB("Acquire::GzipIndexes",false))
837 Final += ".gz";
0a8a80e5 838
97b65b10
MV
839 string msg = "\nIndex-File: true";
840 // FIXME: this really should use "IndexTarget::IsOptional()" but that
841 // seems to be difficult without breaking ABI
842 if (ShortDesc().find("Translation") != 0)
843 msg += "\nFail-Ignore: true";
0a8a80e5 844 struct stat Buf;
3a1f49c4 845 if (stat(Final.c_str(),&Buf) == 0)
97b65b10
MV
846 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
847
848 return msg;
0118833a
AL
849}
850 /*}}}*/
92fcbfc1 851void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
debc84b2 852{
5d885723
DK
853 size_t const nextExt = CompressionExtension.find(' ');
854 if (nextExt != std::string::npos)
e85b4cd5 855 {
5d885723
DK
856 CompressionExtension = CompressionExtension.substr(nextExt+1);
857 Init(RealURI, Desc.Description, Desc.ShortDesc);
6abe2699 858 return;
0d7a243d
EL
859 }
860
17ff0930 861 // on decompression failure, remove bad versions in partial/
5d885723 862 if (Decompression && Erase) {
17ff0930 863 string s = _config->FindDir("Dir::State::lists") + "partial/";
5d885723 864 s.append(URItoFileName(RealURI));
17ff0930 865 unlink(s.c_str());
debc84b2
MZ
866 }
867
debc84b2
MZ
868 Item::Failed(Message,Cnf);
869}
92fcbfc1 870 /*}}}*/
8b89e57f
AL
871// AcqIndex::Done - Finished a fetch /*{{{*/
872// ---------------------------------------------------------------------
873/* This goes through a number of states.. On the initial fetch the
874 method could possibly return an alternate filename which points
875 to the uncompressed version of the file. If this is so the file
876 is copied into the partial directory. In all other cases the file
877 is decompressed with a gzip uri. */
73da43e9 878void pkgAcqIndex::Done(string Message,unsigned long long Size,string Hash,
459681d3 879 pkgAcquire::MethodConfig *Cfg)
8b89e57f 880{
495e5cb2 881 Item::Done(Message,Size,Hash,Cfg);
8b89e57f
AL
882
883 if (Decompression == true)
884 {
b3d44315
MV
885 if (_config->FindB("Debug::pkgAcquire::Auth", false))
886 {
495e5cb2
MV
887 std::cerr << std::endl << RealURI << ": Computed Hash: " << Hash;
888 std::cerr << " Expected Hash: " << ExpectedHash.toStr() << std::endl;
b3d44315
MV
889 }
890
8a8feb29 891 if (!ExpectedHash.empty() && ExpectedHash.toStr() != Hash)
b3d44315 892 {
3c8030a4 893 RenameOnError(HashSumMismatch);
b3d44315
MV
894 return;
895 }
0901c5d0
JAK
896
897 /* Verify the index file for correctness (all indexes must
4fdb6123 898 * have a Package field) (LP: #346386) (Closes: #627642) */
c5f661b7 899 if (Verify == true)
0901c5d0
JAK
900 {
901 FileFd fd(DestFile, FileFd::ReadOnly);
3c8030a4
DK
902 // Only test for correctness if the file is not empty (empty is ok)
903 if (fd.FileSize() > 0)
904 {
905 pkgTagSection sec;
906 pkgTagFile tag(&fd);
907
908 // all our current indexes have a field 'Package' in each section
909 if (_error->PendingError() == true || tag.Step(sec) == false || sec.Exists("Package") == false)
910 {
911 RenameOnError(InvalidFormat);
912 return;
913 }
a0c3110e 914 }
0901c5d0
JAK
915 }
916
8b89e57f
AL
917 // Done, move it into position
918 string FinalFile = _config->FindDir("Dir::State::lists");
b2e465d6 919 FinalFile += URItoFileName(RealURI);
8b89e57f 920 Rename(DestFile,FinalFile);
7a3c2ab0 921 chmod(FinalFile.c_str(),0644);
bfd22fc0 922
7a7fa5f0
AL
923 /* We restore the original name to DestFile so that the clean operation
924 will work OK */
925 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
b2e465d6 926 DestFile += URItoFileName(RealURI);
7a7fa5f0 927
bfd22fc0
AL
928 // Remove the compressed version.
929 if (Erase == true)
bfd22fc0 930 unlink(DestFile.c_str());
8b89e57f
AL
931 return;
932 }
bfd22fc0
AL
933
934 Erase = false;
8267fe24 935 Complete = true;
bfd22fc0 936
8b89e57f
AL
937 // Handle the unzipd case
938 string FileName = LookupTag(Message,"Alt-Filename");
939 if (FileName.empty() == false)
940 {
941 // The files timestamp matches
942 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
943 return;
8b89e57f 944 Decompression = true;
a6568219 945 Local = true;
8b89e57f 946 DestFile += ".decomp";
8267fe24
AL
947 Desc.URI = "copy:" + FileName;
948 QueueURI(Desc);
b98f2859 949 Mode = "copy";
8b89e57f
AL
950 return;
951 }
952
953 FileName = LookupTag(Message,"Filename");
954 if (FileName.empty() == true)
955 {
956 Status = StatError;
957 ErrorText = "Method gave a blank filename";
958 }
5d885723
DK
959
960 std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
0b9032b1 961
8b89e57f 962 // The files timestamp matches
0b9032b1 963 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true) {
964 if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
965 // Update DestFile for .gz suffix so that the clean operation keeps it
966 DestFile += ".gz";
8b89e57f 967 return;
0b9032b1 968 }
bfd22fc0
AL
969
970 if (FileName == DestFile)
971 Erase = true;
8267fe24 972 else
a6568219 973 Local = true;
8b89e57f 974
e85b4cd5
DK
975 string decompProg;
976
bb109d0b 977 // If we enable compressed indexes and already have gzip, keep it
7aeee365 978 if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz" && !Local) {
bb109d0b 979 string FinalFile = _config->FindDir("Dir::State::lists");
980 FinalFile += URItoFileName(RealURI) + ".gz";
bb109d0b 981 Rename(DestFile,FinalFile);
982 chmod(FinalFile.c_str(),0644);
983
984 // Update DestFile for .gz suffix so that the clean operation keeps it
985 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
986 DestFile += URItoFileName(RealURI) + ".gz";
987 return;
988 }
989
e85b4cd5
DK
990 // get the binary name for your used compression type
991 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),"");
992 if(decompProg.empty() == false);
5d885723 993 else if(compExt == "uncompressed")
0d7a243d 994 decompProg = "copy";
debc84b2
MZ
995 else {
996 _error->Error("Unsupported extension: %s", compExt.c_str());
997 return;
998 }
999
8b89e57f
AL
1000 Decompression = true;
1001 DestFile += ".decomp";
e85b4cd5 1002 Desc.URI = decompProg + ":" + FileName;
8267fe24 1003 QueueURI(Desc);
70e0c168
MV
1004
1005 // FIXME: this points to a c++ string that goes out of scope
e85b4cd5 1006 Mode = decompProg.c_str();
8b89e57f 1007}
92fcbfc1 1008 /*}}}*/
a52f938b
OS
1009// AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
1010// ---------------------------------------------------------------------
1011/* The Translation file is added to the queue */
1012pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
495e5cb2
MV
1013 string URI,string URIDesc,string ShortDesc)
1014 : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashString(), "")
a52f938b 1015{
ab53c018
DK
1016}
1017pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, IndexTarget const *Target,
1018 HashString const &ExpectedHash, indexRecords const *MetaIndexParser)
1019 : pkgAcqIndex(Owner, Target, ExpectedHash, MetaIndexParser)
1020{
963b16dc
MV
1021}
1022 /*}}}*/
1023// AcqIndexTrans::Custom600Headers - Insert custom request headers /*{{{*/
1024// ---------------------------------------------------------------------
1025string pkgAcqIndexTrans::Custom600Headers()
1026{
c91d9a63
DK
1027 string Final = _config->FindDir("Dir::State::lists");
1028 Final += URItoFileName(RealURI);
1029
1030 struct stat Buf;
1031 if (stat(Final.c_str(),&Buf) != 0)
a3f7fff8
MV
1032 return "\nFail-Ignore: true\nIndex-File: true";
1033 return "\nFail-Ignore: true\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
a52f938b 1034}
a52f938b
OS
1035 /*}}}*/
1036// AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
1037// ---------------------------------------------------------------------
1038/* */
1039void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1040{
5d885723
DK
1041 size_t const nextExt = CompressionExtension.find(' ');
1042 if (nextExt != std::string::npos)
1043 {
1044 CompressionExtension = CompressionExtension.substr(nextExt+1);
1045 Init(RealURI, Desc.Description, Desc.ShortDesc);
1046 Status = StatIdle;
1047 return;
1048 }
1049
a52f938b
OS
1050 if (Cnf->LocalOnly == true ||
1051 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1052 {
1053 // Ignore this
1054 Status = StatDone;
1055 Complete = false;
1056 Dequeue();
1057 return;
1058 }
5d885723 1059
a52f938b
OS
1060 Item::Failed(Message,Cnf);
1061}
1062 /*}}}*/
92fcbfc1 1063pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, /*{{{*/
b3d44315
MV
1064 string URI,string URIDesc,string ShortDesc,
1065 string MetaIndexURI, string MetaIndexURIDesc,
1066 string MetaIndexShortDesc,
1067 const vector<IndexTarget*>* IndexTargets,
1068 indexRecords* MetaIndexParser) :
1069 Item(Owner), RealURI(URI), MetaIndexURI(MetaIndexURI),
46e00f9d
MV
1070 MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
1071 MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
0118833a 1072{
0a8a80e5 1073 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
b2e465d6 1074 DestFile += URItoFileName(URI);
b3d44315 1075
47eb38f4
MV
1076 // remove any partial downloaded sig-file in partial/.
1077 // it may confuse proxies and is too small to warrant a
1078 // partial download anyway
f6237efd
MV
1079 unlink(DestFile.c_str());
1080
8267fe24 1081 // Create the item
b2e465d6 1082 Desc.Description = URIDesc;
8267fe24 1083 Desc.Owner = this;
b3d44315
MV
1084 Desc.ShortDesc = ShortDesc;
1085 Desc.URI = URI;
b3d44315
MV
1086
1087 string Final = _config->FindDir("Dir::State::lists");
1088 Final += URItoFileName(RealURI);
ffcccd62 1089 if (RealFileExists(Final) == true)
b3d44315 1090 {
ef942597
MV
1091 // File was already in place. It needs to be re-downloaded/verified
1092 // because Release might have changed, we do give it a differnt
1093 // name than DestFile because otherwise the http method will
1094 // send If-Range requests and there are too many broken servers
1095 // out there that do not understand them
1096 LastGoodSig = DestFile+".reverify";
1097 Rename(Final,LastGoodSig);
b3d44315 1098 }
8267fe24 1099
8267fe24 1100 QueueURI(Desc);
ffcccd62
DK
1101}
1102 /*}}}*/
1103pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
1104{
1105 // if the file was never queued undo file-changes done in the constructor
1106 if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false &&
1107 LastGoodSig.empty() == false)
1108 {
1109 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1110 if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true)
1111 Rename(LastGoodSig, Final);
1112 }
1113
0118833a
AL
1114}
1115 /*}}}*/
b3d44315 1116// pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 1117// ---------------------------------------------------------------------
0a8a80e5 1118/* The only header we use is the last-modified header. */
b3d44315 1119string pkgAcqMetaSig::Custom600Headers()
0118833a 1120{
0a8a80e5 1121 struct stat Buf;
ef942597 1122 if (stat(LastGoodSig.c_str(),&Buf) != 0)
a72ace20 1123 return "\nIndex-File: true";
a789b983 1124
a72ace20 1125 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
0118833a 1126}
b3d44315 1127
73da43e9 1128void pkgAcqMetaSig::Done(string Message,unsigned long long Size,string MD5,
b3d44315 1129 pkgAcquire::MethodConfig *Cfg)
c88edf1d 1130{
459681d3 1131 Item::Done(Message,Size,MD5,Cfg);
c88edf1d
AL
1132
1133 string FileName = LookupTag(Message,"Filename");
1134 if (FileName.empty() == true)
1135 {
1136 Status = StatError;
1137 ErrorText = "Method gave a blank filename";
8b89e57f 1138 return;
c88edf1d 1139 }
8b89e57f 1140
c88edf1d
AL
1141 if (FileName != DestFile)
1142 {
b3d44315 1143 // We have to copy it into place
a6568219 1144 Local = true;
8267fe24
AL
1145 Desc.URI = "copy:" + FileName;
1146 QueueURI(Desc);
c88edf1d
AL
1147 return;
1148 }
b3d44315
MV
1149
1150 Complete = true;
1151
ef942597
MV
1152 // put the last known good file back on i-m-s hit (it will
1153 // be re-verified again)
1154 // Else do nothing, we have the new file in DestFile then
1155 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1156 Rename(LastGoodSig, DestFile);
1157
b3d44315 1158 // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
fce72602
MV
1159 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc,
1160 MetaIndexShortDesc, DestFile, IndexTargets,
1161 MetaIndexParser);
b3d44315 1162
c88edf1d
AL
1163}
1164 /*}}}*/
92fcbfc1 1165void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
681d76d0 1166{
47eb38f4 1167 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
a789b983 1168
75dd8af1 1169 // if we get a network error we fail gracefully
7e5f33eb
MV
1170 if(Status == StatTransientNetworkError)
1171 {
24057ad6 1172 Item::Failed(Message,Cnf);
484dbb81 1173 // move the sigfile back on transient network failures
7730e095 1174 if(FileExists(LastGoodSig))
ef942597 1175 Rename(LastGoodSig,Final);
7e5f33eb
MV
1176
1177 // set the status back to , Item::Failed likes to reset it
1178 Status = pkgAcquire::Item::StatTransientNetworkError;
24057ad6
MV
1179 return;
1180 }
1181
75dd8af1 1182 // Delete any existing sigfile when the acquire failed
75dd8af1
MV
1183 unlink(Final.c_str());
1184
b3d44315
MV
1185 // queue a pkgAcqMetaIndex with no sigfile
1186 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
1187 "", IndexTargets, MetaIndexParser);
1188
681d76d0
AL
1189 if (Cnf->LocalOnly == true ||
1190 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1191 {
2b154e53
AL
1192 // Ignore this
1193 Status = StatDone;
1194 Complete = false;
681d76d0
AL
1195 Dequeue();
1196 return;
1197 }
1198
1199 Item::Failed(Message,Cnf);
1200}
92fcbfc1
DK
1201 /*}}}*/
1202pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
b3d44315
MV
1203 string URI,string URIDesc,string ShortDesc,
1204 string SigFile,
1205 const vector<struct IndexTarget*>* IndexTargets,
1206 indexRecords* MetaIndexParser) :
21fd1746
OS
1207 Item(Owner), RealURI(URI), SigFile(SigFile), IndexTargets(IndexTargets),
1208 MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false)
b3d44315 1209{
b3d44315
MV
1210 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1211 DestFile += URItoFileName(URI);
1212
1213 // Create the item
1214 Desc.Description = URIDesc;
1215 Desc.Owner = this;
1216 Desc.ShortDesc = ShortDesc;
1217 Desc.URI = URI;
1218
1219 QueueURI(Desc);
1220}
b3d44315
MV
1221 /*}}}*/
1222// pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
1223// ---------------------------------------------------------------------
1224/* The only header we use is the last-modified header. */
1225string pkgAcqMetaIndex::Custom600Headers()
1226{
1227 string Final = _config->FindDir("Dir::State::lists");
1228 Final += URItoFileName(RealURI);
1229
1230 struct stat Buf;
1231 if (stat(Final.c_str(),&Buf) != 0)
1232 return "\nIndex-File: true";
1233
1234 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1235}
92fcbfc1 1236 /*}}}*/
73da43e9 1237void pkgAcqMetaIndex::Done(string Message,unsigned long long Size,string Hash, /*{{{*/
b3d44315
MV
1238 pkgAcquire::MethodConfig *Cfg)
1239{
495e5cb2 1240 Item::Done(Message,Size,Hash,Cfg);
b3d44315
MV
1241
1242 // MetaIndexes are done in two passes: one to download the
1243 // metaindex with an appropriate method, and a second to verify it
1244 // with the gpgv method
1245
1246 if (AuthPass == true)
1247 {
1248 AuthDone(Message);
fce72602
MV
1249
1250 // all cool, move Release file into place
1251 Complete = true;
b3d44315
MV
1252 }
1253 else
1254 {
1255 RetrievalDone(Message);
1256 if (!Complete)
1257 // Still more retrieving to do
1258 return;
1259
1260 if (SigFile == "")
1261 {
1262 // There was no signature file, so we are finished. Download
1207cf3f 1263 // the indexes and do only hashsum verification if possible
3568a640 1264 MetaIndexParser->Load(DestFile);
1207cf3f 1265 QueueIndexes(false);
b3d44315
MV
1266 }
1267 else
1268 {
1269 // There was a signature file, so pass it to gpgv for
1270 // verification
1271
1272 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1273 std::cerr << "Metaindex acquired, queueing gpg verification ("
1274 << SigFile << "," << DestFile << ")\n";
1275 AuthPass = true;
1276 Desc.URI = "gpgv:" + SigFile;
1277 QueueURI(Desc);
1278 Mode = "gpgv";
56bc3358 1279 return;
b3d44315
MV
1280 }
1281 }
56bc3358
DK
1282
1283 if (Complete == true)
1284 {
1285 string FinalFile = _config->FindDir("Dir::State::lists");
1286 FinalFile += URItoFileName(RealURI);
fe0f7911
DK
1287 if (SigFile == DestFile)
1288 SigFile = FinalFile;
56bc3358
DK
1289 Rename(DestFile,FinalFile);
1290 chmod(FinalFile.c_str(),0644);
1291 DestFile = FinalFile;
1292 }
b3d44315 1293}
92fcbfc1
DK
1294 /*}}}*/
1295void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/
b3d44315
MV
1296{
1297 // We have just finished downloading a Release file (it is not
1298 // verified yet)
1299
1300 string FileName = LookupTag(Message,"Filename");
1301 if (FileName.empty() == true)
1302 {
1303 Status = StatError;
1304 ErrorText = "Method gave a blank filename";
1305 return;
1306 }
1307
1308 if (FileName != DestFile)
1309 {
1310 Local = true;
1311 Desc.URI = "copy:" + FileName;
1312 QueueURI(Desc);
1313 return;
1314 }
1315
fce72602 1316 // make sure to verify against the right file on I-M-S hit
f381d68d 1317 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
fce72602
MV
1318 if(IMSHit)
1319 {
1320 string FinalFile = _config->FindDir("Dir::State::lists");
1321 FinalFile += URItoFileName(RealURI);
fe0f7911 1322 if (SigFile == DestFile)
0aec7d5c 1323 {
fe0f7911 1324 SigFile = FinalFile;
0aec7d5c
DK
1325 // constructor of pkgAcqMetaClearSig moved it out of the way,
1326 // now move it back in on IMS hit for the 'old' file
1327 string const OldClearSig = DestFile + ".reverify";
1328 if (RealFileExists(OldClearSig) == true)
1329 Rename(OldClearSig, FinalFile);
1330 }
fce72602
MV
1331 DestFile = FinalFile;
1332 }
b3d44315 1333 Complete = true;
b3d44315 1334}
92fcbfc1
DK
1335 /*}}}*/
1336void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/
b3d44315
MV
1337{
1338 // At this point, the gpgv method has succeeded, so there is a
1339 // valid signature from a key in the trusted keyring. We
1340 // perform additional verification of its contents, and use them
1341 // to verify the indexes we are about to download
1342
1343 if (!MetaIndexParser->Load(DestFile))
1344 {
1345 Status = StatAuthError;
1346 ErrorText = MetaIndexParser->ErrorText;
1347 return;
1348 }
1349
ce424cd4 1350 if (!VerifyVendor(Message))
b3d44315
MV
1351 {
1352 return;
1353 }
1354
1355 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1356 std::cerr << "Signature verification succeeded: "
1357 << DestFile << std::endl;
1358
1359 // Download further indexes with verification
1360 QueueIndexes(true);
1361
fe0f7911
DK
1362 // is it a clearsigned MetaIndex file?
1363 if (DestFile == SigFile)
1364 return;
1365
b3d44315 1366 // Done, move signature file into position
b3d44315
MV
1367 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1368 URItoFileName(RealURI) + ".gpg";
1369 Rename(SigFile,VerifiedSigFile);
1370 chmod(VerifiedSigFile.c_str(),0644);
1371}
92fcbfc1
DK
1372 /*}}}*/
1373void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/
b3d44315 1374{
0901c5d0 1375#if 0
4fdb6123 1376 /* Reject invalid, existing Release files (LP: #346386) (Closes: #627642)
0901c5d0
JAK
1377 * FIXME: Disabled; it breaks unsigned repositories without hashes */
1378 if (!verify && FileExists(DestFile) && !MetaIndexParser->Load(DestFile))
1379 {
1380 Status = StatError;
1381 ErrorText = MetaIndexParser->ErrorText;
1382 return;
1383 }
1384#endif
8e3900d0
DK
1385 bool transInRelease = false;
1386 {
1387 std::vector<std::string> const keys = MetaIndexParser->MetaKeys();
1388 for (std::vector<std::string>::const_iterator k = keys.begin(); k != keys.end(); ++k)
1389 // FIXME: Feels wrong to check for hardcoded string here, but what should we do else…
1390 if (k->find("Translation-") != std::string::npos)
1391 {
1392 transInRelease = true;
1393 break;
1394 }
1395 }
1396
b3d44315
MV
1397 for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
1398 Target != IndexTargets->end();
f7f0d6c7 1399 ++Target)
b3d44315 1400 {
495e5cb2 1401 HashString ExpectedIndexHash;
1207cf3f 1402 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
a5b9f489 1403 bool compressedAvailable = false;
1207cf3f 1404 if (Record == NULL)
b3d44315 1405 {
a5b9f489
DK
1406 if ((*Target)->IsOptional() == true)
1407 {
1408 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
1409 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
1410 if (MetaIndexParser->Exists(string((*Target)->MetaKey).append(".").append(*t)) == true)
1411 {
1412 compressedAvailable = true;
1413 break;
1414 }
1415 }
1416 else if (verify == true)
ab53c018 1417 {
1207cf3f
DK
1418 Status = StatAuthError;
1419 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str());
1420 return;
ab53c018 1421 }
1207cf3f
DK
1422 }
1423 else
1424 {
1425 ExpectedIndexHash = Record->Hash;
1426 if (_config->FindB("Debug::pkgAcquire::Auth", false))
ab53c018 1427 {
1207cf3f
DK
1428 std::cerr << "Queueing: " << (*Target)->URI << std::endl;
1429 std::cerr << "Expected Hash: " << ExpectedIndexHash.toStr() << std::endl;
1430 std::cerr << "For: " << Record->MetaKeyFilename << std::endl;
1431 }
1432 if (verify == true && ExpectedIndexHash.empty() == true && (*Target)->IsOptional() == false)
1433 {
1434 Status = StatAuthError;
1435 strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str());
1436 return;
ab53c018
DK
1437 }
1438 }
1439
1440 if ((*Target)->IsOptional() == true)
1441 {
1442 if ((*Target)->IsSubIndex() == true)
1443 new pkgAcqSubIndex(Owner, (*Target)->URI, (*Target)->Description,
1444 (*Target)->ShortDesc, ExpectedIndexHash);
a5b9f489 1445 else if (transInRelease == false || Record != NULL || compressedAvailable == true)
8e3900d0 1446 {
f55602cb
DK
1447 if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true &&
1448 MetaIndexParser->Exists(string((*Target)->MetaKey).append(".diff/Index")) == true)
1449 new pkgAcqDiffIndex(Owner, (*Target)->URI, (*Target)->Description,
1450 (*Target)->ShortDesc, ExpectedIndexHash);
1451 else
1452 new pkgAcqIndexTrans(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
8e3900d0 1453 }
ab53c018 1454 continue;
b3d44315 1455 }
e1430400
DK
1456
1457 /* Queue Packages file (either diff or full packages files, depending
1458 on the users option) - we also check if the PDiff Index file is listed
1459 in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this
1460 instead, but passing the required info to it is to much hassle */
1461 if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
1462 MetaIndexParser->Exists(string((*Target)->MetaKey).append(".diff/Index")) == true))
2ac3eeb6 1463 new pkgAcqDiffIndex(Owner, (*Target)->URI, (*Target)->Description,
495e5cb2 1464 (*Target)->ShortDesc, ExpectedIndexHash);
e1430400 1465 else
5d885723 1466 new pkgAcqIndex(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
b3d44315
MV
1467 }
1468}
92fcbfc1
DK
1469 /*}}}*/
1470bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
b3d44315 1471{
ce424cd4
MV
1472 string::size_type pos;
1473
1474 // check for missing sigs (that where not fatal because otherwise we had
1475 // bombed earlier)
1476 string missingkeys;
400ad7a4 1477 string msg = _("There is no public key available for the "
ce424cd4
MV
1478 "following key IDs:\n");
1479 pos = Message.find("NO_PUBKEY ");
1480 if (pos != std::string::npos)
1481 {
1482 string::size_type start = pos+strlen("NO_PUBKEY ");
1483 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1484 missingkeys += (Fingerprint);
1485 }
1486 if(!missingkeys.empty())
1487 _error->Warning("%s", string(msg+missingkeys).c_str());
b3d44315
MV
1488
1489 string Transformed = MetaIndexParser->GetExpectedDist();
1490
1491 if (Transformed == "../project/experimental")
1492 {
1493 Transformed = "experimental";
1494 }
1495
ce424cd4 1496 pos = Transformed.rfind('/');
b3d44315
MV
1497 if (pos != string::npos)
1498 {
1499 Transformed = Transformed.substr(0, pos);
1500 }
1501
1502 if (Transformed == ".")
1503 {
1504 Transformed = "";
1505 }
1506
0323317c
DK
1507 if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
1508 MetaIndexParser->GetValidUntil() > 0) {
1509 time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil();
1510 if (invalid_since > 0)
1511 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
1512 // the time since then the file is invalid - formated in the same way as in
1513 // the download progress display (e.g. 7d 3h 42min 1s)
457bea86
MV
1514 return _error->Error(
1515 _("Release file for %s is expired (invalid since %s). "
1516 "Updates for this repository will not be applied."),
1517 RealURI.c_str(), TimeToStr(invalid_since).c_str());
1ddb8596
DK
1518 }
1519
b3d44315
MV
1520 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1521 {
1522 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
1523 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
1524 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1525 }
1526
1527 if (MetaIndexParser->CheckDist(Transformed) == false)
1528 {
1529 // This might become fatal one day
1530// Status = StatAuthError;
1531// ErrorText = "Conflicting distribution; expected "
1532// + MetaIndexParser->GetExpectedDist() + " but got "
1533// + MetaIndexParser->GetDist();
1534// return false;
1535 if (!Transformed.empty())
1536 {
1ddb8596 1537 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
b3d44315
MV
1538 Desc.Description.c_str(),
1539 Transformed.c_str(),
1540 MetaIndexParser->GetDist().c_str());
1541 }
1542 }
1543
1544 return true;
1545}
92fcbfc1
DK
1546 /*}}}*/
1547// pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
b3d44315
MV
1548// ---------------------------------------------------------------------
1549/* */
1550void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1551{
1552 if (AuthPass == true)
1553 {
fce72602 1554 // gpgv method failed, if we have a good signature
39f38a81
DK
1555 string LastGoodSigFile = _config->FindDir("Dir::State::lists").append("partial/").append(URItoFileName(RealURI));
1556 if (DestFile != SigFile)
1557 LastGoodSigFile.append(".gpg");
1558 LastGoodSigFile.append(".reverify");
fe0f7911 1559
fce72602 1560 if(FileExists(LastGoodSigFile))
f381d68d 1561 {
39f38a81 1562 string VerifiedSigFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
fe0f7911 1563 if (DestFile != SigFile)
39f38a81
DK
1564 VerifiedSigFile.append(".gpg");
1565 Rename(LastGoodSigFile, VerifiedSigFile);
fce72602 1566 Status = StatTransientNetworkError;
b5595da9 1567 _error->Warning(_("An error occurred during the signature "
fce72602 1568 "verification. The repository is not updated "
2493f4b5 1569 "and the previous index files will be used. "
76b8e5a5 1570 "GPG error: %s: %s\n"),
fce72602
MV
1571 Desc.Description.c_str(),
1572 LookupTag(Message,"Message").c_str());
5d149bfc 1573 RunScripts("APT::Update::Auth-Failure");
f381d68d 1574 return;
0901c5d0 1575 } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
4fdb6123 1576 /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
0901c5d0
JAK
1577 _error->Error(_("GPG error: %s: %s"),
1578 Desc.Description.c_str(),
1579 LookupTag(Message,"Message").c_str());
1580 return;
fce72602
MV
1581 } else {
1582 _error->Warning(_("GPG error: %s: %s"),
1583 Desc.Description.c_str(),
1584 LookupTag(Message,"Message").c_str());
f381d68d 1585 }
f381d68d 1586 // gpgv method failed
59271f62 1587 ReportMirrorFailure("GPGFailure");
b3d44315
MV
1588 }
1589
7ea7ac9e
JAK
1590 /* Always move the meta index, even if gpgv failed. This ensures
1591 * that PackageFile objects are correctly filled in */
a235ddf8 1592 if (FileExists(DestFile)) {
7ea7ac9e
JAK
1593 string FinalFile = _config->FindDir("Dir::State::lists");
1594 FinalFile += URItoFileName(RealURI);
1595 /* InRelease files become Release files, otherwise
1596 * they would be considered as trusted later on */
1597 if (SigFile == DestFile) {
1598 RealURI = RealURI.replace(RealURI.rfind("InRelease"), 9,
1599 "Release");
1600 FinalFile = FinalFile.replace(FinalFile.rfind("InRelease"), 9,
1601 "Release");
1602 SigFile = FinalFile;
1603 }
1604 Rename(DestFile,FinalFile);
1605 chmod(FinalFile.c_str(),0644);
1606
1607 DestFile = FinalFile;
1608 }
1609
b3d44315
MV
1610 // No Release file was present, or verification failed, so fall
1611 // back to queueing Packages files without verification
1612 QueueIndexes(false);
1613}
681d76d0 1614 /*}}}*/
fe0f7911
DK
1615pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/
1616 string const &URI, string const &URIDesc, string const &ShortDesc,
1617 string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
1618 string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
1619 const vector<struct IndexTarget*>* IndexTargets,
1620 indexRecords* MetaIndexParser) :
1621 pkgAcqMetaIndex(Owner, URI, URIDesc, ShortDesc, "", IndexTargets, MetaIndexParser),
1622 MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
1623 MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
1624{
1625 SigFile = DestFile;
39f38a81
DK
1626
1627 // keep the old InRelease around in case of transistent network errors
1628 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
ffcccd62 1629 if (RealFileExists(Final) == true)
39f38a81
DK
1630 {
1631 string const LastGoodSig = DestFile + ".reverify";
1632 Rename(Final,LastGoodSig);
1633 }
fe0f7911
DK
1634}
1635 /*}}}*/
ffcccd62
DK
1636pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
1637{
1638 // if the file was never queued undo file-changes done in the constructor
1639 if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false)
1640 {
1641 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1642 string const LastGoodSig = DestFile + ".reverify";
1643 if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true)
1644 Rename(LastGoodSig, Final);
1645 }
1646}
1647 /*}}}*/
8d6c5839
MV
1648// pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
1649// ---------------------------------------------------------------------
1650// FIXME: this can go away once the InRelease file is used widely
1651string pkgAcqMetaClearSig::Custom600Headers()
1652{
1653 string Final = _config->FindDir("Dir::State::lists");
1654 Final += URItoFileName(RealURI);
1655
1656 struct stat Buf;
1657 if (stat(Final.c_str(),&Buf) != 0)
0aec7d5c
DK
1658 {
1659 Final = DestFile + ".reverify";
1660 if (stat(Final.c_str(),&Buf) != 0)
1661 return "\nIndex-File: true\nFail-Ignore: true\n";
1662 }
8d6c5839
MV
1663
1664 return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1665}
1666 /*}}}*/
fe0f7911
DK
1667void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
1668{
1669 if (AuthPass == false)
1670 {
de498a52
DK
1671 // Remove the 'old' InRelease file if we try Release.gpg now as otherwise
1672 // the file will stay around and gives a false-auth impression (CVE-2012-0214)
1673 string FinalFile = _config->FindDir("Dir::State::lists");
1674 FinalFile.append(URItoFileName(RealURI));
1675 if (FileExists(FinalFile))
1676 unlink(FinalFile.c_str());
1677
fe0f7911
DK
1678 new pkgAcqMetaSig(Owner,
1679 MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
1680 MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
1681 IndexTargets, MetaIndexParser);
1682 if (Cnf->LocalOnly == true ||
1683 StringToBool(LookupTag(Message, "Transient-Failure"), false) == false)
1684 Dequeue();
1685 }
1686 else
1687 pkgAcqMetaIndex::Failed(Message, Cnf);
1688}
1689 /*}}}*/
03e39e59
AL
1690// AcqArchive::AcqArchive - Constructor /*{{{*/
1691// ---------------------------------------------------------------------
17caf1b1
AL
1692/* This just sets up the initial fetch environment and queues the first
1693 possibilitiy */
03e39e59 1694pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
30e1eab5
AL
1695 pkgRecords *Recs,pkgCache::VerIterator const &Version,
1696 string &StoreFilename) :
1697 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
b3d44315
MV
1698 StoreFilename(StoreFilename), Vf(Version.FileList()),
1699 Trusted(false)
03e39e59 1700{
7d8afa39 1701 Retries = _config->FindI("Acquire::Retries",0);
813c8eea
AL
1702
1703 if (Version.Arch() == 0)
bdae53f1 1704 {
d1f1f6a8 1705 _error->Error(_("I wasn't able to locate a file for the %s package. "
7a3c2ab0
AL
1706 "This might mean you need to manually fix this package. "
1707 "(due to missing arch)"),
813c8eea 1708 Version.ParentPkg().Name());
bdae53f1
AL
1709 return;
1710 }
813c8eea 1711
b2e465d6
AL
1712 /* We need to find a filename to determine the extension. We make the
1713 assumption here that all the available sources for this version share
1714 the same extension.. */
1715 // Skip not source sources, they do not have file fields.
69c2ecbd 1716 for (; Vf.end() == false; ++Vf)
b2e465d6
AL
1717 {
1718 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1719 continue;
1720 break;
1721 }
1722
1723 // Does not really matter here.. we are going to fail out below
1724 if (Vf.end() != true)
1725 {
1726 // If this fails to get a file name we will bomb out below.
1727 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1728 if (_error->PendingError() == true)
1729 return;
1730
1731 // Generate the final file name as: package_version_arch.foo
1732 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
1733 QuoteString(Version.VerStr(),"_:") + '_' +
1734 QuoteString(Version.Arch(),"_:.") +
1735 "." + flExtension(Parse.FileName());
1736 }
b3d44315
MV
1737
1738 // check if we have one trusted source for the package. if so, switch
6c34ccca
DK
1739 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
1740 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
1741 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
1742 bool seenUntrusted = false;
f7f0d6c7 1743 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
b3d44315
MV
1744 {
1745 pkgIndexFile *Index;
1746 if (Sources->FindIndex(i.File(),Index) == false)
1747 continue;
6c34ccca
DK
1748
1749 if (debugAuth == true)
b3d44315 1750 std::cerr << "Checking index: " << Index->Describe()
6c34ccca
DK
1751 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
1752
1753 if (Index->IsTrusted() == true)
1754 {
b3d44315 1755 Trusted = true;
6c34ccca
DK
1756 if (allowUnauth == false)
1757 break;
b3d44315 1758 }
6c34ccca
DK
1759 else
1760 seenUntrusted = true;
b3d44315
MV
1761 }
1762
a3371852
MV
1763 // "allow-unauthenticated" restores apts old fetching behaviour
1764 // that means that e.g. unauthenticated file:// uris are higher
1765 // priority than authenticated http:// uris
6c34ccca 1766 if (allowUnauth == true && seenUntrusted == true)
a3371852
MV
1767 Trusted = false;
1768
03e39e59 1769 // Select a source
b185acc2 1770 if (QueueNext() == false && _error->PendingError() == false)
d57f6084
DK
1771 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
1772 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
b185acc2
AL
1773}
1774 /*}}}*/
1775// AcqArchive::QueueNext - Queue the next file source /*{{{*/
1776// ---------------------------------------------------------------------
17caf1b1
AL
1777/* This queues the next available file version for download. It checks if
1778 the archive is already available in the cache and stashs the MD5 for
1779 checking later. */
b185acc2 1780bool pkgAcqArchive::QueueNext()
a722b2c5
DK
1781{
1782 string const ForceHash = _config->Find("Acquire::ForceHash");
f7f0d6c7 1783 for (; Vf.end() == false; ++Vf)
03e39e59
AL
1784 {
1785 // Ignore not source sources
1786 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1787 continue;
1788
1789 // Try to cross match against the source list
b2e465d6
AL
1790 pkgIndexFile *Index;
1791 if (Sources->FindIndex(Vf.File(),Index) == false)
1792 continue;
03e39e59 1793
b3d44315
MV
1794 // only try to get a trusted package from another source if that source
1795 // is also trusted
1796 if(Trusted && !Index->IsTrusted())
1797 continue;
1798
03e39e59
AL
1799 // Grab the text package record
1800 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1801 if (_error->PendingError() == true)
b185acc2 1802 return false;
03e39e59 1803
b2e465d6 1804 string PkgFile = Parse.FileName();
a722b2c5
DK
1805 if (ForceHash.empty() == false)
1806 {
d9b9e9e2
MV
1807 if(stringcasecmp(ForceHash, "sha512") == 0)
1808 ExpectedHash = HashString("SHA512", Parse.SHA512Hash());
ee5f5d25 1809 else if(stringcasecmp(ForceHash, "sha256") == 0)
a722b2c5
DK
1810 ExpectedHash = HashString("SHA256", Parse.SHA256Hash());
1811 else if (stringcasecmp(ForceHash, "sha1") == 0)
1812 ExpectedHash = HashString("SHA1", Parse.SHA1Hash());
1813 else
1814 ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
1815 }
1816 else
1817 {
1818 string Hash;
d9b9e9e2
MV
1819 if ((Hash = Parse.SHA512Hash()).empty() == false)
1820 ExpectedHash = HashString("SHA512", Hash);
1821 else if ((Hash = Parse.SHA256Hash()).empty() == false)
a722b2c5
DK
1822 ExpectedHash = HashString("SHA256", Hash);
1823 else if ((Hash = Parse.SHA1Hash()).empty() == false)
1824 ExpectedHash = HashString("SHA1", Hash);
1825 else
1826 ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
1827 }
03e39e59 1828 if (PkgFile.empty() == true)
b2e465d6
AL
1829 return _error->Error(_("The package index files are corrupted. No Filename: "
1830 "field for package %s."),
1831 Version.ParentPkg().Name());
a6568219 1832
b3d44315
MV
1833 Desc.URI = Index->ArchiveURI(PkgFile);
1834 Desc.Description = Index->ArchiveInfo(Version);
1835 Desc.Owner = this;
1836 Desc.ShortDesc = Version.ParentPkg().Name();
1837
17caf1b1 1838 // See if we already have the file. (Legacy filenames)
a6568219
AL
1839 FileSize = Version->Size;
1840 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
1841 struct stat Buf;
1842 if (stat(FinalFile.c_str(),&Buf) == 0)
1843 {
1844 // Make sure the size matches
73da43e9 1845 if ((unsigned long long)Buf.st_size == Version->Size)
a6568219
AL
1846 {
1847 Complete = true;
1848 Local = true;
1849 Status = StatDone;
30e1eab5 1850 StoreFilename = DestFile = FinalFile;
b185acc2 1851 return true;
a6568219
AL
1852 }
1853
6b1ff003
AL
1854 /* Hmm, we have a file and its size does not match, this means it is
1855 an old style mismatched arch */
a6568219
AL
1856 unlink(FinalFile.c_str());
1857 }
17caf1b1
AL
1858
1859 // Check it again using the new style output filenames
1860 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
1861 if (stat(FinalFile.c_str(),&Buf) == 0)
1862 {
1863 // Make sure the size matches
73da43e9 1864 if ((unsigned long long)Buf.st_size == Version->Size)
17caf1b1
AL
1865 {
1866 Complete = true;
1867 Local = true;
1868 Status = StatDone;
1869 StoreFilename = DestFile = FinalFile;
1870 return true;
1871 }
1872
1873 /* Hmm, we have a file and its size does not match, this shouldnt
1874 happen.. */
1875 unlink(FinalFile.c_str());
1876 }
1877
1878 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
6b1ff003
AL
1879
1880 // Check the destination file
1881 if (stat(DestFile.c_str(),&Buf) == 0)
1882 {
1883 // Hmm, the partial file is too big, erase it
73da43e9 1884 if ((unsigned long long)Buf.st_size > Version->Size)
6b1ff003
AL
1885 unlink(DestFile.c_str());
1886 else
1887 PartialSize = Buf.st_size;
1888 }
de31189f
DK
1889
1890 // Disables download of archives - useful if no real installation follows,
1891 // e.g. if we are just interested in proposed installation order
1892 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
1893 {
1894 Complete = true;
1895 Local = true;
1896 Status = StatDone;
1897 StoreFilename = DestFile = FinalFile;
1898 return true;
1899 }
1900
03e39e59 1901 // Create the item
b2e465d6
AL
1902 Local = false;
1903 Desc.URI = Index->ArchiveURI(PkgFile);
1904 Desc.Description = Index->ArchiveInfo(Version);
03e39e59
AL
1905 Desc.Owner = this;
1906 Desc.ShortDesc = Version.ParentPkg().Name();
1907 QueueURI(Desc);
b185acc2 1908
f7f0d6c7 1909 ++Vf;
b185acc2 1910 return true;
03e39e59 1911 }
b185acc2
AL
1912 return false;
1913}
03e39e59
AL
1914 /*}}}*/
1915// AcqArchive::Done - Finished fetching /*{{{*/
1916// ---------------------------------------------------------------------
1917/* */
73da43e9 1918void pkgAcqArchive::Done(string Message,unsigned long long Size,string CalcHash,
459681d3 1919 pkgAcquire::MethodConfig *Cfg)
03e39e59 1920{
495e5cb2 1921 Item::Done(Message,Size,CalcHash,Cfg);
03e39e59
AL
1922
1923 // Check the size
1924 if (Size != Version->Size)
1925 {
3c8030a4 1926 RenameOnError(SizeMismatch);
03e39e59
AL
1927 return;
1928 }
1929
495e5cb2 1930 // Check the hash
8a8feb29 1931 if(ExpectedHash.toStr() != CalcHash)
03e39e59 1932 {
3c8030a4 1933 RenameOnError(HashSumMismatch);
495e5cb2 1934 return;
03e39e59 1935 }
a6568219
AL
1936
1937 // Grab the output filename
03e39e59
AL
1938 string FileName = LookupTag(Message,"Filename");
1939 if (FileName.empty() == true)
1940 {
1941 Status = StatError;
1942 ErrorText = "Method gave a blank filename";
1943 return;
1944 }
a6568219
AL
1945
1946 Complete = true;
30e1eab5
AL
1947
1948 // Reference filename
a6568219
AL
1949 if (FileName != DestFile)
1950 {
30e1eab5 1951 StoreFilename = DestFile = FileName;
a6568219
AL
1952 Local = true;
1953 return;
1954 }
1955
1956 // Done, move it into position
1957 string FinalFile = _config->FindDir("Dir::Cache::Archives");
17caf1b1 1958 FinalFile += flNotDir(StoreFilename);
a6568219 1959 Rename(DestFile,FinalFile);
03e39e59 1960
30e1eab5 1961 StoreFilename = DestFile = FinalFile;
03e39e59
AL
1962 Complete = true;
1963}
1964 /*}}}*/
db890fdb
AL
1965// AcqArchive::Failed - Failure handler /*{{{*/
1966// ---------------------------------------------------------------------
1967/* Here we try other sources */
7d8afa39 1968void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
db890fdb
AL
1969{
1970 ErrorText = LookupTag(Message,"Message");
b2e465d6
AL
1971
1972 /* We don't really want to retry on failed media swaps, this prevents
1973 that. An interesting observation is that permanent failures are not
1974 recorded. */
1975 if (Cnf->Removable == true &&
1976 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1977 {
1978 // Vf = Version.FileList();
f7f0d6c7 1979 while (Vf.end() == false) ++Vf;
b2e465d6
AL
1980 StoreFilename = string();
1981 Item::Failed(Message,Cnf);
1982 return;
1983 }
1984
db890fdb 1985 if (QueueNext() == false)
7d8afa39
AL
1986 {
1987 // This is the retry counter
1988 if (Retries != 0 &&
1989 Cnf->LocalOnly == false &&
1990 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1991 {
1992 Retries--;
1993 Vf = Version.FileList();
1994 if (QueueNext() == true)
1995 return;
1996 }
1997
9dbb421f 1998 StoreFilename = string();
7d8afa39
AL
1999 Item::Failed(Message,Cnf);
2000 }
db890fdb
AL
2001}
2002 /*}}}*/
92fcbfc1 2003// AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
b3d44315
MV
2004// ---------------------------------------------------------------------
2005bool pkgAcqArchive::IsTrusted()
2006{
2007 return Trusted;
2008}
92fcbfc1 2009 /*}}}*/
ab559b35
AL
2010// AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
2011// ---------------------------------------------------------------------
2012/* */
2013void pkgAcqArchive::Finished()
2014{
2015 if (Status == pkgAcquire::Item::StatDone &&
2016 Complete == true)
2017 return;
2018 StoreFilename = string();
2019}
2020 /*}}}*/
36375005
AL
2021// AcqFile::pkgAcqFile - Constructor /*{{{*/
2022// ---------------------------------------------------------------------
2023/* The file is added to the queue */
8a8feb29 2024pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string Hash,
73da43e9 2025 unsigned long long Size,string Dsc,string ShortDesc,
77278c2b
MV
2026 const string &DestDir, const string &DestFilename,
2027 bool IsIndexFile) :
2028 Item(Owner), ExpectedHash(Hash), IsIndexFile(IsIndexFile)
36375005 2029{
08cfc005
AL
2030 Retries = _config->FindI("Acquire::Retries",0);
2031
46e00f9d
MV
2032 if(!DestFilename.empty())
2033 DestFile = DestFilename;
2034 else if(!DestDir.empty())
2035 DestFile = DestDir + "/" + flNotDir(URI);
2036 else
2037 DestFile = flNotDir(URI);
2038
36375005
AL
2039 // Create the item
2040 Desc.URI = URI;
2041 Desc.Description = Dsc;
2042 Desc.Owner = this;
2043
2044 // Set the short description to the archive component
2045 Desc.ShortDesc = ShortDesc;
2046
2047 // Get the transfer sizes
2048 FileSize = Size;
2049 struct stat Buf;
2050 if (stat(DestFile.c_str(),&Buf) == 0)
2051 {
2052 // Hmm, the partial file is too big, erase it
73da43e9 2053 if ((unsigned long long)Buf.st_size > Size)
36375005
AL
2054 unlink(DestFile.c_str());
2055 else
2056 PartialSize = Buf.st_size;
2057 }
092ae175 2058
36375005
AL
2059 QueueURI(Desc);
2060}
2061 /*}}}*/
2062// AcqFile::Done - Item downloaded OK /*{{{*/
2063// ---------------------------------------------------------------------
2064/* */
73da43e9 2065void pkgAcqFile::Done(string Message,unsigned long long Size,string CalcHash,
459681d3 2066 pkgAcquire::MethodConfig *Cnf)
36375005 2067{
495e5cb2
MV
2068 Item::Done(Message,Size,CalcHash,Cnf);
2069
8a8feb29 2070 // Check the hash
2c941d89 2071 if(!ExpectedHash.empty() && ExpectedHash.toStr() != CalcHash)
b3c39978 2072 {
3c8030a4 2073 RenameOnError(HashSumMismatch);
495e5cb2 2074 return;
b3c39978
AL
2075 }
2076
36375005
AL
2077 string FileName = LookupTag(Message,"Filename");
2078 if (FileName.empty() == true)
2079 {
2080 Status = StatError;
2081 ErrorText = "Method gave a blank filename";
2082 return;
2083 }
2084
2085 Complete = true;
2086
2087 // The files timestamp matches
2088 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2089 return;
2090
2091 // We have to copy it into place
2092 if (FileName != DestFile)
2093 {
2094 Local = true;
459681d3
AL
2095 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
2096 Cnf->Removable == true)
917ae805
AL
2097 {
2098 Desc.URI = "copy:" + FileName;
2099 QueueURI(Desc);
2100 return;
2101 }
2102
83ab33fc
AL
2103 // Erase the file if it is a symlink so we can overwrite it
2104 struct stat St;
2105 if (lstat(DestFile.c_str(),&St) == 0)
2106 {
2107 if (S_ISLNK(St.st_mode) != 0)
2108 unlink(DestFile.c_str());
2109 }
2110
2111 // Symlink the file
917ae805
AL
2112 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
2113 {
83ab33fc 2114 ErrorText = "Link to " + DestFile + " failure ";
917ae805
AL
2115 Status = StatError;
2116 Complete = false;
2117 }
36375005
AL
2118 }
2119}
2120 /*}}}*/
08cfc005
AL
2121// AcqFile::Failed - Failure handler /*{{{*/
2122// ---------------------------------------------------------------------
2123/* Here we try other sources */
2124void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2125{
2126 ErrorText = LookupTag(Message,"Message");
2127
2128 // This is the retry counter
2129 if (Retries != 0 &&
2130 Cnf->LocalOnly == false &&
2131 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2132 {
2133 Retries--;
2134 QueueURI(Desc);
2135 return;
2136 }
2137
2138 Item::Failed(Message,Cnf);
2139}
2140 /*}}}*/
77278c2b
MV
2141// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2142// ---------------------------------------------------------------------
2143/* The only header we use is the last-modified header. */
2144string pkgAcqFile::Custom600Headers()
2145{
2146 if (IsIndexFile)
2147 return "\nIndex-File: true";
61a07c57 2148 return "";
77278c2b
MV
2149}
2150 /*}}}*/