]> git.saurik.com Git - apt.git/blame - apt-pkg/acquire-item.cc
* first rewrite of the apt pdiff support patch finished
[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 /*{{{*/
16#ifdef __GNUG__
17#pragma implementation "apt-pkg/acquire-item.h"
18#endif
19#include <apt-pkg/acquire-item.h>
20#include <apt-pkg/configuration.h>
b2e465d6 21#include <apt-pkg/sourcelist.h>
b3d44315 22#include <apt-pkg/vendorlist.h>
03e39e59 23#include <apt-pkg/error.h>
cdcc6d34 24#include <apt-pkg/strutl.h>
36375005 25#include <apt-pkg/fileutl.h>
b3d44315 26#include <apt-pkg/md5.h>
ac5b205a
MV
27#include <apt-pkg/sha1.h>
28#include <apt-pkg/tagfile.h>
0a8a80e5 29
b2e465d6
AL
30#include <apti18n.h>
31
0a8a80e5
AL
32#include <sys/stat.h>
33#include <unistd.h>
c88edf1d 34#include <errno.h>
5819a761 35#include <string>
ac5b205a 36#include <sstream>
c88edf1d 37#include <stdio.h>
0118833a
AL
38 /*}}}*/
39
b3d44315 40using namespace std;
5819a761 41
0118833a
AL
42// Acquire::Item::Item - Constructor /*{{{*/
43// ---------------------------------------------------------------------
44/* */
8267fe24 45pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
6b1ff003
AL
46 PartialSize(0), Mode(0), ID(0), Complete(false),
47 Local(false), QueueCounter(0)
0118833a
AL
48{
49 Owner->Add(this);
c88edf1d 50 Status = StatIdle;
0118833a
AL
51}
52 /*}}}*/
53// Acquire::Item::~Item - Destructor /*{{{*/
54// ---------------------------------------------------------------------
55/* */
56pkgAcquire::Item::~Item()
57{
58 Owner->Remove(this);
59}
60 /*}}}*/
c88edf1d
AL
61// Acquire::Item::Failed - Item failed to download /*{{{*/
62// ---------------------------------------------------------------------
93bf083d
AL
63/* We return to an idle state if there are still other queues that could
64 fetch this object */
7d8afa39 65void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
c88edf1d 66{
93bf083d 67 Status = StatIdle;
db890fdb 68 ErrorText = LookupTag(Message,"Message");
c88edf1d 69 if (QueueCounter <= 1)
93bf083d 70 {
a72ace20 71 /* This indicates that the file is not available right now but might
7d8afa39 72 be sometime later. If we do a retry cycle then this should be
17caf1b1 73 retried [CDROMs] */
7d8afa39
AL
74 if (Cnf->LocalOnly == true &&
75 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
a72ace20
AL
76 {
77 Status = StatIdle;
681d76d0 78 Dequeue();
a72ace20
AL
79 return;
80 }
81
93bf083d 82 Status = StatError;
681d76d0 83 Dequeue();
93bf083d 84 }
c88edf1d
AL
85}
86 /*}}}*/
8267fe24
AL
87// Acquire::Item::Start - Item has begun to download /*{{{*/
88// ---------------------------------------------------------------------
17caf1b1
AL
89/* Stash status and the file size. Note that setting Complete means
90 sub-phases of the acquire process such as decompresion are operating */
727f18af 91void pkgAcquire::Item::Start(string /*Message*/,unsigned long Size)
8267fe24
AL
92{
93 Status = StatFetching;
94 if (FileSize == 0 && Complete == false)
95 FileSize = Size;
96}
97 /*}}}*/
c88edf1d
AL
98// Acquire::Item::Done - Item downloaded OK /*{{{*/
99// ---------------------------------------------------------------------
100/* */
459681d3
AL
101void pkgAcquire::Item::Done(string Message,unsigned long Size,string,
102 pkgAcquire::MethodConfig *Cnf)
c88edf1d 103{
b98f2859
AL
104 // We just downloaded something..
105 string FileName = LookupTag(Message,"Filename");
106 if (Complete == false && FileName == DestFile)
107 {
108 if (Owner->Log != 0)
109 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
110 }
aa0e1101
AL
111
112 if (FileSize == 0)
113 FileSize= Size;
b98f2859 114
c88edf1d
AL
115 Status = StatDone;
116 ErrorText = string();
117 Owner->Dequeue(this);
118}
119 /*}}}*/
8b89e57f
AL
120// Acquire::Item::Rename - Rename a file /*{{{*/
121// ---------------------------------------------------------------------
122/* This helper function is used by alot of item methods as thier final
123 step */
124void pkgAcquire::Item::Rename(string From,string To)
125{
126 if (rename(From.c_str(),To.c_str()) != 0)
127 {
128 char S[300];
0fcd01de 129 snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
8b89e57f
AL
130 From.c_str(),To.c_str());
131 Status = StatError;
132 ErrorText = S;
7a3c2ab0 133 }
8b89e57f
AL
134}
135 /*}}}*/
0118833a 136
ac5b205a
MV
137// AcqIndexDiffs::AcqIndexDiffs - Constructor
138// ---------------------------------------------------------------------
139/* The package diff is added to the queue. one object is constructed
140 * for each diff and the index
141 */
142pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
143 string URI,string URIDesc,string ShortDesc,
144 string ExpectedMD5, vector<string> diffs)
145 : Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5), needed_files(diffs)
146{
147
148 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
149 DestFile += URItoFileName(URI);
150
151 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
152
153 Desc.Description = URIDesc;
154 Desc.Owner = this;
155 Desc.ShortDesc = ShortDesc;
156
157 CurrentPackagesFile = _config->FindDir("Dir::State::lists");
158 CurrentPackagesFile += URItoFileName(RealURI);
159
160 if(Debug) {
161 std::clog << "pkgAcqIndexDiffs::pkgAcqIndexDiffs(): "
162 << CurrentPackagesFile << std::endl;
163 }
164
165 if(!FileExists(CurrentPackagesFile) ||
166 !_config->FindB("Acquire::Diffs",true)) {
167 // we don't have a pkg file or we don't want to queue
168 if(Debug)
169 std::clog << "No index file or canceld by user" << std::endl;
170 Failed("", NULL);
171 return;
172 }
173
174 if(needed_files.size() == 0)
175 QueueDiffIndex(URI);
176 else
177 QueueNextDiff();
178}
179
180void pkgAcqIndexDiffs::QueueDiffIndex(string URI)
181{
182 Desc.URI = URI + ".diff/Index";
183 Desc.Description = Description + "IndexDiff";
184 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
185 DestFile += URItoFileName(URI) + string(".IndexDiff");
186
187 if(Debug)
188 std::clog << "QueueDiffIndex: " << Desc.URI << std::endl;
189
190 QueueURI(Desc);
191}
192
193void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
194{
195 if(Debug)
196 std::clog << "Failed(): " << Desc.URI << std::endl
197 << "Falling back to big package file" << std::endl;
198 new pkgAcqIndex(Owner, RealURI, Desc.Description,Desc.ShortDesc,
199 ExpectedMD5);
200 Finish();
201}
202
203
204// helper that cleans the item out of the fetcher queue
205void pkgAcqIndexDiffs::Finish(bool allDone)
206{
207 // we restore the original name, this is required, otherwise
208 // the file will be cleaned
209 if(allDone) {
210 // this is for the "real" finish
211 DestFile = _config->FindDir("Dir::State::lists");
212 DestFile += URItoFileName(RealURI);
213 Complete = true;
214 Dequeue();
215 if(Debug)
216 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
217 return;
218
219 }
220
221 if(Debug)
222 std::clog << "Finishing: " << Desc.URI << std::endl;
223 Complete = false;
224 Status = StatDone;
225 Dequeue();
226 return;
227}
228
229
230// this needs to be rewriten to not depend on the external ed
231bool pkgAcqIndexDiffs::ApplyDiff(string PatchFile)
232{
233 char *error;
234 int res=0;
235
236 string FinalFile = _config->FindDir("Dir::State::lists");
237 FinalFile += URItoFileName(RealURI);
238
239 int Process = ExecFork();
240 if (Process == 0)
241 {
242 chdir(_config->FindDir("Dir::State::lists").c_str());
243 string cmd = "(zcat " + PatchFile + "; echo \"wq\" ) | ed " + FinalFile + " >/dev/null 2>/dev/null";
244 if(Debug)
245 std::clog << "Runing: " << cmd << std::endl;
246 res = system(cmd.c_str());
247 _exit(WEXITSTATUS(res));
248 }
249 if(!ExecWait(Process, error, true)) {
250 //_error->Error("Patch failed: %s ", error);
251 return false;
252 }
253
254 return true;
255}
256
257bool pkgAcqIndexDiffs::QueueNextDiff()
258{
259 // queue diff
260 Desc.URI = string(RealURI) + string(".diff/") + needed_files[0] + string(".gz");
261 Desc.Description = Description + string("-diff");
262
263 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
264 DestFile += URItoFileName(RealURI + string(".diff/") + needed_files[0]);
265
266 if(Debug)
267 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
268
269 QueueURI(Desc);
270
271 return true;
272}
273
274bool pkgAcqIndexDiffs::ParseIndexDiff(string IndexDiffFile)
275{
276 if(Debug)
277 std::clog << "pkgAcqIndexDiffs::ParseIndexDiff() " << IndexDiffFile
278 << std::endl;
279
280 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
281 pkgTagFile TF(&Fd);
282 if (_error->PendingError() == true)
283 return false;
284 pkgTagSection Tags;
285 if(TF.Step(Tags) == true)
286 {
287 string local_sha1;
288 string tmp = Tags.FindS("SHA1-Current");
289 std::stringstream ss(tmp);
290 ss >> ServerSha1;
291
292 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
293 SHA1Summation SHA1;
294 SHA1.AddFD(fd.Fd(), fd.Size());
295 local_sha1 = string(SHA1.Result());
296
297 if(local_sha1 == ServerSha1) {
298 if(Debug)
299 std::clog << "Package file is up-to-date" << std::endl;
300 Finish(true);
301 return true;
302 }
303 if(Debug)
304 std::clog << "SHA1-Current: " << ServerSha1 << std::endl;
305
306 // check the historie and see what patches we need
307 string history = Tags.FindS("SHA1-History");
308 std::stringstream hist(history);
309 string sha1, size, file;
310 bool found = false;
311 while(hist >> sha1 >> size >> file) {
312 if(sha1 == local_sha1)
313 found=true;
314 if(found) {
315 if(Debug)
316 std::clog << "Need to get diff: " << file << std::endl;
317 needed_files.push_back(file);
318 }
319 }
320 // no information how to get the patches, bail out
321 if(!found) {
322 if(Debug)
323 std::clog << "Can't find a patch in the index file" << std::endl;
324 // Failed will queue a big package file
325 Failed("", NULL);
326 } else {
327 // queue the diffs
328 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
329 ExpectedMD5, needed_files);
330 Finish();
331 return true;
332 }
333 }
334
335 return false;
336}
337
338
339void pkgAcqIndexDiffs::Done(string Message,unsigned long Size,string Md5Hash,
340 pkgAcquire::MethodConfig *Cnf)
341{
342 if(Debug)
343 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
344
345 Item::Done(Message,Size,Md5Hash,Cnf);
346
347 int len = Desc.URI.size();
348 // sucess in downloading the index
349 if(Desc.URI.substr(len-strlen("Index"),len-1) == "Index") {
350 if(!ParseIndexDiff(DestFile))
351 return Failed("", NULL);
352 else
353 return Finish();
354 }
355
356 // sucess in downloading a diff
357 if(Desc.URI.find(".diff") != string::npos) {
358 ApplyDiff(DestFile);
359 needed_files.erase(needed_files.begin());
360
361 if(needed_files.size() > 0) {
362 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
363 ExpectedMD5, needed_files);
364 } else {
365 Finish(true);
366 return;
367 }
368 }
369
370 Finish();
371}
372
373
0118833a
AL
374// AcqIndex::AcqIndex - Constructor /*{{{*/
375// ---------------------------------------------------------------------
376/* The package file is added to the queue and a second class is
b2e465d6
AL
377 instantiated to fetch the revision file */
378pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
b3d44315 379 string URI,string URIDesc,string ShortDesc,
ac5b205a
MV
380 string ExpectedMD5, string comprExt)
381 : Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5)
0118833a 382{
8b89e57f 383 Decompression = false;
bfd22fc0 384 Erase = false;
8b89e57f 385
0a8a80e5 386 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
b2e465d6 387 DestFile += URItoFileName(URI);
8267fe24 388
b3d44315
MV
389 if(comprExt.empty())
390 {
391 // autoselect
392 if(FileExists("/usr/bin/bzip2"))
393 Desc.URI = URI + ".bz2";
394 else
395 Desc.URI = URI + ".gz";
396 } else {
397 Desc.URI = URI + comprExt;
398 }
399
b2e465d6 400 Desc.Description = URIDesc;
8267fe24 401 Desc.Owner = this;
b2e465d6 402 Desc.ShortDesc = ShortDesc;
8267fe24
AL
403
404 QueueURI(Desc);
0118833a
AL
405}
406 /*}}}*/
0a8a80e5 407// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 408// ---------------------------------------------------------------------
0a8a80e5
AL
409/* The only header we use is the last-modified header. */
410string pkgAcqIndex::Custom600Headers()
0118833a 411{
0a8a80e5 412 string Final = _config->FindDir("Dir::State::lists");
b2e465d6 413 Final += URItoFileName(RealURI);
0a8a80e5
AL
414
415 struct stat Buf;
416 if (stat(Final.c_str(),&Buf) != 0)
a72ace20 417 return "\nIndex-File: true";
0118833a 418
a72ace20 419 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
0118833a
AL
420}
421 /*}}}*/
debc84b2
MZ
422
423void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
424{
425 // no .bz2 found, retry with .gz
426 if(Desc.URI.substr(Desc.URI.size()-3,Desc.URI.size()-1) == "bz2") {
427 Desc.URI = Desc.URI.substr(0,Desc.URI.size()-3) + "gz";
b3d44315
MV
428
429 // retry with a gzip one
430 new pkgAcqIndex(Owner, RealURI, Desc.Description,Desc.ShortDesc,
431 ExpectedMD5, string(".gz"));
432 Status = StatDone;
433 Complete = false;
434 Dequeue();
debc84b2
MZ
435 return;
436 }
437
438
439 Item::Failed(Message,Cnf);
440}
441
442
8b89e57f
AL
443// AcqIndex::Done - Finished a fetch /*{{{*/
444// ---------------------------------------------------------------------
445/* This goes through a number of states.. On the initial fetch the
446 method could possibly return an alternate filename which points
447 to the uncompressed version of the file. If this is so the file
448 is copied into the partial directory. In all other cases the file
449 is decompressed with a gzip uri. */
459681d3
AL
450void pkgAcqIndex::Done(string Message,unsigned long Size,string MD5,
451 pkgAcquire::MethodConfig *Cfg)
8b89e57f 452{
459681d3 453 Item::Done(Message,Size,MD5,Cfg);
8b89e57f
AL
454
455 if (Decompression == true)
456 {
b3d44315
MV
457 if (_config->FindB("Debug::pkgAcquire::Auth", false))
458 {
459 std::cerr << std::endl << RealURI << ": Computed MD5: " << MD5;
460 std::cerr << " Expected MD5: " << ExpectedMD5 << std::endl;
461 }
462
463 if (MD5.empty())
464 {
465 MD5Summation sum;
466 FileFd Fd(DestFile, FileFd::ReadOnly);
467 sum.AddFD(Fd.Fd(), Fd.Size());
468 Fd.Close();
469 MD5 = (string)sum.Result();
470 }
471
472 if (!ExpectedMD5.empty() && MD5 != ExpectedMD5)
473 {
474 Status = StatAuthError;
475 ErrorText = _("MD5Sum mismatch");
476 Rename(DestFile,DestFile + ".FAILED");
477 return;
478 }
8b89e57f
AL
479 // Done, move it into position
480 string FinalFile = _config->FindDir("Dir::State::lists");
b2e465d6 481 FinalFile += URItoFileName(RealURI);
8b89e57f 482 Rename(DestFile,FinalFile);
7a3c2ab0 483 chmod(FinalFile.c_str(),0644);
bfd22fc0 484
7a7fa5f0
AL
485 /* We restore the original name to DestFile so that the clean operation
486 will work OK */
487 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
b2e465d6 488 DestFile += URItoFileName(RealURI);
7a7fa5f0 489
bfd22fc0
AL
490 // Remove the compressed version.
491 if (Erase == true)
bfd22fc0 492 unlink(DestFile.c_str());
8b89e57f
AL
493 return;
494 }
bfd22fc0
AL
495
496 Erase = false;
8267fe24 497 Complete = true;
bfd22fc0 498
8b89e57f
AL
499 // Handle the unzipd case
500 string FileName = LookupTag(Message,"Alt-Filename");
501 if (FileName.empty() == false)
502 {
503 // The files timestamp matches
504 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
505 return;
b3d44315 506
8b89e57f 507 Decompression = true;
a6568219 508 Local = true;
8b89e57f 509 DestFile += ".decomp";
8267fe24
AL
510 Desc.URI = "copy:" + FileName;
511 QueueURI(Desc);
b98f2859 512 Mode = "copy";
8b89e57f
AL
513 return;
514 }
515
516 FileName = LookupTag(Message,"Filename");
517 if (FileName.empty() == true)
518 {
519 Status = StatError;
520 ErrorText = "Method gave a blank filename";
521 }
522
523 // The files timestamp matches
524 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
525 return;
bfd22fc0
AL
526
527 if (FileName == DestFile)
528 Erase = true;
8267fe24 529 else
a6568219 530 Local = true;
8b89e57f 531
debc84b2
MZ
532 string compExt = Desc.URI.substr(Desc.URI.size()-3,Desc.URI.size()-1);
533 char *decompProg;
534 if(compExt == "bz2")
535 decompProg = "bzip2";
536 else if(compExt == ".gz")
537 decompProg = "gzip";
538 else {
539 _error->Error("Unsupported extension: %s", compExt.c_str());
540 return;
541 }
542
8b89e57f
AL
543 Decompression = true;
544 DestFile += ".decomp";
debc84b2 545 Desc.URI = string(decompProg) + ":" + FileName;
8267fe24 546 QueueURI(Desc);
debc84b2 547 Mode = decompProg;
8b89e57f 548}
8b89e57f 549
b3d44315
MV
550pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,
551 string URI,string URIDesc,string ShortDesc,
552 string MetaIndexURI, string MetaIndexURIDesc,
553 string MetaIndexShortDesc,
554 const vector<IndexTarget*>* IndexTargets,
555 indexRecords* MetaIndexParser) :
556 Item(Owner), RealURI(URI), MetaIndexURI(MetaIndexURI),
557 MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc)
0118833a 558{
b3d44315
MV
559 this->MetaIndexParser = MetaIndexParser;
560 this->IndexTargets = IndexTargets;
0a8a80e5 561 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
b2e465d6 562 DestFile += URItoFileName(URI);
b3d44315 563
f6237efd
MV
564 // remove any partial downloaded sig-file. it may confuse proxies
565 // and is too small to warrant a partial download anyway
566 unlink(DestFile.c_str());
567
8267fe24 568 // Create the item
b2e465d6 569 Desc.Description = URIDesc;
8267fe24 570 Desc.Owner = this;
b3d44315
MV
571 Desc.ShortDesc = ShortDesc;
572 Desc.URI = URI;
573
574
575 string Final = _config->FindDir("Dir::State::lists");
576 Final += URItoFileName(RealURI);
577 struct stat Buf;
578 if (stat(Final.c_str(),&Buf) == 0)
579 {
580 // File was already in place. It needs to be re-verified
581 // because Release might have changed, so Move it into partial
582 Rename(Final,DestFile);
583 }
8267fe24 584
8267fe24 585 QueueURI(Desc);
0118833a
AL
586}
587 /*}}}*/
b3d44315 588// pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 589// ---------------------------------------------------------------------
0a8a80e5 590/* The only header we use is the last-modified header. */
b3d44315 591string pkgAcqMetaSig::Custom600Headers()
0118833a 592{
0a8a80e5 593 struct stat Buf;
2aab5956 594 if (stat(DestFile.c_str(),&Buf) != 0)
a72ace20 595 return "\nIndex-File: true";
a789b983 596
a72ace20 597 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
0118833a 598}
b3d44315
MV
599
600void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5,
601 pkgAcquire::MethodConfig *Cfg)
c88edf1d 602{
459681d3 603 Item::Done(Message,Size,MD5,Cfg);
c88edf1d
AL
604
605 string FileName = LookupTag(Message,"Filename");
606 if (FileName.empty() == true)
607 {
608 Status = StatError;
609 ErrorText = "Method gave a blank filename";
8b89e57f 610 return;
c88edf1d 611 }
8b89e57f 612
c88edf1d
AL
613 if (FileName != DestFile)
614 {
b3d44315 615 // We have to copy it into place
a6568219 616 Local = true;
8267fe24
AL
617 Desc.URI = "copy:" + FileName;
618 QueueURI(Desc);
c88edf1d
AL
619 return;
620 }
b3d44315
MV
621
622 Complete = true;
623
624 // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
625 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
626 DestFile, IndexTargets, MetaIndexParser);
627
c88edf1d
AL
628}
629 /*}}}*/
b3d44315 630void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
681d76d0 631{
b3d44315
MV
632 // Delete any existing sigfile, so that this source isn't
633 // mistakenly trusted
634 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
635 unlink(Final.c_str());
a789b983 636
b3d44315
MV
637 // queue a pkgAcqMetaIndex with no sigfile
638 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
639 "", IndexTargets, MetaIndexParser);
640
681d76d0
AL
641 if (Cnf->LocalOnly == true ||
642 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
643 {
2b154e53
AL
644 // Ignore this
645 Status = StatDone;
646 Complete = false;
681d76d0
AL
647 Dequeue();
648 return;
649 }
650
651 Item::Failed(Message,Cnf);
652}
b3d44315
MV
653
654pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner,
655 string URI,string URIDesc,string ShortDesc,
656 string SigFile,
657 const vector<struct IndexTarget*>* IndexTargets,
658 indexRecords* MetaIndexParser) :
659 Item(Owner), RealURI(URI), SigFile(SigFile)
660{
661 this->AuthPass = false;
662 this->MetaIndexParser = MetaIndexParser;
663 this->IndexTargets = IndexTargets;
664 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
665 DestFile += URItoFileName(URI);
666
667 // Create the item
668 Desc.Description = URIDesc;
669 Desc.Owner = this;
670 Desc.ShortDesc = ShortDesc;
671 Desc.URI = URI;
672
673 QueueURI(Desc);
674}
675
676 /*}}}*/
677// pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
678// ---------------------------------------------------------------------
679/* The only header we use is the last-modified header. */
680string pkgAcqMetaIndex::Custom600Headers()
681{
682 string Final = _config->FindDir("Dir::State::lists");
683 Final += URItoFileName(RealURI);
684
685 struct stat Buf;
686 if (stat(Final.c_str(),&Buf) != 0)
687 return "\nIndex-File: true";
688
689 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
690}
691
692void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string MD5,
693 pkgAcquire::MethodConfig *Cfg)
694{
695 Item::Done(Message,Size,MD5,Cfg);
696
697 // MetaIndexes are done in two passes: one to download the
698 // metaindex with an appropriate method, and a second to verify it
699 // with the gpgv method
700
701 if (AuthPass == true)
702 {
703 AuthDone(Message);
704 }
705 else
706 {
707 RetrievalDone(Message);
708 if (!Complete)
709 // Still more retrieving to do
710 return;
711
712 if (SigFile == "")
713 {
714 // There was no signature file, so we are finished. Download
715 // the indexes without verification.
716 QueueIndexes(false);
717 }
718 else
719 {
720 // There was a signature file, so pass it to gpgv for
721 // verification
722
723 if (_config->FindB("Debug::pkgAcquire::Auth", false))
724 std::cerr << "Metaindex acquired, queueing gpg verification ("
725 << SigFile << "," << DestFile << ")\n";
726 AuthPass = true;
727 Desc.URI = "gpgv:" + SigFile;
728 QueueURI(Desc);
729 Mode = "gpgv";
730 }
731 }
732}
733
734void pkgAcqMetaIndex::RetrievalDone(string Message)
735{
736 // We have just finished downloading a Release file (it is not
737 // verified yet)
738
739 string FileName = LookupTag(Message,"Filename");
740 if (FileName.empty() == true)
741 {
742 Status = StatError;
743 ErrorText = "Method gave a blank filename";
744 return;
745 }
746
747 if (FileName != DestFile)
748 {
749 Local = true;
750 Desc.URI = "copy:" + FileName;
751 QueueURI(Desc);
752 return;
753 }
754
755 Complete = true;
756
757 string FinalFile = _config->FindDir("Dir::State::lists");
758 FinalFile += URItoFileName(RealURI);
759
760 // The files timestamp matches
761 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == false)
762 {
763 // Move it into position
764 Rename(DestFile,FinalFile);
765 }
766 DestFile = FinalFile;
767}
768
769void pkgAcqMetaIndex::AuthDone(string Message)
770{
771 // At this point, the gpgv method has succeeded, so there is a
772 // valid signature from a key in the trusted keyring. We
773 // perform additional verification of its contents, and use them
774 // to verify the indexes we are about to download
775
776 if (!MetaIndexParser->Load(DestFile))
777 {
778 Status = StatAuthError;
779 ErrorText = MetaIndexParser->ErrorText;
780 return;
781 }
782
783 if (!VerifyVendor())
784 {
785 return;
786 }
787
788 if (_config->FindB("Debug::pkgAcquire::Auth", false))
789 std::cerr << "Signature verification succeeded: "
790 << DestFile << std::endl;
791
792 // Download further indexes with verification
793 QueueIndexes(true);
794
795 // Done, move signature file into position
796
797 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
798 URItoFileName(RealURI) + ".gpg";
799 Rename(SigFile,VerifiedSigFile);
800 chmod(VerifiedSigFile.c_str(),0644);
801}
802
803void pkgAcqMetaIndex::QueueIndexes(bool verify)
804{
805 for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
806 Target != IndexTargets->end();
807 Target++)
808 {
809 string ExpectedIndexMD5;
810 if (verify)
811 {
812 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
813 if (!Record)
814 {
815 Status = StatAuthError;
816 ErrorText = "Unable to find expected entry "
817 + (*Target)->MetaKey + " in Meta-index file (malformed Release file?)";
818 return;
819 }
820 ExpectedIndexMD5 = Record->MD5Hash;
821 if (_config->FindB("Debug::pkgAcquire::Auth", false))
822 {
823 std::cerr << "Queueing: " << (*Target)->URI << std::endl;
824 std::cerr << "Expected MD5: " << ExpectedIndexMD5 << std::endl;
825 }
826 if (ExpectedIndexMD5.empty())
827 {
828 Status = StatAuthError;
829 ErrorText = "Unable to find MD5 sum for "
830 + (*Target)->MetaKey + " in Meta-index file";
831 return;
832 }
833 }
834
835 // Queue Packages file
ac5b205a
MV
836 new pkgAcqIndexDiffs(Owner, (*Target)->URI, (*Target)->Description,
837 (*Target)->ShortDesc, ExpectedIndexMD5);
b3d44315
MV
838 }
839}
840
841bool pkgAcqMetaIndex::VerifyVendor()
842{
843// // Maybe this should be made available from above so we don't have
844// // to read and parse it every time?
845// pkgVendorList List;
846// List.ReadMainList();
847
848// const Vendor* Vndr = NULL;
849// for (std::vector<string>::const_iterator I = GPGVOutput.begin(); I != GPGVOutput.end(); I++)
850// {
851// string::size_type pos = (*I).find("VALIDSIG ");
852// if (_config->FindB("Debug::Vendor", false))
853// std::cerr << "Looking for VALIDSIG in \"" << (*I) << "\": pos " << pos
854// << std::endl;
855// if (pos != std::string::npos)
856// {
857// string Fingerprint = (*I).substr(pos+sizeof("VALIDSIG"));
858// if (_config->FindB("Debug::Vendor", false))
859// std::cerr << "Looking for \"" << Fingerprint << "\" in vendor..." <<
860// std::endl;
861// Vndr = List.FindVendor(Fingerprint) != "";
862// if (Vndr != NULL);
863// break;
864// }
865// }
866
867 string Transformed = MetaIndexParser->GetExpectedDist();
868
869 if (Transformed == "../project/experimental")
870 {
871 Transformed = "experimental";
872 }
873
874 string::size_type pos = Transformed.rfind('/');
875 if (pos != string::npos)
876 {
877 Transformed = Transformed.substr(0, pos);
878 }
879
880 if (Transformed == ".")
881 {
882 Transformed = "";
883 }
884
885 if (_config->FindB("Debug::pkgAcquire::Auth", false))
886 {
887 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
888 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
889 std::cerr << "Transformed Dist: " << Transformed << std::endl;
890 }
891
892 if (MetaIndexParser->CheckDist(Transformed) == false)
893 {
894 // This might become fatal one day
895// Status = StatAuthError;
896// ErrorText = "Conflicting distribution; expected "
897// + MetaIndexParser->GetExpectedDist() + " but got "
898// + MetaIndexParser->GetDist();
899// return false;
900 if (!Transformed.empty())
901 {
902 _error->Warning("Conflicting distribution: %s (expected %s but got %s)",
903 Desc.Description.c_str(),
904 Transformed.c_str(),
905 MetaIndexParser->GetDist().c_str());
906 }
907 }
908
909 return true;
910}
911 /*}}}*/
912// pkgAcqMetaIndex::Failed - no Release file present or no signature
913// file present /*{{{*/
914// ---------------------------------------------------------------------
915/* */
916void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
917{
918 if (AuthPass == true)
919 {
920 // gpgv method failed
921 _error->Warning("GPG error: %s: %s",
922 Desc.Description.c_str(),
923 LookupTag(Message,"Message").c_str());
924 }
925
926 // No Release file was present, or verification failed, so fall
927 // back to queueing Packages files without verification
928 QueueIndexes(false);
929}
930
681d76d0 931 /*}}}*/
03e39e59
AL
932
933// AcqArchive::AcqArchive - Constructor /*{{{*/
934// ---------------------------------------------------------------------
17caf1b1
AL
935/* This just sets up the initial fetch environment and queues the first
936 possibilitiy */
03e39e59 937pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
30e1eab5
AL
938 pkgRecords *Recs,pkgCache::VerIterator const &Version,
939 string &StoreFilename) :
940 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
b3d44315
MV
941 StoreFilename(StoreFilename), Vf(Version.FileList()),
942 Trusted(false)
03e39e59 943{
7d8afa39 944 Retries = _config->FindI("Acquire::Retries",0);
813c8eea
AL
945
946 if (Version.Arch() == 0)
bdae53f1 947 {
d1f1f6a8 948 _error->Error(_("I wasn't able to locate a file for the %s package. "
7a3c2ab0
AL
949 "This might mean you need to manually fix this package. "
950 "(due to missing arch)"),
813c8eea 951 Version.ParentPkg().Name());
bdae53f1
AL
952 return;
953 }
813c8eea 954
b2e465d6
AL
955 /* We need to find a filename to determine the extension. We make the
956 assumption here that all the available sources for this version share
957 the same extension.. */
958 // Skip not source sources, they do not have file fields.
959 for (; Vf.end() == false; Vf++)
960 {
961 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
962 continue;
963 break;
964 }
965
966 // Does not really matter here.. we are going to fail out below
967 if (Vf.end() != true)
968 {
969 // If this fails to get a file name we will bomb out below.
970 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
971 if (_error->PendingError() == true)
972 return;
973
974 // Generate the final file name as: package_version_arch.foo
975 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
976 QuoteString(Version.VerStr(),"_:") + '_' +
977 QuoteString(Version.Arch(),"_:.") +
978 "." + flExtension(Parse.FileName());
979 }
b3d44315
MV
980
981 // check if we have one trusted source for the package. if so, switch
982 // to "TrustedOnly" mode
983 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; i++)
984 {
985 pkgIndexFile *Index;
986 if (Sources->FindIndex(i.File(),Index) == false)
987 continue;
988 if (_config->FindB("Debug::pkgAcquire::Auth", false))
989 {
990 std::cerr << "Checking index: " << Index->Describe()
991 << "(Trusted=" << Index->IsTrusted() << ")\n";
992 }
993 if (Index->IsTrusted()) {
994 Trusted = true;
995 break;
996 }
997 }
998
03e39e59 999 // Select a source
b185acc2 1000 if (QueueNext() == false && _error->PendingError() == false)
b2e465d6
AL
1001 _error->Error(_("I wasn't able to locate file for the %s package. "
1002 "This might mean you need to manually fix this package."),
b185acc2
AL
1003 Version.ParentPkg().Name());
1004}
1005 /*}}}*/
1006// AcqArchive::QueueNext - Queue the next file source /*{{{*/
1007// ---------------------------------------------------------------------
17caf1b1
AL
1008/* This queues the next available file version for download. It checks if
1009 the archive is already available in the cache and stashs the MD5 for
1010 checking later. */
b185acc2 1011bool pkgAcqArchive::QueueNext()
b2e465d6 1012{
03e39e59
AL
1013 for (; Vf.end() == false; Vf++)
1014 {
1015 // Ignore not source sources
1016 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1017 continue;
1018
1019 // Try to cross match against the source list
b2e465d6
AL
1020 pkgIndexFile *Index;
1021 if (Sources->FindIndex(Vf.File(),Index) == false)
1022 continue;
03e39e59 1023
b3d44315
MV
1024 // only try to get a trusted package from another source if that source
1025 // is also trusted
1026 if(Trusted && !Index->IsTrusted())
1027 continue;
1028
03e39e59
AL
1029 // Grab the text package record
1030 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1031 if (_error->PendingError() == true)
b185acc2 1032 return false;
03e39e59 1033
b2e465d6 1034 string PkgFile = Parse.FileName();
03e39e59
AL
1035 MD5 = Parse.MD5Hash();
1036 if (PkgFile.empty() == true)
b2e465d6
AL
1037 return _error->Error(_("The package index files are corrupted. No Filename: "
1038 "field for package %s."),
1039 Version.ParentPkg().Name());
a6568219 1040
b3d44315
MV
1041 Desc.URI = Index->ArchiveURI(PkgFile);
1042 Desc.Description = Index->ArchiveInfo(Version);
1043 Desc.Owner = this;
1044 Desc.ShortDesc = Version.ParentPkg().Name();
1045
17caf1b1 1046 // See if we already have the file. (Legacy filenames)
a6568219
AL
1047 FileSize = Version->Size;
1048 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
1049 struct stat Buf;
1050 if (stat(FinalFile.c_str(),&Buf) == 0)
1051 {
1052 // Make sure the size matches
1053 if ((unsigned)Buf.st_size == Version->Size)
1054 {
1055 Complete = true;
1056 Local = true;
1057 Status = StatDone;
30e1eab5 1058 StoreFilename = DestFile = FinalFile;
b185acc2 1059 return true;
a6568219
AL
1060 }
1061
6b1ff003
AL
1062 /* Hmm, we have a file and its size does not match, this means it is
1063 an old style mismatched arch */
a6568219
AL
1064 unlink(FinalFile.c_str());
1065 }
17caf1b1
AL
1066
1067 // Check it again using the new style output filenames
1068 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
1069 if (stat(FinalFile.c_str(),&Buf) == 0)
1070 {
1071 // Make sure the size matches
1072 if ((unsigned)Buf.st_size == Version->Size)
1073 {
1074 Complete = true;
1075 Local = true;
1076 Status = StatDone;
1077 StoreFilename = DestFile = FinalFile;
1078 return true;
1079 }
1080
1081 /* Hmm, we have a file and its size does not match, this shouldnt
1082 happen.. */
1083 unlink(FinalFile.c_str());
1084 }
1085
1086 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
6b1ff003
AL
1087
1088 // Check the destination file
1089 if (stat(DestFile.c_str(),&Buf) == 0)
1090 {
1091 // Hmm, the partial file is too big, erase it
1092 if ((unsigned)Buf.st_size > Version->Size)
1093 unlink(DestFile.c_str());
1094 else
1095 PartialSize = Buf.st_size;
1096 }
1097
03e39e59 1098 // Create the item
b2e465d6
AL
1099 Local = false;
1100 Desc.URI = Index->ArchiveURI(PkgFile);
1101 Desc.Description = Index->ArchiveInfo(Version);
03e39e59
AL
1102 Desc.Owner = this;
1103 Desc.ShortDesc = Version.ParentPkg().Name();
1104 QueueURI(Desc);
b185acc2
AL
1105
1106 Vf++;
1107 return true;
03e39e59 1108 }
b185acc2
AL
1109 return false;
1110}
03e39e59
AL
1111 /*}}}*/
1112// AcqArchive::Done - Finished fetching /*{{{*/
1113// ---------------------------------------------------------------------
1114/* */
459681d3
AL
1115void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash,
1116 pkgAcquire::MethodConfig *Cfg)
03e39e59 1117{
459681d3 1118 Item::Done(Message,Size,Md5Hash,Cfg);
03e39e59
AL
1119
1120 // Check the size
1121 if (Size != Version->Size)
1122 {
bdae53f1 1123 Status = StatError;
b2e465d6 1124 ErrorText = _("Size mismatch");
03e39e59
AL
1125 return;
1126 }
1127
1128 // Check the md5
1129 if (Md5Hash.empty() == false && MD5.empty() == false)
1130 {
1131 if (Md5Hash != MD5)
1132 {
bdae53f1 1133 Status = StatError;
b2e465d6 1134 ErrorText = _("MD5Sum mismatch");
9978c7b0 1135 Rename(DestFile,DestFile + ".FAILED");
03e39e59
AL
1136 return;
1137 }
1138 }
a6568219
AL
1139
1140 // Grab the output filename
03e39e59
AL
1141 string FileName = LookupTag(Message,"Filename");
1142 if (FileName.empty() == true)
1143 {
1144 Status = StatError;
1145 ErrorText = "Method gave a blank filename";
1146 return;
1147 }
a6568219
AL
1148
1149 Complete = true;
30e1eab5
AL
1150
1151 // Reference filename
a6568219
AL
1152 if (FileName != DestFile)
1153 {
30e1eab5 1154 StoreFilename = DestFile = FileName;
a6568219
AL
1155 Local = true;
1156 return;
1157 }
1158
1159 // Done, move it into position
1160 string FinalFile = _config->FindDir("Dir::Cache::Archives");
17caf1b1 1161 FinalFile += flNotDir(StoreFilename);
a6568219 1162 Rename(DestFile,FinalFile);
03e39e59 1163
30e1eab5 1164 StoreFilename = DestFile = FinalFile;
03e39e59
AL
1165 Complete = true;
1166}
1167 /*}}}*/
db890fdb
AL
1168// AcqArchive::Failed - Failure handler /*{{{*/
1169// ---------------------------------------------------------------------
1170/* Here we try other sources */
7d8afa39 1171void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
db890fdb
AL
1172{
1173 ErrorText = LookupTag(Message,"Message");
b2e465d6
AL
1174
1175 /* We don't really want to retry on failed media swaps, this prevents
1176 that. An interesting observation is that permanent failures are not
1177 recorded. */
1178 if (Cnf->Removable == true &&
1179 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1180 {
1181 // Vf = Version.FileList();
1182 while (Vf.end() == false) Vf++;
1183 StoreFilename = string();
1184 Item::Failed(Message,Cnf);
1185 return;
1186 }
1187
db890fdb 1188 if (QueueNext() == false)
7d8afa39
AL
1189 {
1190 // This is the retry counter
1191 if (Retries != 0 &&
1192 Cnf->LocalOnly == false &&
1193 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1194 {
1195 Retries--;
1196 Vf = Version.FileList();
1197 if (QueueNext() == true)
1198 return;
1199 }
1200
9dbb421f 1201 StoreFilename = string();
7d8afa39
AL
1202 Item::Failed(Message,Cnf);
1203 }
db890fdb
AL
1204}
1205 /*}}}*/
b3d44315
MV
1206// AcqArchive::IsTrusted - Determine whether this archive comes from a
1207// trusted source /*{{{*/
1208// ---------------------------------------------------------------------
1209bool pkgAcqArchive::IsTrusted()
1210{
1211 return Trusted;
1212}
1213
ab559b35
AL
1214// AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
1215// ---------------------------------------------------------------------
1216/* */
1217void pkgAcqArchive::Finished()
1218{
1219 if (Status == pkgAcquire::Item::StatDone &&
1220 Complete == true)
1221 return;
1222 StoreFilename = string();
1223}
1224 /*}}}*/
36375005
AL
1225
1226// AcqFile::pkgAcqFile - Constructor /*{{{*/
1227// ---------------------------------------------------------------------
1228/* The file is added to the queue */
1229pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string MD5,
1230 unsigned long Size,string Dsc,string ShortDesc) :
b3c39978 1231 Item(Owner), Md5Hash(MD5)
36375005 1232{
08cfc005
AL
1233 Retries = _config->FindI("Acquire::Retries",0);
1234
36375005
AL
1235 DestFile = flNotDir(URI);
1236
1237 // Create the item
1238 Desc.URI = URI;
1239 Desc.Description = Dsc;
1240 Desc.Owner = this;
1241
1242 // Set the short description to the archive component
1243 Desc.ShortDesc = ShortDesc;
1244
1245 // Get the transfer sizes
1246 FileSize = Size;
1247 struct stat Buf;
1248 if (stat(DestFile.c_str(),&Buf) == 0)
1249 {
1250 // Hmm, the partial file is too big, erase it
1251 if ((unsigned)Buf.st_size > Size)
1252 unlink(DestFile.c_str());
1253 else
1254 PartialSize = Buf.st_size;
1255 }
1256
1257 QueueURI(Desc);
1258}
1259 /*}}}*/
1260// AcqFile::Done - Item downloaded OK /*{{{*/
1261// ---------------------------------------------------------------------
1262/* */
459681d3
AL
1263void pkgAcqFile::Done(string Message,unsigned long Size,string MD5,
1264 pkgAcquire::MethodConfig *Cnf)
36375005 1265{
b3c39978
AL
1266 // Check the md5
1267 if (Md5Hash.empty() == false && MD5.empty() == false)
1268 {
1269 if (Md5Hash != MD5)
1270 {
1271 Status = StatError;
1272 ErrorText = "MD5Sum mismatch";
1273 Rename(DestFile,DestFile + ".FAILED");
1274 return;
1275 }
1276 }
1277
459681d3 1278 Item::Done(Message,Size,MD5,Cnf);
36375005
AL
1279
1280 string FileName = LookupTag(Message,"Filename");
1281 if (FileName.empty() == true)
1282 {
1283 Status = StatError;
1284 ErrorText = "Method gave a blank filename";
1285 return;
1286 }
1287
1288 Complete = true;
1289
1290 // The files timestamp matches
1291 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1292 return;
1293
1294 // We have to copy it into place
1295 if (FileName != DestFile)
1296 {
1297 Local = true;
459681d3
AL
1298 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
1299 Cnf->Removable == true)
917ae805
AL
1300 {
1301 Desc.URI = "copy:" + FileName;
1302 QueueURI(Desc);
1303 return;
1304 }
1305
83ab33fc
AL
1306 // Erase the file if it is a symlink so we can overwrite it
1307 struct stat St;
1308 if (lstat(DestFile.c_str(),&St) == 0)
1309 {
1310 if (S_ISLNK(St.st_mode) != 0)
1311 unlink(DestFile.c_str());
1312 }
1313
1314 // Symlink the file
917ae805
AL
1315 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
1316 {
83ab33fc 1317 ErrorText = "Link to " + DestFile + " failure ";
917ae805
AL
1318 Status = StatError;
1319 Complete = false;
1320 }
36375005
AL
1321 }
1322}
1323 /*}}}*/
08cfc005
AL
1324// AcqFile::Failed - Failure handler /*{{{*/
1325// ---------------------------------------------------------------------
1326/* Here we try other sources */
1327void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1328{
1329 ErrorText = LookupTag(Message,"Message");
1330
1331 // This is the retry counter
1332 if (Retries != 0 &&
1333 Cnf->LocalOnly == false &&
1334 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1335 {
1336 Retries--;
1337 QueueURI(Desc);
1338 return;
1339 }
1340
1341 Item::Failed(Message,Cnf);
1342}
1343 /*}}}*/