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