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