Add a Acquire::CompressionTypes config variable from there the
[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
e85b4cd5
DK
554 Configuration::Item const *Opts = _config->Tree("Acquire::CompressionTypes");
555 if (Opts != 0)
556 Opts = Opts->Child;
557
558 const char dirBin[] = "Dir::Bin::";
559 for (; Opts != 0; Opts = Opts->Next)
560 {
561 if (Opts->Tag.empty() == true || Opts->Value.empty() == true)
562 continue;
563 const string bin = _config->FindFile(string(dirBin).append(Opts->Value).c_str(),"");
564 if (bin != "" && !FileExists(bin))
565 continue;
566 comprExt = '.' + Opts->Tag;
567 break;
568 }
b3d44315 569 }
e85b4cd5
DK
570 CompressionExtension = ((comprExt == "plain" || comprExt == ".") ? "" : comprExt);
571
27d5a204 572 Desc.URI = URI + CompressionExtension;
b3d44315 573
b2e465d6 574 Desc.Description = URIDesc;
8267fe24 575 Desc.Owner = this;
b2e465d6 576 Desc.ShortDesc = ShortDesc;
8267fe24
AL
577
578 QueueURI(Desc);
0118833a
AL
579}
580 /*}}}*/
0a8a80e5 581// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 582// ---------------------------------------------------------------------
0a8a80e5
AL
583/* The only header we use is the last-modified header. */
584string pkgAcqIndex::Custom600Headers()
0118833a 585{
0a8a80e5 586 string Final = _config->FindDir("Dir::State::lists");
b2e465d6 587 Final += URItoFileName(RealURI);
0a8a80e5
AL
588
589 struct stat Buf;
590 if (stat(Final.c_str(),&Buf) != 0)
a72ace20 591 return "\nIndex-File: true";
0118833a 592
a72ace20 593 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
0118833a
AL
594}
595 /*}}}*/
92fcbfc1 596void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
debc84b2 597{
e85b4cd5
DK
598 Configuration::Item const *Opts = _config->Tree("Acquire::CompressionTypes");
599 if (Opts != 0)
600 Opts = Opts->Child;
601
602 const char dirBin[] = "Dir::Bin::";
603 for (; Opts != 0; Opts = Opts->Next)
604 {
605 if (Opts->Tag.empty() == true || Opts->Value.empty() == true)
606 continue;
607
608 // jump over all already checked compression types
609 const unsigned int nameLen = Desc.URI.size() - Opts->Tag.size();
610 if(Desc.URI.substr(nameLen) != Opts->Tag || Opts->Next == 0)
611 continue;
612
613 // check if we need an external binary for this compression type
614 const string bin = _config->FindFile(string(dirBin).append(Opts->Next->Value).c_str(),"");
615 if (bin != "" && !FileExists(bin))
616 continue;
617
618 // retry with the next extension
619 Desc.URI = Desc.URI.substr(0, nameLen) + Opts->Next->Tag;
620
621 new pkgAcqIndex(Owner, RealURI, Desc.Description, Desc.ShortDesc,
622 ExpectedHash, string(".").append(Opts->Next->Tag));
0d7a243d 623
b3d44315
MV
624 Status = StatDone;
625 Complete = false;
626 Dequeue();
debc84b2 627 return;
0d7a243d
EL
628 }
629
17ff0930
MV
630 // on decompression failure, remove bad versions in partial/
631 if(Decompression && Erase) {
632 string s = _config->FindDir("Dir::State::lists") + "partial/";
633 s += URItoFileName(RealURI);
634 unlink(s.c_str());
debc84b2
MZ
635 }
636
debc84b2
MZ
637 Item::Failed(Message,Cnf);
638}
92fcbfc1 639 /*}}}*/
8b89e57f
AL
640// AcqIndex::Done - Finished a fetch /*{{{*/
641// ---------------------------------------------------------------------
642/* This goes through a number of states.. On the initial fetch the
643 method could possibly return an alternate filename which points
644 to the uncompressed version of the file. If this is so the file
645 is copied into the partial directory. In all other cases the file
646 is decompressed with a gzip uri. */
495e5cb2 647void pkgAcqIndex::Done(string Message,unsigned long Size,string Hash,
459681d3 648 pkgAcquire::MethodConfig *Cfg)
8b89e57f 649{
495e5cb2 650 Item::Done(Message,Size,Hash,Cfg);
8b89e57f
AL
651
652 if (Decompression == true)
653 {
b3d44315
MV
654 if (_config->FindB("Debug::pkgAcquire::Auth", false))
655 {
495e5cb2
MV
656 std::cerr << std::endl << RealURI << ": Computed Hash: " << Hash;
657 std::cerr << " Expected Hash: " << ExpectedHash.toStr() << std::endl;
b3d44315
MV
658 }
659
8a8feb29 660 if (!ExpectedHash.empty() && ExpectedHash.toStr() != Hash)
b3d44315
MV
661 {
662 Status = StatAuthError;
9498d182 663 ErrorText = _("Hash Sum mismatch");
b3d44315
MV
664 Rename(DestFile,DestFile + ".FAILED");
665 return;
666 }
8b89e57f
AL
667 // Done, move it into position
668 string FinalFile = _config->FindDir("Dir::State::lists");
b2e465d6 669 FinalFile += URItoFileName(RealURI);
8b89e57f 670 Rename(DestFile,FinalFile);
7a3c2ab0 671 chmod(FinalFile.c_str(),0644);
bfd22fc0 672
7a7fa5f0
AL
673 /* We restore the original name to DestFile so that the clean operation
674 will work OK */
675 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
b2e465d6 676 DestFile += URItoFileName(RealURI);
7a7fa5f0 677
bfd22fc0
AL
678 // Remove the compressed version.
679 if (Erase == true)
bfd22fc0 680 unlink(DestFile.c_str());
8b89e57f
AL
681 return;
682 }
bfd22fc0
AL
683
684 Erase = false;
8267fe24 685 Complete = true;
bfd22fc0 686
8b89e57f
AL
687 // Handle the unzipd case
688 string FileName = LookupTag(Message,"Alt-Filename");
689 if (FileName.empty() == false)
690 {
691 // The files timestamp matches
692 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
693 return;
8b89e57f 694 Decompression = true;
a6568219 695 Local = true;
8b89e57f 696 DestFile += ".decomp";
8267fe24
AL
697 Desc.URI = "copy:" + FileName;
698 QueueURI(Desc);
b98f2859 699 Mode = "copy";
8b89e57f
AL
700 return;
701 }
702
703 FileName = LookupTag(Message,"Filename");
704 if (FileName.empty() == true)
705 {
706 Status = StatError;
707 ErrorText = "Method gave a blank filename";
708 }
709
710 // The files timestamp matches
711 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
712 return;
bfd22fc0
AL
713
714 if (FileName == DestFile)
715 Erase = true;
8267fe24 716 else
a6568219 717 Local = true;
8b89e57f 718
589898ae 719 string compExt = flExtension(flNotDir(URI(Desc.URI).Path));
e85b4cd5
DK
720 string decompProg;
721
722 // get the binary name for your used compression type
723 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),"");
724 if(decompProg.empty() == false);
708ead3a 725 // flExtensions returns the full name if no extension is found
1c8ab720
MV
726 // this is why we have this complicated compare operation here
727 // FIMXE: add a new flJustExtension() that return "" if no
728 // extension is found and use that above so that it can
729 // be tested against ""
708ead3a 730 else if(compExt == flNotDir(URI(Desc.URI).Path))
0d7a243d 731 decompProg = "copy";
debc84b2
MZ
732 else {
733 _error->Error("Unsupported extension: %s", compExt.c_str());
734 return;
735 }
736
8b89e57f
AL
737 Decompression = true;
738 DestFile += ".decomp";
e85b4cd5 739 Desc.URI = decompProg + ":" + FileName;
8267fe24 740 QueueURI(Desc);
e85b4cd5 741 Mode = decompProg.c_str();
8b89e57f 742}
92fcbfc1 743 /*}}}*/
a52f938b
OS
744// AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
745// ---------------------------------------------------------------------
746/* The Translation file is added to the queue */
747pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
495e5cb2
MV
748 string URI,string URIDesc,string ShortDesc)
749 : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashString(), "")
a52f938b
OS
750{
751}
a52f938b
OS
752 /*}}}*/
753// AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
754// ---------------------------------------------------------------------
755/* */
756void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
757{
758 if (Cnf->LocalOnly == true ||
759 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
760 {
761 // Ignore this
762 Status = StatDone;
763 Complete = false;
764 Dequeue();
765 return;
766 }
767
768 Item::Failed(Message,Cnf);
769}
770 /*}}}*/
92fcbfc1 771pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, /*{{{*/
b3d44315
MV
772 string URI,string URIDesc,string ShortDesc,
773 string MetaIndexURI, string MetaIndexURIDesc,
774 string MetaIndexShortDesc,
775 const vector<IndexTarget*>* IndexTargets,
776 indexRecords* MetaIndexParser) :
777 Item(Owner), RealURI(URI), MetaIndexURI(MetaIndexURI),
46e00f9d
MV
778 MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
779 MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
0118833a 780{
0a8a80e5 781 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
b2e465d6 782 DestFile += URItoFileName(URI);
b3d44315 783
47eb38f4
MV
784 // remove any partial downloaded sig-file in partial/.
785 // it may confuse proxies and is too small to warrant a
786 // partial download anyway
f6237efd
MV
787 unlink(DestFile.c_str());
788
8267fe24 789 // Create the item
b2e465d6 790 Desc.Description = URIDesc;
8267fe24 791 Desc.Owner = this;
b3d44315
MV
792 Desc.ShortDesc = ShortDesc;
793 Desc.URI = URI;
b3d44315
MV
794
795 string Final = _config->FindDir("Dir::State::lists");
796 Final += URItoFileName(RealURI);
797 struct stat Buf;
798 if (stat(Final.c_str(),&Buf) == 0)
799 {
ef942597
MV
800 // File was already in place. It needs to be re-downloaded/verified
801 // because Release might have changed, we do give it a differnt
802 // name than DestFile because otherwise the http method will
803 // send If-Range requests and there are too many broken servers
804 // out there that do not understand them
805 LastGoodSig = DestFile+".reverify";
806 Rename(Final,LastGoodSig);
b3d44315 807 }
8267fe24 808
8267fe24 809 QueueURI(Desc);
0118833a
AL
810}
811 /*}}}*/
b3d44315 812// pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 813// ---------------------------------------------------------------------
0a8a80e5 814/* The only header we use is the last-modified header. */
b3d44315 815string pkgAcqMetaSig::Custom600Headers()
0118833a 816{
0a8a80e5 817 struct stat Buf;
ef942597 818 if (stat(LastGoodSig.c_str(),&Buf) != 0)
a72ace20 819 return "\nIndex-File: true";
a789b983 820
a72ace20 821 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
0118833a 822}
b3d44315
MV
823
824void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5,
825 pkgAcquire::MethodConfig *Cfg)
c88edf1d 826{
459681d3 827 Item::Done(Message,Size,MD5,Cfg);
c88edf1d
AL
828
829 string FileName = LookupTag(Message,"Filename");
830 if (FileName.empty() == true)
831 {
832 Status = StatError;
833 ErrorText = "Method gave a blank filename";
8b89e57f 834 return;
c88edf1d 835 }
8b89e57f 836
c88edf1d
AL
837 if (FileName != DestFile)
838 {
b3d44315 839 // We have to copy it into place
a6568219 840 Local = true;
8267fe24
AL
841 Desc.URI = "copy:" + FileName;
842 QueueURI(Desc);
c88edf1d
AL
843 return;
844 }
b3d44315
MV
845
846 Complete = true;
847
ef942597
MV
848 // put the last known good file back on i-m-s hit (it will
849 // be re-verified again)
850 // Else do nothing, we have the new file in DestFile then
851 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
852 Rename(LastGoodSig, DestFile);
853
b3d44315
MV
854 // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
855 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
856 DestFile, IndexTargets, MetaIndexParser);
857
c88edf1d
AL
858}
859 /*}}}*/
92fcbfc1 860void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
681d76d0 861{
47eb38f4 862 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
a789b983 863
75dd8af1 864 // if we get a network error we fail gracefully
7e5f33eb
MV
865 if(Status == StatTransientNetworkError)
866 {
24057ad6 867 Item::Failed(Message,Cnf);
484dbb81 868 // move the sigfile back on transient network failures
47eb38f4 869 if(FileExists(DestFile))
ef942597 870 Rename(LastGoodSig,Final);
7e5f33eb
MV
871
872 // set the status back to , Item::Failed likes to reset it
873 Status = pkgAcquire::Item::StatTransientNetworkError;
24057ad6
MV
874 return;
875 }
876
75dd8af1 877 // Delete any existing sigfile when the acquire failed
75dd8af1
MV
878 unlink(Final.c_str());
879
b3d44315
MV
880 // queue a pkgAcqMetaIndex with no sigfile
881 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
882 "", IndexTargets, MetaIndexParser);
883
681d76d0
AL
884 if (Cnf->LocalOnly == true ||
885 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
886 {
2b154e53
AL
887 // Ignore this
888 Status = StatDone;
889 Complete = false;
681d76d0
AL
890 Dequeue();
891 return;
892 }
893
894 Item::Failed(Message,Cnf);
895}
92fcbfc1
DK
896 /*}}}*/
897pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
b3d44315
MV
898 string URI,string URIDesc,string ShortDesc,
899 string SigFile,
900 const vector<struct IndexTarget*>* IndexTargets,
901 indexRecords* MetaIndexParser) :
21fd1746
OS
902 Item(Owner), RealURI(URI), SigFile(SigFile), IndexTargets(IndexTargets),
903 MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false)
b3d44315 904{
b3d44315
MV
905 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
906 DestFile += URItoFileName(URI);
907
908 // Create the item
909 Desc.Description = URIDesc;
910 Desc.Owner = this;
911 Desc.ShortDesc = ShortDesc;
912 Desc.URI = URI;
913
914 QueueURI(Desc);
915}
b3d44315
MV
916 /*}}}*/
917// pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
918// ---------------------------------------------------------------------
919/* The only header we use is the last-modified header. */
920string pkgAcqMetaIndex::Custom600Headers()
921{
922 string Final = _config->FindDir("Dir::State::lists");
923 Final += URItoFileName(RealURI);
924
925 struct stat Buf;
926 if (stat(Final.c_str(),&Buf) != 0)
927 return "\nIndex-File: true";
928
929 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
930}
92fcbfc1
DK
931 /*}}}*/
932void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string Hash, /*{{{*/
b3d44315
MV
933 pkgAcquire::MethodConfig *Cfg)
934{
495e5cb2 935 Item::Done(Message,Size,Hash,Cfg);
b3d44315
MV
936
937 // MetaIndexes are done in two passes: one to download the
938 // metaindex with an appropriate method, and a second to verify it
939 // with the gpgv method
940
941 if (AuthPass == true)
942 {
943 AuthDone(Message);
944 }
945 else
946 {
947 RetrievalDone(Message);
948 if (!Complete)
949 // Still more retrieving to do
950 return;
951
952 if (SigFile == "")
953 {
954 // There was no signature file, so we are finished. Download
955 // the indexes without verification.
956 QueueIndexes(false);
957 }
958 else
959 {
960 // There was a signature file, so pass it to gpgv for
961 // verification
962
963 if (_config->FindB("Debug::pkgAcquire::Auth", false))
964 std::cerr << "Metaindex acquired, queueing gpg verification ("
965 << SigFile << "," << DestFile << ")\n";
966 AuthPass = true;
967 Desc.URI = "gpgv:" + SigFile;
968 QueueURI(Desc);
969 Mode = "gpgv";
970 }
971 }
972}
92fcbfc1
DK
973 /*}}}*/
974void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/
b3d44315
MV
975{
976 // We have just finished downloading a Release file (it is not
977 // verified yet)
978
979 string FileName = LookupTag(Message,"Filename");
980 if (FileName.empty() == true)
981 {
982 Status = StatError;
983 ErrorText = "Method gave a blank filename";
984 return;
985 }
986
987 if (FileName != DestFile)
988 {
989 Local = true;
990 Desc.URI = "copy:" + FileName;
991 QueueURI(Desc);
992 return;
993 }
994
f381d68d
MV
995 // see if the download was a IMSHit
996 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
b3d44315
MV
997 Complete = true;
998
999 string FinalFile = _config->FindDir("Dir::State::lists");
1000 FinalFile += URItoFileName(RealURI);
1001
4c499611
MV
1002 // If we get a IMS hit we can remove the empty file in partial
1003 // othersie we move the file in place
1004 if (IMSHit)
1005 unlink(DestFile.c_str());
1006 else
b3d44315 1007 Rename(DestFile,FinalFile);
4c499611 1008
1790e0cf 1009 chmod(FinalFile.c_str(),0644);
b3d44315
MV
1010 DestFile = FinalFile;
1011}
92fcbfc1
DK
1012 /*}}}*/
1013void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/
b3d44315
MV
1014{
1015 // At this point, the gpgv method has succeeded, so there is a
1016 // valid signature from a key in the trusted keyring. We
1017 // perform additional verification of its contents, and use them
1018 // to verify the indexes we are about to download
1019
1020 if (!MetaIndexParser->Load(DestFile))
1021 {
1022 Status = StatAuthError;
1023 ErrorText = MetaIndexParser->ErrorText;
1024 return;
1025 }
1026
ce424cd4 1027 if (!VerifyVendor(Message))
b3d44315
MV
1028 {
1029 return;
1030 }
1031
1032 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1033 std::cerr << "Signature verification succeeded: "
1034 << DestFile << std::endl;
1035
1036 // Download further indexes with verification
1037 QueueIndexes(true);
1038
1039 // Done, move signature file into position
1040
1041 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1042 URItoFileName(RealURI) + ".gpg";
1043 Rename(SigFile,VerifiedSigFile);
1044 chmod(VerifiedSigFile.c_str(),0644);
1045}
92fcbfc1
DK
1046 /*}}}*/
1047void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/
b3d44315
MV
1048{
1049 for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
1050 Target != IndexTargets->end();
1051 Target++)
1052 {
495e5cb2 1053 HashString ExpectedIndexHash;
b3d44315
MV
1054 if (verify)
1055 {
1056 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
1057 if (!Record)
1058 {
1059 Status = StatAuthError;
1060 ErrorText = "Unable to find expected entry "
1061 + (*Target)->MetaKey + " in Meta-index file (malformed Release file?)";
1062 return;
1063 }
495e5cb2 1064 ExpectedIndexHash = Record->Hash;
b3d44315
MV
1065 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1066 {
1067 std::cerr << "Queueing: " << (*Target)->URI << std::endl;
495e5cb2 1068 std::cerr << "Expected Hash: " << ExpectedIndexHash.toStr() << std::endl;
b3d44315 1069 }
495e5cb2 1070 if (ExpectedIndexHash.empty())
b3d44315
MV
1071 {
1072 Status = StatAuthError;
495e5cb2 1073 ErrorText = "Unable to find hash sum for "
b3d44315
MV
1074 + (*Target)->MetaKey + " in Meta-index file";
1075 return;
1076 }
1077 }
1078
2ac3eeb6
MV
1079 // Queue Packages file (either diff or full packages files, depending
1080 // on the users option)
27299daf 1081 if(_config->FindB("Acquire::PDiffs",true) == true)
2ac3eeb6 1082 new pkgAcqDiffIndex(Owner, (*Target)->URI, (*Target)->Description,
495e5cb2 1083 (*Target)->ShortDesc, ExpectedIndexHash);
2ac3eeb6 1084 else
81fcf9e2 1085 new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description,
495e5cb2 1086 (*Target)->ShortDesc, ExpectedIndexHash);
b3d44315
MV
1087 }
1088}
92fcbfc1
DK
1089 /*}}}*/
1090bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
b3d44315
MV
1091{
1092// // Maybe this should be made available from above so we don't have
1093// // to read and parse it every time?
1094// pkgVendorList List;
1095// List.ReadMainList();
1096
1097// const Vendor* Vndr = NULL;
1098// for (std::vector<string>::const_iterator I = GPGVOutput.begin(); I != GPGVOutput.end(); I++)
1099// {
1100// string::size_type pos = (*I).find("VALIDSIG ");
1101// if (_config->FindB("Debug::Vendor", false))
1102// std::cerr << "Looking for VALIDSIG in \"" << (*I) << "\": pos " << pos
1103// << std::endl;
1104// if (pos != std::string::npos)
1105// {
1106// string Fingerprint = (*I).substr(pos+sizeof("VALIDSIG"));
1107// if (_config->FindB("Debug::Vendor", false))
1108// std::cerr << "Looking for \"" << Fingerprint << "\" in vendor..." <<
1109// std::endl;
1110// Vndr = List.FindVendor(Fingerprint) != "";
1111// if (Vndr != NULL);
1112// break;
1113// }
1114// }
ce424cd4
MV
1115 string::size_type pos;
1116
1117 // check for missing sigs (that where not fatal because otherwise we had
1118 // bombed earlier)
1119 string missingkeys;
400ad7a4 1120 string msg = _("There is no public key available for the "
ce424cd4
MV
1121 "following key IDs:\n");
1122 pos = Message.find("NO_PUBKEY ");
1123 if (pos != std::string::npos)
1124 {
1125 string::size_type start = pos+strlen("NO_PUBKEY ");
1126 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1127 missingkeys += (Fingerprint);
1128 }
1129 if(!missingkeys.empty())
1130 _error->Warning("%s", string(msg+missingkeys).c_str());
b3d44315
MV
1131
1132 string Transformed = MetaIndexParser->GetExpectedDist();
1133
1134 if (Transformed == "../project/experimental")
1135 {
1136 Transformed = "experimental";
1137 }
1138
ce424cd4 1139 pos = Transformed.rfind('/');
b3d44315
MV
1140 if (pos != string::npos)
1141 {
1142 Transformed = Transformed.substr(0, pos);
1143 }
1144
1145 if (Transformed == ".")
1146 {
1147 Transformed = "";
1148 }
1149
1150 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1151 {
1152 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
1153 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
1154 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1155 }
1156
1157 if (MetaIndexParser->CheckDist(Transformed) == false)
1158 {
1159 // This might become fatal one day
1160// Status = StatAuthError;
1161// ErrorText = "Conflicting distribution; expected "
1162// + MetaIndexParser->GetExpectedDist() + " but got "
1163// + MetaIndexParser->GetDist();
1164// return false;
1165 if (!Transformed.empty())
1166 {
1167 _error->Warning("Conflicting distribution: %s (expected %s but got %s)",
1168 Desc.Description.c_str(),
1169 Transformed.c_str(),
1170 MetaIndexParser->GetDist().c_str());
1171 }
1172 }
1173
1174 return true;
1175}
92fcbfc1
DK
1176 /*}}}*/
1177// pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
b3d44315
MV
1178// ---------------------------------------------------------------------
1179/* */
1180void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1181{
1182 if (AuthPass == true)
1183 {
f381d68d
MV
1184 // if we fail the authentication but got the file via a IMS-Hit
1185 // this means that the file wasn't downloaded and that it might be
1186 // just stale (server problem, proxy etc). we delete what we have
1187 // queue it again without i-m-s
1188 // alternatively we could just unlink the file and let the user try again
1189 if (IMSHit)
1190 {
1191 Complete = false;
1192 Local = false;
1193 AuthPass = false;
1194 unlink(DestFile.c_str());
1195
1196 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1197 DestFile += URItoFileName(RealURI);
1198 Desc.URI = RealURI;
1199 QueueURI(Desc);
1200 return;
1201 }
1202
1203 // gpgv method failed
b3d44315
MV
1204 _error->Warning("GPG error: %s: %s",
1205 Desc.Description.c_str(),
1206 LookupTag(Message,"Message").c_str());
f381d68d 1207
b3d44315
MV
1208 }
1209
1210 // No Release file was present, or verification failed, so fall
1211 // back to queueing Packages files without verification
1212 QueueIndexes(false);
1213}
681d76d0 1214 /*}}}*/
03e39e59
AL
1215// AcqArchive::AcqArchive - Constructor /*{{{*/
1216// ---------------------------------------------------------------------
17caf1b1
AL
1217/* This just sets up the initial fetch environment and queues the first
1218 possibilitiy */
03e39e59 1219pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
30e1eab5
AL
1220 pkgRecords *Recs,pkgCache::VerIterator const &Version,
1221 string &StoreFilename) :
1222 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
b3d44315
MV
1223 StoreFilename(StoreFilename), Vf(Version.FileList()),
1224 Trusted(false)
03e39e59 1225{
7d8afa39 1226 Retries = _config->FindI("Acquire::Retries",0);
813c8eea
AL
1227
1228 if (Version.Arch() == 0)
bdae53f1 1229 {
d1f1f6a8 1230 _error->Error(_("I wasn't able to locate a file for the %s package. "
7a3c2ab0
AL
1231 "This might mean you need to manually fix this package. "
1232 "(due to missing arch)"),
813c8eea 1233 Version.ParentPkg().Name());
bdae53f1
AL
1234 return;
1235 }
813c8eea 1236
b2e465d6
AL
1237 /* We need to find a filename to determine the extension. We make the
1238 assumption here that all the available sources for this version share
1239 the same extension.. */
1240 // Skip not source sources, they do not have file fields.
1241 for (; Vf.end() == false; Vf++)
1242 {
1243 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1244 continue;
1245 break;
1246 }
1247
1248 // Does not really matter here.. we are going to fail out below
1249 if (Vf.end() != true)
1250 {
1251 // If this fails to get a file name we will bomb out below.
1252 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1253 if (_error->PendingError() == true)
1254 return;
1255
1256 // Generate the final file name as: package_version_arch.foo
1257 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
1258 QuoteString(Version.VerStr(),"_:") + '_' +
1259 QuoteString(Version.Arch(),"_:.") +
1260 "." + flExtension(Parse.FileName());
1261 }
b3d44315
MV
1262
1263 // check if we have one trusted source for the package. if so, switch
1264 // to "TrustedOnly" mode
1265 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; i++)
1266 {
1267 pkgIndexFile *Index;
1268 if (Sources->FindIndex(i.File(),Index) == false)
1269 continue;
1270 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1271 {
1272 std::cerr << "Checking index: " << Index->Describe()
1273 << "(Trusted=" << Index->IsTrusted() << ")\n";
1274 }
1275 if (Index->IsTrusted()) {
1276 Trusted = true;
1277 break;
1278 }
1279 }
1280
a3371852
MV
1281 // "allow-unauthenticated" restores apts old fetching behaviour
1282 // that means that e.g. unauthenticated file:// uris are higher
1283 // priority than authenticated http:// uris
1284 if (_config->FindB("APT::Get::AllowUnauthenticated",false) == true)
1285 Trusted = false;
1286
03e39e59 1287 // Select a source
b185acc2 1288 if (QueueNext() == false && _error->PendingError() == false)
b2e465d6
AL
1289 _error->Error(_("I wasn't able to locate file for the %s package. "
1290 "This might mean you need to manually fix this package."),
b185acc2
AL
1291 Version.ParentPkg().Name());
1292}
1293 /*}}}*/
1294// AcqArchive::QueueNext - Queue the next file source /*{{{*/
1295// ---------------------------------------------------------------------
17caf1b1
AL
1296/* This queues the next available file version for download. It checks if
1297 the archive is already available in the cache and stashs the MD5 for
1298 checking later. */
b185acc2 1299bool pkgAcqArchive::QueueNext()
b2e465d6 1300{
03e39e59
AL
1301 for (; Vf.end() == false; Vf++)
1302 {
1303 // Ignore not source sources
1304 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1305 continue;
1306
1307 // Try to cross match against the source list
b2e465d6
AL
1308 pkgIndexFile *Index;
1309 if (Sources->FindIndex(Vf.File(),Index) == false)
1310 continue;
03e39e59 1311
b3d44315
MV
1312 // only try to get a trusted package from another source if that source
1313 // is also trusted
1314 if(Trusted && !Index->IsTrusted())
1315 continue;
1316
03e39e59
AL
1317 // Grab the text package record
1318 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1319 if (_error->PendingError() == true)
b185acc2 1320 return false;
03e39e59 1321
b2e465d6 1322 string PkgFile = Parse.FileName();
495e5cb2 1323 if(Parse.SHA256Hash() != "")
8a8feb29 1324 ExpectedHash = HashString("SHA256", Parse.SHA256Hash());
495e5cb2 1325 else if (Parse.SHA1Hash() != "")
8a8feb29 1326 ExpectedHash = HashString("SHA1", Parse.SHA1Hash());
495e5cb2 1327 else
8a8feb29 1328 ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
03e39e59 1329 if (PkgFile.empty() == true)
b2e465d6
AL
1330 return _error->Error(_("The package index files are corrupted. No Filename: "
1331 "field for package %s."),
1332 Version.ParentPkg().Name());
a6568219 1333
b3d44315
MV
1334 Desc.URI = Index->ArchiveURI(PkgFile);
1335 Desc.Description = Index->ArchiveInfo(Version);
1336 Desc.Owner = this;
1337 Desc.ShortDesc = Version.ParentPkg().Name();
1338
17caf1b1 1339 // See if we already have the file. (Legacy filenames)
a6568219
AL
1340 FileSize = Version->Size;
1341 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
1342 struct stat Buf;
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;
30e1eab5 1351 StoreFilename = DestFile = FinalFile;
b185acc2 1352 return true;
a6568219
AL
1353 }
1354
6b1ff003
AL
1355 /* Hmm, we have a file and its size does not match, this means it is
1356 an old style mismatched arch */
a6568219
AL
1357 unlink(FinalFile.c_str());
1358 }
17caf1b1
AL
1359
1360 // Check it again using the new style output filenames
1361 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
1362 if (stat(FinalFile.c_str(),&Buf) == 0)
1363 {
1364 // Make sure the size matches
1365 if ((unsigned)Buf.st_size == Version->Size)
1366 {
1367 Complete = true;
1368 Local = true;
1369 Status = StatDone;
1370 StoreFilename = DestFile = FinalFile;
1371 return true;
1372 }
1373
1374 /* Hmm, we have a file and its size does not match, this shouldnt
1375 happen.. */
1376 unlink(FinalFile.c_str());
1377 }
1378
1379 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
6b1ff003
AL
1380
1381 // Check the destination file
1382 if (stat(DestFile.c_str(),&Buf) == 0)
1383 {
1384 // Hmm, the partial file is too big, erase it
1385 if ((unsigned)Buf.st_size > Version->Size)
1386 unlink(DestFile.c_str());
1387 else
1388 PartialSize = Buf.st_size;
1389 }
1390
03e39e59 1391 // Create the item
b2e465d6
AL
1392 Local = false;
1393 Desc.URI = Index->ArchiveURI(PkgFile);
1394 Desc.Description = Index->ArchiveInfo(Version);
03e39e59
AL
1395 Desc.Owner = this;
1396 Desc.ShortDesc = Version.ParentPkg().Name();
1397 QueueURI(Desc);
b185acc2
AL
1398
1399 Vf++;
1400 return true;
03e39e59 1401 }
b185acc2
AL
1402 return false;
1403}
03e39e59
AL
1404 /*}}}*/
1405// AcqArchive::Done - Finished fetching /*{{{*/
1406// ---------------------------------------------------------------------
1407/* */
495e5cb2 1408void pkgAcqArchive::Done(string Message,unsigned long Size,string CalcHash,
459681d3 1409 pkgAcquire::MethodConfig *Cfg)
03e39e59 1410{
495e5cb2 1411 Item::Done(Message,Size,CalcHash,Cfg);
03e39e59
AL
1412
1413 // Check the size
1414 if (Size != Version->Size)
1415 {
bdae53f1 1416 Status = StatError;
b2e465d6 1417 ErrorText = _("Size mismatch");
03e39e59
AL
1418 return;
1419 }
1420
495e5cb2 1421 // Check the hash
8a8feb29 1422 if(ExpectedHash.toStr() != CalcHash)
03e39e59 1423 {
495e5cb2 1424 Status = StatError;
9498d182 1425 ErrorText = _("Hash Sum mismatch");
495e5cb2
MV
1426 if(FileExists(DestFile))
1427 Rename(DestFile,DestFile + ".FAILED");
1428 return;
03e39e59 1429 }
a6568219
AL
1430
1431 // Grab the output filename
03e39e59
AL
1432 string FileName = LookupTag(Message,"Filename");
1433 if (FileName.empty() == true)
1434 {
1435 Status = StatError;
1436 ErrorText = "Method gave a blank filename";
1437 return;
1438 }
a6568219
AL
1439
1440 Complete = true;
30e1eab5
AL
1441
1442 // Reference filename
a6568219
AL
1443 if (FileName != DestFile)
1444 {
30e1eab5 1445 StoreFilename = DestFile = FileName;
a6568219
AL
1446 Local = true;
1447 return;
1448 }
1449
1450 // Done, move it into position
1451 string FinalFile = _config->FindDir("Dir::Cache::Archives");
17caf1b1 1452 FinalFile += flNotDir(StoreFilename);
a6568219 1453 Rename(DestFile,FinalFile);
03e39e59 1454
30e1eab5 1455 StoreFilename = DestFile = FinalFile;
03e39e59
AL
1456 Complete = true;
1457}
1458 /*}}}*/
db890fdb
AL
1459// AcqArchive::Failed - Failure handler /*{{{*/
1460// ---------------------------------------------------------------------
1461/* Here we try other sources */
7d8afa39 1462void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
db890fdb
AL
1463{
1464 ErrorText = LookupTag(Message,"Message");
b2e465d6
AL
1465
1466 /* We don't really want to retry on failed media swaps, this prevents
1467 that. An interesting observation is that permanent failures are not
1468 recorded. */
1469 if (Cnf->Removable == true &&
1470 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1471 {
1472 // Vf = Version.FileList();
1473 while (Vf.end() == false) Vf++;
1474 StoreFilename = string();
1475 Item::Failed(Message,Cnf);
1476 return;
1477 }
1478
db890fdb 1479 if (QueueNext() == false)
7d8afa39
AL
1480 {
1481 // This is the retry counter
1482 if (Retries != 0 &&
1483 Cnf->LocalOnly == false &&
1484 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1485 {
1486 Retries--;
1487 Vf = Version.FileList();
1488 if (QueueNext() == true)
1489 return;
1490 }
1491
9dbb421f 1492 StoreFilename = string();
7d8afa39
AL
1493 Item::Failed(Message,Cnf);
1494 }
db890fdb
AL
1495}
1496 /*}}}*/
92fcbfc1 1497// AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
b3d44315
MV
1498// ---------------------------------------------------------------------
1499bool pkgAcqArchive::IsTrusted()
1500{
1501 return Trusted;
1502}
92fcbfc1 1503 /*}}}*/
ab559b35
AL
1504// AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
1505// ---------------------------------------------------------------------
1506/* */
1507void pkgAcqArchive::Finished()
1508{
1509 if (Status == pkgAcquire::Item::StatDone &&
1510 Complete == true)
1511 return;
1512 StoreFilename = string();
1513}
1514 /*}}}*/
36375005
AL
1515// AcqFile::pkgAcqFile - Constructor /*{{{*/
1516// ---------------------------------------------------------------------
1517/* The file is added to the queue */
8a8feb29 1518pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string Hash,
46e00f9d
MV
1519 unsigned long Size,string Dsc,string ShortDesc,
1520 const string &DestDir, const string &DestFilename) :
8a8feb29 1521 Item(Owner), ExpectedHash(Hash)
36375005 1522{
08cfc005
AL
1523 Retries = _config->FindI("Acquire::Retries",0);
1524
46e00f9d
MV
1525 if(!DestFilename.empty())
1526 DestFile = DestFilename;
1527 else if(!DestDir.empty())
1528 DestFile = DestDir + "/" + flNotDir(URI);
1529 else
1530 DestFile = flNotDir(URI);
1531
36375005
AL
1532 // Create the item
1533 Desc.URI = URI;
1534 Desc.Description = Dsc;
1535 Desc.Owner = this;
1536
1537 // Set the short description to the archive component
1538 Desc.ShortDesc = ShortDesc;
1539
1540 // Get the transfer sizes
1541 FileSize = Size;
1542 struct stat Buf;
1543 if (stat(DestFile.c_str(),&Buf) == 0)
1544 {
1545 // Hmm, the partial file is too big, erase it
1546 if ((unsigned)Buf.st_size > Size)
1547 unlink(DestFile.c_str());
1548 else
1549 PartialSize = Buf.st_size;
1550 }
092ae175 1551
36375005
AL
1552 QueueURI(Desc);
1553}
1554 /*}}}*/
1555// AcqFile::Done - Item downloaded OK /*{{{*/
1556// ---------------------------------------------------------------------
1557/* */
495e5cb2 1558void pkgAcqFile::Done(string Message,unsigned long Size,string CalcHash,
459681d3 1559 pkgAcquire::MethodConfig *Cnf)
36375005 1560{
495e5cb2
MV
1561 Item::Done(Message,Size,CalcHash,Cnf);
1562
8a8feb29 1563 // Check the hash
2c941d89 1564 if(!ExpectedHash.empty() && ExpectedHash.toStr() != CalcHash)
b3c39978 1565 {
495e5cb2 1566 Status = StatError;
9498d182 1567 ErrorText = "Hash Sum mismatch";
495e5cb2
MV
1568 Rename(DestFile,DestFile + ".FAILED");
1569 return;
b3c39978
AL
1570 }
1571
36375005
AL
1572 string FileName = LookupTag(Message,"Filename");
1573 if (FileName.empty() == true)
1574 {
1575 Status = StatError;
1576 ErrorText = "Method gave a blank filename";
1577 return;
1578 }
1579
1580 Complete = true;
1581
1582 // The files timestamp matches
1583 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1584 return;
1585
1586 // We have to copy it into place
1587 if (FileName != DestFile)
1588 {
1589 Local = true;
459681d3
AL
1590 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
1591 Cnf->Removable == true)
917ae805
AL
1592 {
1593 Desc.URI = "copy:" + FileName;
1594 QueueURI(Desc);
1595 return;
1596 }
1597
83ab33fc
AL
1598 // Erase the file if it is a symlink so we can overwrite it
1599 struct stat St;
1600 if (lstat(DestFile.c_str(),&St) == 0)
1601 {
1602 if (S_ISLNK(St.st_mode) != 0)
1603 unlink(DestFile.c_str());
1604 }
1605
1606 // Symlink the file
917ae805
AL
1607 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
1608 {
83ab33fc 1609 ErrorText = "Link to " + DestFile + " failure ";
917ae805
AL
1610 Status = StatError;
1611 Complete = false;
1612 }
36375005
AL
1613 }
1614}
1615 /*}}}*/
08cfc005
AL
1616// AcqFile::Failed - Failure handler /*{{{*/
1617// ---------------------------------------------------------------------
1618/* Here we try other sources */
1619void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1620{
1621 ErrorText = LookupTag(Message,"Message");
1622
1623 // This is the retry counter
1624 if (Retries != 0 &&
1625 Cnf->LocalOnly == false &&
1626 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1627 {
1628 Retries--;
1629 QueueURI(Desc);
1630 return;
1631 }
1632
1633 Item::Failed(Message,Cnf);
1634}
1635 /*}}}*/