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