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