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