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