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