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