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