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