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