]> git.saurik.com Git - apt.git/blame - apt-pkg/acquire-item.cc
Merge branch 'feature/acq-trans' into feature/expected-size
[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 /*{{{*/
ea542140
DK
16#include <config.h>
17
0118833a
AL
18#include <apt-pkg/acquire-item.h>
19#include <apt-pkg/configuration.h>
e878aedb 20#include <apt-pkg/aptconfiguration.h>
b2e465d6 21#include <apt-pkg/sourcelist.h>
03e39e59 22#include <apt-pkg/error.h>
cdcc6d34 23#include <apt-pkg/strutl.h>
36375005 24#include <apt-pkg/fileutl.h>
ac5b205a
MV
25#include <apt-pkg/sha1.h>
26#include <apt-pkg/tagfile.h>
472ff00e 27#include <apt-pkg/indexrecords.h>
453b82a3
DK
28#include <apt-pkg/acquire.h>
29#include <apt-pkg/hashes.h>
30#include <apt-pkg/indexfile.h>
31#include <apt-pkg/pkgcache.h>
32#include <apt-pkg/cacheiterators.h>
33#include <apt-pkg/pkgrecords.h>
34
35#include <stddef.h>
36#include <stdlib.h>
37#include <string.h>
38#include <iostream>
39#include <vector>
0a8a80e5
AL
40#include <sys/stat.h>
41#include <unistd.h>
c88edf1d 42#include <errno.h>
5819a761 43#include <string>
ac5b205a 44#include <sstream>
c88edf1d 45#include <stdio.h>
1ddb8596 46#include <ctime>
5684f71f
DK
47#include <sys/types.h>
48#include <pwd.h>
49#include <grp.h>
ea542140
DK
50
51#include <apti18n.h>
0118833a
AL
52 /*}}}*/
53
b3d44315 54using namespace std;
5819a761 55
b3501edb
DK
56static void printHashSumComparision(std::string const &URI, HashStringList const &Expected, HashStringList const &Actual) /*{{{*/
57{
58 if (_config->FindB("Debug::Acquire::HashSumMismatch", false) == false)
59 return;
60 std::cerr << std::endl << URI << ":" << std::endl << " Expected Hash: " << std::endl;
61 for (HashStringList::const_iterator hs = Expected.begin(); hs != Expected.end(); ++hs)
62 std::cerr << "\t- " << hs->toStr() << std::endl;
63 std::cerr << " Actual Hash: " << std::endl;
64 for (HashStringList::const_iterator hs = Actual.begin(); hs != Actual.end(); ++hs)
65 std::cerr << "\t- " << hs->toStr() << std::endl;
66}
67 /*}}}*/
ea7682a0 68static void ChangeOwnerAndPermissionOfFile(char const * const requester, char const * const file, char const * const user, char const * const group, mode_t const mode)
5684f71f
DK
69{
70 // ensure the file is owned by root and has good permissions
71 struct passwd const * const pw = getpwnam(user);
72 struct group const * const gr = getgrnam(group);
73 if (getuid() == 0) // if we aren't root, we can't chown, so don't try it
74 {
75 if (pw != NULL && gr != NULL && chown(file, pw->pw_uid, gr->gr_gid) != 0)
76 _error->WarningE(requester, "chown to %s:%s of file %s failed", user, group, file);
77 }
78 if (chmod(file, mode) != 0)
79 _error->WarningE(requester, "chmod 0%o of file %s failed", mode, file);
80}
ea7682a0 81static std::string GetPartialFileName(std::string const &file)
5684f71f
DK
82{
83 std::string DestFile = _config->FindDir("Dir::State::lists") + "partial/";
84 DestFile += file;
85 return DestFile;
86}
ea7682a0 87static std::string GetPartialFileNameFromURI(std::string const &uri)
5684f71f 88{
ea7682a0 89 return GetPartialFileName(URItoFileName(uri));
5684f71f
DK
90}
91
b3501edb 92
0118833a 93// Acquire::Item::Item - Constructor /*{{{*/
ffbe056d
DK
94#if __GNUC__ >= 4
95 #pragma GCC diagnostic push
96 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
97#endif
e05672e8
MV
98pkgAcquire::Item::Item(pkgAcquire *Owner,
99 HashStringList const &ExpectedHashes,
715c65de 100 pkgAcqMetaBase *TransactionManager)
e05672e8 101 : Owner(Owner), FileSize(0), PartialSize(0), Mode(0), ID(0), Complete(false),
715c65de 102 Local(false), QueueCounter(0), TransactionManager(TransactionManager),
e05672e8 103 ExpectedAdditionalItems(0), ExpectedHashes(ExpectedHashes)
0118833a
AL
104{
105 Owner->Add(this);
c88edf1d 106 Status = StatIdle;
715c65de
MV
107 if(TransactionManager != NULL)
108 TransactionManager->Add(this);
0118833a 109}
ffbe056d
DK
110#if __GNUC__ >= 4
111 #pragma GCC diagnostic pop
112#endif
0118833a
AL
113 /*}}}*/
114// Acquire::Item::~Item - Destructor /*{{{*/
115// ---------------------------------------------------------------------
116/* */
117pkgAcquire::Item::~Item()
118{
119 Owner->Remove(this);
120}
121 /*}}}*/
c88edf1d
AL
122// Acquire::Item::Failed - Item failed to download /*{{{*/
123// ---------------------------------------------------------------------
93bf083d
AL
124/* We return to an idle state if there are still other queues that could
125 fetch this object */
7d8afa39 126void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
c88edf1d 127{
2737f28a
MV
128 if(ErrorText == "")
129 ErrorText = LookupTag(Message,"Message");
361593e9 130 UsedMirror = LookupTag(Message,"UsedMirror");
c88edf1d 131 if (QueueCounter <= 1)
93bf083d 132 {
a72ace20 133 /* This indicates that the file is not available right now but might
7d8afa39 134 be sometime later. If we do a retry cycle then this should be
17caf1b1 135 retried [CDROMs] */
4dbfe436 136 if (Cnf != NULL && Cnf->LocalOnly == true &&
7d8afa39 137 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
a72ace20
AL
138 {
139 Status = StatIdle;
681d76d0 140 Dequeue();
a72ace20
AL
141 return;
142 }
7e5f33eb 143
93bf083d 144 Status = StatError;
4dbfe436 145 Complete = false;
681d76d0 146 Dequeue();
4dbfe436
DK
147 }
148 else
149 Status = StatIdle;
23c5897c 150
36280399 151 // report mirror failure back to LP if we actually use a mirror
f0b509cd
MV
152 string FailReason = LookupTag(Message, "FailReason");
153 if(FailReason.size() != 0)
154 ReportMirrorFailure(FailReason);
155 else
156 ReportMirrorFailure(ErrorText);
c88edf1d
AL
157}
158 /*}}}*/
8267fe24
AL
159// Acquire::Item::Start - Item has begun to download /*{{{*/
160// ---------------------------------------------------------------------
17caf1b1
AL
161/* Stash status and the file size. Note that setting Complete means
162 sub-phases of the acquire process such as decompresion are operating */
73da43e9 163void pkgAcquire::Item::Start(string /*Message*/,unsigned long long Size)
8267fe24
AL
164{
165 Status = StatFetching;
166 if (FileSize == 0 && Complete == false)
167 FileSize = Size;
168}
169 /*}}}*/
c88edf1d
AL
170// Acquire::Item::Done - Item downloaded OK /*{{{*/
171// ---------------------------------------------------------------------
172/* */
b3501edb 173void pkgAcquire::Item::Done(string Message,unsigned long long Size,HashStringList const &/*Hash*/,
65512241 174 pkgAcquire::MethodConfig * /*Cnf*/)
c88edf1d 175{
b98f2859
AL
176 // We just downloaded something..
177 string FileName = LookupTag(Message,"Filename");
1f4dd8fd 178 UsedMirror = LookupTag(Message,"UsedMirror");
8f30ca30 179 if (Complete == false && !Local && FileName == DestFile)
b98f2859
AL
180 {
181 if (Owner->Log != 0)
182 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
183 }
aa0e1101
AL
184
185 if (FileSize == 0)
186 FileSize= Size;
c88edf1d
AL
187 Status = StatDone;
188 ErrorText = string();
189 Owner->Dequeue(this);
190}
191 /*}}}*/
8b89e57f
AL
192// Acquire::Item::Rename - Rename a file /*{{{*/
193// ---------------------------------------------------------------------
1e3f4083 194/* This helper function is used by a lot of item methods as their final
8b89e57f 195 step */
03bfbc96 196bool pkgAcquire::Item::Rename(string From,string To)
8b89e57f
AL
197{
198 if (rename(From.c_str(),To.c_str()) != 0)
199 {
200 char S[300];
0fcd01de 201 snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
8b89e57f
AL
202 From.c_str(),To.c_str());
203 Status = StatError;
03bfbc96
MV
204 ErrorText += S;
205 return false;
7a3c2ab0 206 }
03bfbc96 207 return true;
8b89e57f
AL
208}
209 /*}}}*/
5684f71f
DK
210
211void pkgAcquire::Item::QueueURI(ItemDesc &Item)
212{
4dbfe436 213 if (RealFileExists(DestFile))
ea7682a0 214 ChangeOwnerAndPermissionOfFile("GetPartialFileName", DestFile.c_str(), "_apt", "root", 0600);
5684f71f
DK
215 Owner->Enqueue(Item);
216}
217void pkgAcquire::Item::Dequeue()
218{
219 Owner->Dequeue(this);
220}
221
3c8030a4
DK
222bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const error)/*{{{*/
223{
224 if(FileExists(DestFile))
225 Rename(DestFile, DestFile + ".FAILED");
226
227 switch (error)
228 {
229 case HashSumMismatch:
230 ErrorText = _("Hash Sum mismatch");
231 Status = StatAuthError;
232 ReportMirrorFailure("HashChecksumFailure");
233 break;
234 case SizeMismatch:
235 ErrorText = _("Size mismatch");
236 Status = StatAuthError;
237 ReportMirrorFailure("SizeFailure");
238 break;
239 case InvalidFormat:
240 ErrorText = _("Invalid file format");
241 Status = StatError;
242 // do not report as usually its not the mirrors fault, but Portal/Proxy
243 break;
631a7dc7
MV
244 case SignatureError:
245 ErrorText = _("Signature error");
246 Status = StatError;
247 break;
248 case NotClearsigned:
249 ErrorText = _("Does not start with a cleartext signature");
250 Status = StatError;
251 break;
3c8030a4
DK
252 }
253 return false;
254}
255 /*}}}*/
8267fbd9 256void pkgAcquire::Item::SetActiveSubprocess(const std::string &subprocess)/*{{{*/
eeac6897
MV
257{
258 ActiveSubprocess = subprocess;
259#if __GNUC__ >= 4
260 #pragma GCC diagnostic push
261 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
262#endif
263 Mode = ActiveSubprocess.c_str();
264#if __GNUC__ >= 4
265 #pragma GCC diagnostic pop
266#endif
267}
8267fbd9 268 /*}}}*/
c91d9a63
DK
269// Acquire::Item::ReportMirrorFailure /*{{{*/
270// ---------------------------------------------------------------------
36280399
MV
271void pkgAcquire::Item::ReportMirrorFailure(string FailCode)
272{
59271f62
MV
273 // we only act if a mirror was used at all
274 if(UsedMirror.empty())
275 return;
36280399
MV
276#if 0
277 std::cerr << "\nReportMirrorFailure: "
278 << UsedMirror
59271f62 279 << " Uri: " << DescURI()
36280399
MV
280 << " FailCode: "
281 << FailCode << std::endl;
282#endif
283 const char *Args[40];
284 unsigned int i = 0;
285 string report = _config->Find("Methods::Mirror::ProblemReporting",
3f599bb7 286 "/usr/lib/apt/apt-report-mirror-failure");
36280399
MV
287 if(!FileExists(report))
288 return;
289 Args[i++] = report.c_str();
290 Args[i++] = UsedMirror.c_str();
f0b509cd 291 Args[i++] = DescURI().c_str();
36280399 292 Args[i++] = FailCode.c_str();
361593e9 293 Args[i++] = NULL;
36280399
MV
294 pid_t pid = ExecFork();
295 if(pid < 0)
296 {
297 _error->Error("ReportMirrorFailure Fork failed");
298 return;
299 }
300 else if(pid == 0)
301 {
361593e9
MV
302 execvp(Args[0], (char**)Args);
303 std::cerr << "Could not exec " << Args[0] << std::endl;
304 _exit(100);
36280399
MV
305 }
306 if(!ExecWait(pid, "report-mirror-failure"))
307 {
308 _error->Warning("Couldn't report problem to '%s'",
361593e9 309 _config->Find("Methods::Mirror::ProblemReporting").c_str());
36280399
MV
310 }
311}
c91d9a63 312 /*}}}*/
92fcbfc1 313// AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
ac5b205a 314// ---------------------------------------------------------------------
1e3f4083 315/* Get the DiffIndex file first and see if there are patches available
2237bd01
MV
316 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
317 * patches. If anything goes wrong in that process, it will fall back to
318 * the original packages file
ac5b205a 319 */
e05672e8 320pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
715c65de 321 pkgAcqMetaBase *TransactionManager,
e110d7bf
MV
322 IndexTarget const * const Target,
323 HashStringList const &ExpectedHashes,
e39698a4 324 indexRecords *MetaIndexParser)
715c65de 325 : pkgAcqBaseIndex(Owner, TransactionManager, Target, ExpectedHashes,
a64bf0eb 326 MetaIndexParser), PackagesFileReadyInPartial(false)
ac5b205a
MV
327{
328
ac5b205a
MV
329 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
330
a64bf0eb 331 RealURI = Target->URI;
ac5b205a 332 Desc.Owner = this;
4d0818cc 333 Desc.Description = Target->Description + ".diff/Index";
e39698a4
MV
334 Desc.ShortDesc = Target->ShortDesc;
335 Desc.URI = Target->URI + ".diff/Index";
2237bd01 336
ea7682a0 337 DestFile = GetPartialFileNameFromURI(Desc.URI);
2237bd01
MV
338
339 if(Debug)
340 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
ac5b205a 341
2237bd01 342 // look for the current package file
ac5b205a
MV
343 CurrentPackagesFile = _config->FindDir("Dir::State::lists");
344 CurrentPackagesFile += URItoFileName(RealURI);
345
b4e57d2d
MV
346 // FIXME: this file:/ check is a hack to prevent fetching
347 // from local sources. this is really silly, and
348 // should be fixed cleanly as soon as possible
ac5b205a 349 if(!FileExists(CurrentPackagesFile) ||
81fcf9e2 350 Desc.URI.substr(0,strlen("file:/")) == "file:/")
2ac3eeb6 351 {
ac5b205a 352 // we don't have a pkg file or we don't want to queue
f6d4ab9a 353 Failed("No index file, local or canceld by user", NULL);
ac5b205a
MV
354 return;
355 }
356
1e4a2b76
AT
357 if(Debug)
358 std::clog << "pkgAcqDiffIndex::pkgAcqDiffIndex(): "
359 << CurrentPackagesFile << std::endl;
360
ac5b205a 361 QueueURI(Desc);
2237bd01 362
ac5b205a 363}
92fcbfc1 364 /*}}}*/
6cb30d01
MV
365// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
366// ---------------------------------------------------------------------
367/* The only header we use is the last-modified header. */
b3501edb 368string pkgAcqDiffIndex::Custom600Headers() const
6cb30d01 369{
6cb30d01 370 string Final = _config->FindDir("Dir::State::lists");
31b9d841 371 Final += URItoFileName(Desc.URI);
4d0818cc 372
6cb30d01
MV
373 if(Debug)
374 std::clog << "Custom600Header-IMS: " << Final << std::endl;
375
376 struct stat Buf;
377 if (stat(Final.c_str(),&Buf) != 0)
378 return "\nIndex-File: true";
379
380 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
381}
92fcbfc1
DK
382 /*}}}*/
383bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) /*{{{*/
2237bd01 384{
f6d4ab9a
DK
385 // failing here is fine: our caller will take care of trying to
386 // get the complete file if patching fails
2237bd01 387 if(Debug)
1e4a2b76
AT
388 std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
389 << std::endl;
2237bd01 390
2237bd01
MV
391 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
392 pkgTagFile TF(&Fd);
393 if (_error->PendingError() == true)
394 return false;
395
f6d4ab9a
DK
396 pkgTagSection Tags;
397 if(unlikely(TF.Step(Tags) == false))
398 return false;
002d9943 399
f6d4ab9a
DK
400 HashStringList ServerHashes;
401 unsigned long long ServerSize = 0;
402
403 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
404 {
405 std::string tagname = *type;
406 tagname.append("-Current");
407 std::string const tmp = Tags.FindS(tagname.c_str());
408 if (tmp.empty() == true)
409 continue;
410
411 string hash;
412 unsigned long long size;
2237bd01 413 std::stringstream ss(tmp);
f6d4ab9a
DK
414 ss >> hash >> size;
415 if (unlikely(hash.empty() == true))
416 continue;
417 if (unlikely(ServerSize != 0 && ServerSize != size))
418 continue;
419 ServerHashes.push_back(HashString(*type, hash));
420 ServerSize = size;
421 }
2237bd01 422
f6d4ab9a
DK
423 if (ServerHashes.usable() == false)
424 {
425 if (Debug == true)
426 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Did not find a good hashsum in the index" << std::endl;
427 return false;
428 }
2237bd01 429
f6d4ab9a
DK
430 if (ServerHashes != HashSums())
431 {
432 if (Debug == true)
2ac3eeb6 433 {
f6d4ab9a 434 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Index has different hashes than parser, probably older, so fail pdiffing" << std::endl;
4d0818cc 435 printHashSumComparision(CurrentPackagesFile, ServerHashes, HashSums());
5e1ed088 436 }
f6d4ab9a
DK
437 return false;
438 }
439
440 if (ServerHashes.VerifyFile(CurrentPackagesFile) == true)
441 {
442 // we have the same sha1 as the server so we are done here
443 if(Debug)
4d0818cc
MV
444 std::clog << "pkgAcqDiffIndex: Package file " << CurrentPackagesFile << " is up-to-date" << std::endl;
445
f6d4ab9a
DK
446 // list cleanup needs to know that this file as well as the already
447 // present index is ours, so we create an empty diff to save it for us
4d0818cc
MV
448 new pkgAcqIndexDiffs(Owner, TransactionManager, Target,
449 ExpectedHashes, MetaIndexParser);
f6d4ab9a
DK
450 return true;
451 }
452
453 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
454 Hashes LocalHashesCalc;
455 LocalHashesCalc.AddFD(fd);
456 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
457
458 if(Debug)
459 std::clog << "Server-Current: " << ServerHashes.find(NULL)->toStr() << " and we start at "
460 << fd.Name() << " " << fd.FileSize() << " " << LocalHashes.find(NULL)->toStr() << std::endl;
461
462 // parse all of (provided) history
463 vector<DiffInfo> available_patches;
464 bool firstAcceptedHashes = true;
465 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
466 {
467 if (LocalHashes.find(*type) == NULL)
468 continue;
469
470 std::string tagname = *type;
471 tagname.append("-History");
472 std::string const tmp = Tags.FindS(tagname.c_str());
473 if (tmp.empty() == true)
474 continue;
475
476 string hash, filename;
477 unsigned long long size;
478 std::stringstream ss(tmp);
479
480 while (ss >> hash >> size >> filename)
2ac3eeb6 481 {
f6d4ab9a
DK
482 if (unlikely(hash.empty() == true || filename.empty() == true))
483 continue;
002d9943 484
f6d4ab9a
DK
485 // see if we have a record for this file already
486 std::vector<DiffInfo>::iterator cur = available_patches.begin();
487 for (; cur != available_patches.end(); ++cur)
2ac3eeb6 488 {
f6d4ab9a 489 if (cur->file != filename || unlikely(cur->result_size != size))
02dceb31 490 continue;
f6d4ab9a
DK
491 cur->result_hashes.push_back(HashString(*type, hash));
492 break;
02dceb31 493 }
f6d4ab9a
DK
494 if (cur != available_patches.end())
495 continue;
496 if (firstAcceptedHashes == true)
497 {
498 DiffInfo next;
499 next.file = filename;
500 next.result_hashes.push_back(HashString(*type, hash));
501 next.result_size = size;
502 next.patch_size = 0;
503 available_patches.push_back(next);
504 }
505 else
02dceb31 506 {
f6d4ab9a
DK
507 if (Debug == true)
508 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
509 << " wasn't in the list for the first parsed hash! (history)" << std::endl;
510 break;
2237bd01
MV
511 }
512 }
f6d4ab9a
DK
513 firstAcceptedHashes = false;
514 }
515
516 if (unlikely(available_patches.empty() == true))
517 {
518 if (Debug)
519 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
520 << "Couldn't find any patches for the patch series." << std::endl;
521 return false;
522 }
523
524 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
525 {
526 if (LocalHashes.find(*type) == NULL)
527 continue;
528
529 std::string tagname = *type;
530 tagname.append("-Patches");
531 std::string const tmp = Tags.FindS(tagname.c_str());
532 if (tmp.empty() == true)
533 continue;
2237bd01 534
f6d4ab9a
DK
535 string hash, filename;
536 unsigned long long size;
537 std::stringstream ss(tmp);
2237bd01 538
f6d4ab9a 539 while (ss >> hash >> size >> filename)
2ac3eeb6 540 {
f6d4ab9a
DK
541 if (unlikely(hash.empty() == true || filename.empty() == true))
542 continue;
47d2bc78 543
f6d4ab9a
DK
544 // see if we have a record for this file already
545 std::vector<DiffInfo>::iterator cur = available_patches.begin();
546 for (; cur != available_patches.end(); ++cur)
47d2bc78 547 {
f6d4ab9a
DK
548 if (cur->file != filename)
549 continue;
550 if (unlikely(cur->patch_size != 0 && cur->patch_size != size))
551 continue;
552 cur->patch_hashes.push_back(HashString(*type, hash));
553 cur->patch_size = size;
554 break;
47d2bc78 555 }
f6d4ab9a
DK
556 if (cur != available_patches.end())
557 continue;
558 if (Debug == true)
559 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
560 << " wasn't in the list for the first parsed hash! (patches)" << std::endl;
561 break;
2237bd01
MV
562 }
563 }
f6d4ab9a
DK
564
565 bool foundStart = false;
566 for (std::vector<DiffInfo>::iterator cur = available_patches.begin();
567 cur != available_patches.end(); ++cur)
568 {
569 if (LocalHashes != cur->result_hashes)
570 continue;
571
572 available_patches.erase(available_patches.begin(), cur);
573 foundStart = true;
574 break;
575 }
576
577 if (foundStart == false || unlikely(available_patches.empty() == true))
578 {
579 if (Debug)
580 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
581 << "Couldn't find the start of the patch series." << std::endl;
582 return false;
583 }
584
585 // patching with too many files is rather slow compared to a fast download
586 unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
587 if (fileLimit != 0 && fileLimit < available_patches.size())
588 {
589 if (Debug)
590 std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
591 << ") so fallback to complete download" << std::endl;
592 return false;
593 }
594
595 // calculate the size of all patches we have to get
596 // note that all sizes are uncompressed, while we download compressed files
597 unsigned long long patchesSize = 0;
598 for (std::vector<DiffInfo>::const_iterator cur = available_patches.begin();
599 cur != available_patches.end(); ++cur)
600 patchesSize += cur->patch_size;
601 unsigned long long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100);
602 if (false && sizeLimit > 0 && (sizeLimit/100) < patchesSize)
603 {
604 if (Debug)
605 std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100
606 << ") so fallback to complete download" << std::endl;
607 return false;
608 }
609
4d0818cc
MV
610 // FIXME: make this use the method
611 PackagesFileReadyInPartial = true;
612 std::string const Partial = GetPartialFileNameFromURI(RealURI);
613
614 FileFd From(CurrentPackagesFile, FileFd::ReadOnly);
615 FileFd To(Partial, FileFd::WriteEmpty);
616 if(CopyFile(From, To) == false)
617 return _error->Errno("CopyFile", "failed to copy");
05aab406 618
05aab406 619 if(Debug)
4d0818cc
MV
620 std::cerr << "Done copying " << CurrentPackagesFile
621 << " -> " << Partial
622 << std::endl;
623
f6d4ab9a
DK
624 // we have something, queue the diffs
625 string::size_type const last_space = Description.rfind(" ");
626 if(last_space != string::npos)
627 Description.erase(last_space, Description.size()-last_space);
628
629 /* decide if we should download patches one by one or in one go:
630 The first is good if the server merges patches, but many don't so client
631 based merging can be attempt in which case the second is better.
632 "bad things" will happen if patches are merged on the server,
633 but client side merging is attempt as well */
634 bool pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true);
635 if (pdiff_merge == true)
636 {
637 // reprepro adds this flag if it has merged patches on the server
638 std::string const precedence = Tags.FindS("X-Patch-Precedence");
639 pdiff_merge = (precedence != "merged");
640 }
641
642 if (pdiff_merge == false)
643 {
4d0818cc
MV
644 new pkgAcqIndexDiffs(Owner, TransactionManager, Target, ExpectedHashes,
645 MetaIndexParser, available_patches);
f6d4ab9a
DK
646 }
647 else
648 {
649 std::vector<pkgAcqIndexMergeDiffs*> *diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
650 for(size_t i = 0; i < available_patches.size(); ++i)
4d0818cc
MV
651 (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, TransactionManager,
652 Target,
f6d4ab9a
DK
653 ExpectedHashes,
654 MetaIndexParser,
655 available_patches[i],
656 diffs);
657 }
658
659 Complete = false;
660 Status = StatDone;
661 Dequeue();
662 return true;
2237bd01 663}
92fcbfc1 664 /*}}}*/
4dbfe436 665void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig * Cnf)/*{{{*/
2237bd01
MV
666{
667 if(Debug)
65512241 668 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
1e3f4083 669 << "Falling back to normal index file acquire" << std::endl;
2237bd01 670
715c65de 671 new pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser);
2237bd01 672
4dbfe436 673 Item::Failed(Message,Cnf);
2237bd01 674 Status = StatDone;
2237bd01 675}
92fcbfc1 676 /*}}}*/
b3501edb 677void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
2237bd01
MV
678 pkgAcquire::MethodConfig *Cnf)
679{
680 if(Debug)
681 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
682
b3501edb 683 Item::Done(Message, Size, Hashes, Cnf);
2237bd01 684
8d266656 685 // verify the index target
1e8ba0d4 686 if(Target && Target->MetaKey != "" && MetaIndexParser && Hashes.usable())
8d266656
MV
687 {
688 std::string IndexMetaKey = Target->MetaKey + ".diff/Index";
689 indexRecords::checkSum *Record = MetaIndexParser->Lookup(IndexMetaKey);
690 if(Record && Record->Hashes.usable() && Hashes != Record->Hashes)
691 {
692 RenameOnError(HashSumMismatch);
693 printHashSumComparision(RealURI, Record->Hashes, Hashes);
694 Failed(Message, Cnf);
695 return;
696 }
697
698 }
699
2237bd01 700 string FinalFile;
4d0818cc
MV
701 FinalFile = _config->FindDir("Dir::State::lists");
702 FinalFile += URItoFileName(Desc.URI);
2237bd01 703
4d0818cc
MV
704 if(StringToBool(LookupTag(Message,"IMS-Hit"),false))
705 DestFile = FinalFile;
2237bd01 706
22b2ef9d 707 if(!ParseDiffIndex(DestFile))
4dbfe436 708 return Failed("Message: Couldn't parse pdiff index", Cnf);
22b2ef9d
MV
709
710 // queue for final move
22b2ef9d 711 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2237bd01
MV
712
713 Complete = true;
714 Status = StatDone;
715 Dequeue();
716 return;
717}
92fcbfc1
DK
718 /*}}}*/
719// AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
2237bd01
MV
720// ---------------------------------------------------------------------
721/* The package diff is added to the queue. one object is constructed
722 * for each diff and the index
723 */
e05672e8 724pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
715c65de 725 pkgAcqMetaBase *TransactionManager,
c2184314 726 struct IndexTarget const * const Target,
e110d7bf 727 HashStringList const &ExpectedHashes,
c2184314 728 indexRecords *MetaIndexParser,
495e5cb2 729 vector<DiffInfo> diffs)
a64bf0eb 730 : pkgAcqBaseIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser),
f6d4ab9a 731 available_patches(diffs)
2237bd01 732{
ea7682a0 733 DestFile = GetPartialFileNameFromURI(Target->URI);
2237bd01
MV
734
735 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
736
a64bf0eb 737 RealURI = Target->URI;
2237bd01 738 Desc.Owner = this;
c2184314
MV
739 Description = Target->Description;
740 Desc.ShortDesc = Target->ShortDesc;
2237bd01 741
69c2ecbd 742 if(available_patches.empty() == true)
2ac3eeb6 743 {
03bfbc96
MV
744 // we are done (yeah!), check hashes against the final file
745 DestFile = _config->FindDir("Dir::State::lists");
746 DestFile += URItoFileName(Target->URI);
2237bd01 747 Finish(true);
2ac3eeb6
MV
748 }
749 else
750 {
2237bd01
MV
751 // get the next diff
752 State = StateFetchDiff;
753 QueueNextDiff();
754 }
755}
92fcbfc1 756 /*}}}*/
65512241 757void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
ac5b205a 758{
2237bd01 759 if(Debug)
65512241 760 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
1e3f4083 761 << "Falling back to normal index file acquire" << std::endl;
715c65de 762 new pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser);
ac5b205a
MV
763 Finish();
764}
92fcbfc1
DK
765 /*}}}*/
766// Finish - helper that cleans the item out of the fetcher queue /*{{{*/
ac5b205a
MV
767void pkgAcqIndexDiffs::Finish(bool allDone)
768{
d4ab7e9c
MV
769 if(Debug)
770 std::clog << "pkgAcqIndexDiffs::Finish(): "
771 << allDone << " "
772 << Desc.URI << std::endl;
773
ac5b205a
MV
774 // we restore the original name, this is required, otherwise
775 // the file will be cleaned
2ac3eeb6
MV
776 if(allDone)
777 {
fa3b260f 778 if(HashSums().usable() && !HashSums().VerifyFile(DestFile))
2d4722e2 779 {
3c8030a4 780 RenameOnError(HashSumMismatch);
2d4722e2
MV
781 Dequeue();
782 return;
783 }
784
03bfbc96 785 // queue for copy
4d0818cc
MV
786 std::string FinalFile = _config->FindDir("Dir::State::lists");
787 FinalFile += URItoFileName(RealURI);
788 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
fa3a96a1 789
2d4722e2 790 // this is for the "real" finish
ac5b205a 791 Complete = true;
cffc2ddd 792 Status = StatDone;
ac5b205a
MV
793 Dequeue();
794 if(Debug)
795 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
796 return;
ac5b205a
MV
797 }
798
799 if(Debug)
800 std::clog << "Finishing: " << Desc.URI << std::endl;
801 Complete = false;
802 Status = StatDone;
803 Dequeue();
804 return;
805}
92fcbfc1
DK
806 /*}}}*/
807bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
ac5b205a 808{
94dc9d7d 809 // calc sha1 of the just patched file
ea7682a0 810 std::string const FinalFile = GetPartialFileNameFromURI(RealURI);
03bfbc96
MV
811
812 if(!FileExists(FinalFile))
813 {
4dbfe436 814 Failed("Message: No FinalFile " + FinalFile + " available", NULL);
03bfbc96
MV
815 return false;
816 }
94dc9d7d 817
f213b6ea 818 FileFd fd(FinalFile, FileFd::ReadOnly);
f6d4ab9a
DK
819 Hashes LocalHashesCalc;
820 LocalHashesCalc.AddFD(fd);
821 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
822
3de9ff77 823 if(Debug)
f6d4ab9a
DK
824 std::clog << "QueueNextDiff: " << FinalFile << " (" << LocalHashes.find(NULL)->toStr() << ")" << std::endl;
825
826 if (unlikely(LocalHashes.usable() == false || ExpectedHashes.usable() == false))
827 {
828 Failed("Local/Expected hashes are not usable", NULL);
829 return false;
830 }
94dc9d7d 831
03bfbc96 832
8a3207f4 833 // final file reached before all patches are applied
f6d4ab9a 834 if(LocalHashes == ExpectedHashes)
8a3207f4
DK
835 {
836 Finish(true);
837 return true;
838 }
839
26d27645
MV
840 // remove all patches until the next matching patch is found
841 // this requires the Index file to be ordered
f6d4ab9a 842 for(vector<DiffInfo>::iterator I = available_patches.begin();
f7f0d6c7 843 available_patches.empty() == false &&
2ac3eeb6 844 I != available_patches.end() &&
f6d4ab9a 845 I->result_hashes != LocalHashes;
f7f0d6c7 846 ++I)
2ac3eeb6 847 {
26d27645 848 available_patches.erase(I);
59a704f0 849 }
94dc9d7d
MV
850
851 // error checking and falling back if no patch was found
f7f0d6c7
DK
852 if(available_patches.empty() == true)
853 {
f6d4ab9a 854 Failed("No patches left to reach target", NULL);
94dc9d7d
MV
855 return false;
856 }
6cb30d01 857
94dc9d7d 858 // queue the right diff
e788a834 859 Desc.URI = RealURI + ".diff/" + available_patches[0].file + ".gz";
05aab406 860 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
ea7682a0 861 DestFile = GetPartialFileNameFromURI(RealURI + ".diff/" + available_patches[0].file);
ac5b205a
MV
862
863 if(Debug)
864 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
f6d4ab9a 865
ac5b205a
MV
866 QueueURI(Desc);
867
868 return true;
869}
92fcbfc1 870 /*}}}*/
b3501edb 871void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size, HashStringList const &Hashes, /*{{{*/
ac5b205a
MV
872 pkgAcquire::MethodConfig *Cnf)
873{
874 if(Debug)
875 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
876
b3501edb 877 Item::Done(Message, Size, Hashes, Cnf);
ac5b205a 878
8d266656 879 // FIXME: verify this download too before feeding it to rred
ea7682a0 880 std::string const FinalFile = GetPartialFileNameFromURI(RealURI);
6cb30d01 881
1e3f4083 882 // success in downloading a diff, enter ApplyDiff state
caffd480 883 if(State == StateFetchDiff)
4a0a786f 884 {
f6d4ab9a
DK
885 FileFd fd(DestFile, FileFd::ReadOnly, FileFd::Gzip);
886 class Hashes LocalHashesCalc;
887 LocalHashesCalc.AddFD(fd);
888 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
889
890 if (fd.Size() != available_patches[0].patch_size ||
891 available_patches[0].patch_hashes != LocalHashes)
892 {
893 Failed("Patch has Size/Hashsum mismatch", NULL);
894 return;
895 }
4a0a786f
MV
896
897 // rred excepts the patch as $FinalFile.ed
898 Rename(DestFile,FinalFile+".ed");
899
900 if(Debug)
901 std::clog << "Sending to rred method: " << FinalFile << std::endl;
902
903 State = StateApplyDiff;
b7347826 904 Local = true;
4a0a786f
MV
905 Desc.URI = "rred:" + FinalFile;
906 QueueURI(Desc);
eeac6897 907 SetActiveSubprocess("rred");
4a0a786f
MV
908 return;
909 }
910
911
912 // success in download/apply a diff, queue next (if needed)
913 if(State == StateApplyDiff)
914 {
915 // remove the just applied patch
94dc9d7d 916 available_patches.erase(available_patches.begin());
34d6ece7 917 unlink((FinalFile + ".ed").c_str());
ac5b205a 918
4a0a786f 919 // move into place
59a704f0
MV
920 if(Debug)
921 {
4a0a786f
MV
922 std::clog << "Moving patched file in place: " << std::endl
923 << DestFile << " -> " << FinalFile << std::endl;
59a704f0 924 }
4a0a786f 925 Rename(DestFile,FinalFile);
1790e0cf 926 chmod(FinalFile.c_str(),0644);
4a0a786f
MV
927
928 // see if there is more to download
f7f0d6c7 929 if(available_patches.empty() == false) {
715c65de 930 new pkgAcqIndexDiffs(Owner, TransactionManager, Target,
e110d7bf 931 ExpectedHashes, MetaIndexParser,
f6d4ab9a 932 available_patches);
4a0a786f
MV
933 return Finish();
934 } else
03bfbc96
MV
935 // update
936 DestFile = FinalFile;
4a0a786f 937 return Finish(true);
ac5b205a 938 }
ac5b205a 939}
92fcbfc1 940 /*}}}*/
47d2bc78 941// AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
e05672e8 942pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire *Owner,
715c65de 943 pkgAcqMetaBase *TransactionManager,
c2184314 944 struct IndexTarget const * const Target,
e110d7bf 945 HashStringList const &ExpectedHashes,
c2184314
MV
946 indexRecords *MetaIndexParser,
947 DiffInfo const &patch,
948 std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
a64bf0eb 949 : pkgAcqBaseIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser),
0b58b3f8 950 patch(patch), allPatches(allPatches), State(StateFetchDiff)
47d2bc78 951{
47d2bc78
DK
952 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
953
a64bf0eb 954 RealURI = Target->URI;
47d2bc78 955 Desc.Owner = this;
c2184314
MV
956 Description = Target->Description;
957 Desc.ShortDesc = Target->ShortDesc;
47d2bc78 958
e788a834 959 Desc.URI = RealURI + ".diff/" + patch.file + ".gz";
47d2bc78 960 Desc.Description = Description + " " + patch.file + string(".pdiff");
5684f71f 961
ea7682a0 962 DestFile = GetPartialFileNameFromURI(RealURI + ".diff/" + patch.file);
47d2bc78
DK
963
964 if(Debug)
965 std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
966
967 QueueURI(Desc);
968}
969 /*}}}*/
4dbfe436 970void pkgAcqIndexMergeDiffs::Failed(string Message,pkgAcquire::MethodConfig * Cnf)/*{{{*/
47d2bc78
DK
971{
972 if(Debug)
973 std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
4dbfe436
DK
974
975 Item::Failed(Message,Cnf);
47d2bc78 976 Status = StatDone;
47d2bc78
DK
977
978 // check if we are the first to fail, otherwise we are done here
979 State = StateDoneDiff;
980 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
981 I != allPatches->end(); ++I)
982 if ((*I)->State == StateErrorDiff)
983 return;
984
985 // first failure means we should fallback
986 State = StateErrorDiff;
1e3f4083 987 std::clog << "Falling back to normal index file acquire" << std::endl;
715c65de 988 new pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser);
47d2bc78
DK
989}
990 /*}}}*/
b3501edb 991void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
47d2bc78
DK
992 pkgAcquire::MethodConfig *Cnf)
993{
994 if(Debug)
995 std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
996
b3501edb 997 Item::Done(Message,Size,Hashes,Cnf);
47d2bc78 998
8d266656 999 // FIXME: verify download before feeding it to rred
ea7682a0 1000 string const FinalFile = GetPartialFileNameFromURI(RealURI);
47d2bc78
DK
1001
1002 if (State == StateFetchDiff)
1003 {
f6d4ab9a
DK
1004 FileFd fd(DestFile, FileFd::ReadOnly, FileFd::Gzip);
1005 class Hashes LocalHashesCalc;
1006 LocalHashesCalc.AddFD(fd);
1007 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
1008
1009 if (fd.Size() != patch.patch_size || patch.patch_hashes != LocalHashes)
1010 {
1011 Failed("Patch has Size/Hashsum mismatch", NULL);
1012 return;
1013 }
1014
47d2bc78
DK
1015 // rred expects the patch as $FinalFile.ed.$patchname.gz
1016 Rename(DestFile, FinalFile + ".ed." + patch.file + ".gz");
1017
1018 // check if this is the last completed diff
1019 State = StateDoneDiff;
1020 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
1021 I != allPatches->end(); ++I)
1022 if ((*I)->State != StateDoneDiff)
1023 {
1024 if(Debug)
1025 std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
1026 return;
1027 }
1028
1029 // this is the last completed diff, so we are ready to apply now
1030 State = StateApplyDiff;
1031
1032 if(Debug)
1033 std::clog << "Sending to rred method: " << FinalFile << std::endl;
1034
1035 Local = true;
1036 Desc.URI = "rred:" + FinalFile;
1037 QueueURI(Desc);
eeac6897 1038 SetActiveSubprocess("rred");
47d2bc78
DK
1039 return;
1040 }
1041 // success in download/apply all diffs, clean up
1042 else if (State == StateApplyDiff)
1043 {
1044 // see if we really got the expected file
b3501edb 1045 if(ExpectedHashes.usable() && !ExpectedHashes.VerifyFile(DestFile))
47d2bc78
DK
1046 {
1047 RenameOnError(HashSumMismatch);
1048 return;
1049 }
1050
03bfbc96
MV
1051
1052 std::string FinalFile = _config->FindDir("Dir::State::lists");
1053 FinalFile += URItoFileName(RealURI);
1054
47d2bc78
DK
1055 // move the result into place
1056 if(Debug)
03bfbc96 1057 std::clog << "Queue patched file in place: " << std::endl
47d2bc78 1058 << DestFile << " -> " << FinalFile << std::endl;
47d2bc78 1059
03bfbc96 1060 // queue for copy by the transaction manager
fa3a96a1 1061 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
47d2bc78 1062
34d6ece7
DK
1063 // ensure the ed's are gone regardless of list-cleanup
1064 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
1065 I != allPatches->end(); ++I)
1066 {
ea7682a0 1067 std::string const PartialFile = GetPartialFileNameFromURI(RealURI);
03bfbc96
MV
1068 std::string patch = PartialFile + ".ed." + (*I)->patch.file + ".gz";
1069 std::cerr << patch << std::endl;
34d6ece7
DK
1070 unlink(patch.c_str());
1071 }
1072
47d2bc78
DK
1073 // all set and done
1074 Complete = true;
1075 if(Debug)
1076 std::clog << "allDone: " << DestFile << "\n" << std::endl;
1077 }
1078}
1079 /*}}}*/
651bddad
MV
1080// AcqBaseIndex::VerifyHashByMetaKey - verify hash for the given metakey /*{{{*/
1081bool pkgAcqBaseIndex::VerifyHashByMetaKey(HashStringList const &Hashes)
1082{
1e8ba0d4 1083 if(MetaKey != "" && Hashes.usable())
651bddad
MV
1084 {
1085 indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
1086 if(Record && Record->Hashes.usable() && Hashes != Record->Hashes)
1087 {
1088 printHashSumComparision(RealURI, Record->Hashes, Hashes);
1089 return false;
1090 }
1091 }
1092 return true;
1093}
8267fbd9 1094 /*}}}*/
0118833a
AL
1095// AcqIndex::AcqIndex - Constructor /*{{{*/
1096// ---------------------------------------------------------------------
8267fbd9
DK
1097/* The package file is added to the queue and a second class is
1098 instantiated to fetch the revision file */
b2e465d6 1099pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
b3d44315 1100 string URI,string URIDesc,string ShortDesc,
916b8910 1101 HashStringList const &ExpectedHash)
a64bf0eb 1102 : pkgAcqBaseIndex(Owner, 0, NULL, ExpectedHash, NULL)
0118833a 1103{
a64bf0eb
MV
1104 RealURI = URI;
1105
56472095 1106 AutoSelectCompression();
21638c3a
MV
1107 Init(URI, URIDesc, ShortDesc);
1108
1109 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
715c65de
MV
1110 std::clog << "New pkgIndex with TransactionManager "
1111 << TransactionManager << std::endl;
56472095 1112}
56472095 1113 /*}}}*/
21638c3a 1114// AcqIndex::AcqIndex - Constructor /*{{{*/
e05672e8 1115pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
715c65de 1116 pkgAcqMetaBase *TransactionManager,
56472095 1117 IndexTarget const *Target,
8267fbd9 1118 HashStringList const &ExpectedHash,
56472095 1119 indexRecords *MetaIndexParser)
8267fbd9 1120 : pkgAcqBaseIndex(Owner, TransactionManager, Target, ExpectedHash,
a64bf0eb 1121 MetaIndexParser)
56472095 1122{
a64bf0eb
MV
1123 RealURI = Target->URI;
1124
56472095
MV
1125 // autoselect the compression method
1126 AutoSelectCompression();
1127 Init(Target->URI, Target->Description, Target->ShortDesc);
1128
e05672e8 1129 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
715c65de
MV
1130 std::clog << "New pkgIndex with TransactionManager "
1131 << TransactionManager << std::endl;
56472095
MV
1132}
1133 /*}}}*/
21638c3a 1134// AcqIndex::AutoSelectCompression - Select compression /*{{{*/
56472095
MV
1135void pkgAcqIndex::AutoSelectCompression()
1136{
5d885723 1137 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
651bddad 1138 CompressionExtensions = "";
b3501edb 1139 if (ExpectedHashes.usable())
5d885723 1140 {
651bddad
MV
1141 for (std::vector<std::string>::const_iterator t = types.begin();
1142 t != types.end(); ++t)
1143 {
1144 std::string CompressedMetaKey = string(Target->MetaKey).append(".").append(*t);
8267fbd9 1145 if (*t == "uncompressed" ||
651bddad
MV
1146 MetaIndexParser->Exists(CompressedMetaKey) == true)
1147 CompressionExtensions.append(*t).append(" ");
1148 }
5d885723
DK
1149 }
1150 else
1151 {
1152 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
651bddad 1153 CompressionExtensions.append(*t).append(" ");
5d885723 1154 }
651bddad
MV
1155 if (CompressionExtensions.empty() == false)
1156 CompressionExtensions.erase(CompressionExtensions.end()-1);
5d885723 1157}
8267fbd9 1158 /*}}}*/
5d885723 1159// AcqIndex::Init - defered Constructor /*{{{*/
8267fbd9 1160void pkgAcqIndex::Init(string const &URI, string const &URIDesc,
3f073d44
MV
1161 string const &ShortDesc)
1162{
651bddad 1163 Stage = STAGE_DOWNLOAD;
13e8426f 1164
ea7682a0 1165 DestFile = GetPartialFileNameFromURI(URI);
8267fe24 1166
1e8ba0d4
MV
1167 CurrentCompressionExtension = CompressionExtensions.substr(0, CompressionExtensions.find(' '));
1168 if (CurrentCompressionExtension == "uncompressed")
b11f9599 1169 {
5d885723 1170 Desc.URI = URI;
e39698a4
MV
1171 if(Target)
1172 MetaKey = string(Target->MetaKey);
b11f9599 1173 }
5d885723 1174 else
b11f9599 1175 {
1e8ba0d4
MV
1176 Desc.URI = URI + '.' + CurrentCompressionExtension;
1177 DestFile = DestFile + '.' + CurrentCompressionExtension;
e39698a4 1178 if(Target)
1e8ba0d4 1179 MetaKey = string(Target->MetaKey) + '.' + CurrentCompressionExtension;
b11f9599
MV
1180 }
1181
1182 // load the filesize
e39698a4
MV
1183 if(MetaIndexParser)
1184 {
1185 indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
1186 if(Record)
1187 FileSize = Record->Size;
8267fbd9 1188
59194959 1189 InitByHashIfNeeded(MetaKey);
e39698a4 1190 }
b3d44315 1191
b2e465d6 1192 Desc.Description = URIDesc;
8267fe24 1193 Desc.Owner = this;
b2e465d6 1194 Desc.ShortDesc = ShortDesc;
5d885723 1195
8267fe24 1196 QueueURI(Desc);
0118833a
AL
1197}
1198 /*}}}*/
59194959 1199// AcqIndex::AdjustForByHash - modify URI for by-hash support /*{{{*/
59194959
MV
1200void pkgAcqIndex::InitByHashIfNeeded(const std::string MetaKey)
1201{
1202 // TODO:
1203 // - (maybe?) add support for by-hash into the sources.list as flag
1204 // - make apt-ftparchive generate the hashes (and expire?)
1205 std::string HostKnob = "APT::Acquire::" + ::URI(Desc.URI).Host + "::By-Hash";
1206 if(_config->FindB("APT::Acquire::By-Hash", false) == true ||
1207 _config->FindB(HostKnob, false) == true ||
1208 MetaIndexParser->GetSupportsAcquireByHash())
1209 {
1210 indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
1211 if(Record)
1212 {
1213 // FIXME: should we really use the best hash here? or a fixed one?
1214 const HashString *TargetHash = Record->Hashes.find("");
1215 std::string ByHash = "/by-hash/" + TargetHash->HashType() + "/" + TargetHash->HashValue();
1216 size_t trailing_slash = Desc.URI.find_last_of("/");
1217 Desc.URI = Desc.URI.replace(
1218 trailing_slash,
1219 Desc.URI.substr(trailing_slash+1).size()+1,
1220 ByHash);
1221 } else {
1222 _error->Warning(
1223 "Fetching ByHash requested but can not find record for %s",
1224 MetaKey.c_str());
1225 }
1226 }
1227}
1228 /*}}}*/
0a8a80e5 1229// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 1230// ---------------------------------------------------------------------
0a8a80e5 1231/* The only header we use is the last-modified header. */
b3501edb 1232string pkgAcqIndex::Custom600Headers() const
0118833a 1233{
3f073d44 1234 string Final = GetFinalFilename();
8267fbd9 1235
97b65b10 1236 string msg = "\nIndex-File: true";
0a8a80e5 1237 struct stat Buf;
3a1f49c4 1238 if (stat(Final.c_str(),&Buf) == 0)
97b65b10
MV
1239 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1240
1241 return msg;
0118833a
AL
1242}
1243 /*}}}*/
8267fbd9
DK
1244// pkgAcqIndex::Failed - getting the indexfile failed /*{{{*/
1245void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
debc84b2 1246{
651bddad 1247 size_t const nextExt = CompressionExtensions.find(' ');
5d885723 1248 if (nextExt != std::string::npos)
e85b4cd5 1249 {
651bddad 1250 CompressionExtensions = CompressionExtensions.substr(nextExt+1);
5d885723 1251 Init(RealURI, Desc.Description, Desc.ShortDesc);
6abe2699 1252 return;
0d7a243d
EL
1253 }
1254
17ff0930 1255 // on decompression failure, remove bad versions in partial/
651bddad
MV
1256 if (Stage == STAGE_DECOMPRESS_AND_VERIFY)
1257 {
1e8ba0d4 1258 unlink(EraseFileName.c_str());
debc84b2
MZ
1259 }
1260
debc84b2 1261 Item::Failed(Message,Cnf);
56472095
MV
1262
1263 /// cancel the entire transaction
715c65de 1264 TransactionManager->AbortTransaction();
debc84b2 1265}
92fcbfc1 1266 /*}}}*/
8267fbd9 1267// pkgAcqIndex::GetFinalFilename - Return the full final file path /*{{{*/
3f073d44 1268std::string pkgAcqIndex::GetFinalFilename() const
63b7249e
MV
1269{
1270 std::string FinalFile = _config->FindDir("Dir::State::lists");
3f073d44 1271 FinalFile += URItoFileName(RealURI);
b0f4b486 1272 if (_config->FindB("Acquire::GzipIndexes",false) == true)
1e8ba0d4 1273 FinalFile += '.' + CurrentCompressionExtension;
63b7249e
MV
1274 return FinalFile;
1275}
8267fbd9
DK
1276 /*}}}*/
1277// AcqIndex::ReverifyAfterIMS - Reverify index after an ims-hit /*{{{*/
916b8910 1278void pkgAcqIndex::ReverifyAfterIMS()
63b7249e 1279{
c36db2b5
MV
1280 // update destfile to *not* include the compression extension when doing
1281 // a reverify (as its uncompressed on disk already)
ea7682a0 1282 DestFile = GetPartialFileNameFromURI(RealURI);
c36db2b5
MV
1283
1284 // adjust DestFile if its compressed on disk
b0f4b486 1285 if (_config->FindB("Acquire::GzipIndexes",false) == true)
1e8ba0d4 1286 DestFile += '.' + CurrentCompressionExtension;
63b7249e
MV
1287
1288 // copy FinalFile into partial/ so that we check the hash again
3f073d44 1289 string FinalFile = GetFinalFilename();
651bddad 1290 Stage = STAGE_DECOMPRESS_AND_VERIFY;
63b7249e
MV
1291 Desc.URI = "copy:" + FinalFile;
1292 QueueURI(Desc);
1293}
8267fbd9
DK
1294 /*}}}*/
1295// AcqIndex::ValidateFile - Validate the content of the downloaded file /*{{{*/
899e4ded
MV
1296bool pkgAcqIndex::ValidateFile(const std::string &FileName)
1297{
1298 // FIXME: this can go away once we only ever download stuff that
1299 // has a valid hash and we never do GET based probing
1300 // FIXME2: this also leaks debian-isms into the code and should go therefore
1301
1302 /* Always validate the index file for correctness (all indexes must
1303 * have a Package field) (LP: #346386) (Closes: #627642)
1304 */
651bddad 1305 FileFd fd(FileName, FileFd::ReadOnly, FileFd::Extension);
899e4ded
MV
1306 // Only test for correctness if the content of the file is not empty
1307 // (empty is ok)
1308 if (fd.Size() > 0)
1309 {
1310 pkgTagSection sec;
1311 pkgTagFile tag(&fd);
1312
1313 // all our current indexes have a field 'Package' in each section
1314 if (_error->PendingError() == true ||
1315 tag.Step(sec) == false ||
1316 sec.Exists("Package") == false)
1317 return false;
1318 }
1319 return true;
1320}
8267fbd9 1321 /*}}}*/
8b89e57f
AL
1322// AcqIndex::Done - Finished a fetch /*{{{*/
1323// ---------------------------------------------------------------------
1324/* This goes through a number of states.. On the initial fetch the
1325 method could possibly return an alternate filename which points
1326 to the uncompressed version of the file. If this is so the file
1327 is copied into the partial directory. In all other cases the file
b6f0063c 1328 is decompressed with a compressed uri. */
651bddad
MV
1329void pkgAcqIndex::Done(string Message,
1330 unsigned long long Size,
c8aa88aa 1331 HashStringList const &Hashes,
459681d3 1332 pkgAcquire::MethodConfig *Cfg)
8b89e57f 1333{
b3501edb 1334 Item::Done(Message,Size,Hashes,Cfg);
63b7249e 1335
651bddad 1336 switch(Stage)
8b89e57f 1337 {
651bddad
MV
1338 case STAGE_DOWNLOAD:
1339 StageDownloadDone(Message, Hashes, Cfg);
1340 break;
1341 case STAGE_DECOMPRESS_AND_VERIFY:
1342 StageDecompressDone(Message, Hashes, Cfg);
1343 break;
5f6c6c6e 1344 }
651bddad 1345}
8267fbd9
DK
1346 /*}}}*/
1347// AcqIndex::StageDownloadDone - Queue for decompress and verify /*{{{*/
651bddad
MV
1348void pkgAcqIndex::StageDownloadDone(string Message,
1349 HashStringList const &Hashes,
1350 pkgAcquire::MethodConfig *Cfg)
1351{
1352 // First check if the calculcated Hash of the (compressed) downloaded
1353 // file matches the hash we have in the MetaIndexRecords for this file
1354 if(VerifyHashByMetaKey(Hashes) == false)
5f6c6c6e 1355 {
651bddad
MV
1356 RenameOnError(HashSumMismatch);
1357 Failed(Message, Cfg);
1358 return;
8b89e57f 1359 }
bfd22fc0 1360
8267fe24 1361 Complete = true;
8267fbd9 1362
8b89e57f
AL
1363 // Handle the unzipd case
1364 string FileName = LookupTag(Message,"Alt-Filename");
1365 if (FileName.empty() == false)
1366 {
651bddad 1367 Stage = STAGE_DECOMPRESS_AND_VERIFY;
a6568219 1368 Local = true;
8b89e57f 1369 DestFile += ".decomp";
8267fe24
AL
1370 Desc.URI = "copy:" + FileName;
1371 QueueURI(Desc);
eeac6897 1372 SetActiveSubprocess("copy");
8b89e57f
AL
1373 return;
1374 }
1375
1376 FileName = LookupTag(Message,"Filename");
1377 if (FileName.empty() == true)
1378 {
1379 Status = StatError;
1380 ErrorText = "Method gave a blank filename";
1381 }
5d885723 1382
651bddad
MV
1383 // Methods like e.g. "file:" will give us a (compressed) FileName that is
1384 // not the "DestFile" we set, in this case we uncompress from the local file
1385 if (FileName != DestFile)
a6568219 1386 Local = true;
1e8ba0d4
MV
1387 else
1388 EraseFileName = FileName;
daff4aa3 1389
651bddad
MV
1390 // we need to verify the file against the current Release file again
1391 // on if-modfied-since hit to avoid a stale attack against us
1392 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
ca7fd76c 1393 {
651bddad
MV
1394 // do not reverify cdrom sources as apt-cdrom may rewrite the Packages
1395 // file when its doing the indexcopy
1396 if (RealURI.substr(0,6) == "cdrom:")
1397 return;
b0f4b486 1398
651bddad 1399 // The files timestamp matches, reverify by copy into partial/
1e8ba0d4 1400 EraseFileName = "";
651bddad 1401 ReverifyAfterIMS();
8b89e57f 1402 return;
ca7fd76c 1403 }
e85b4cd5 1404
651bddad 1405 // If we have compressed indexes enabled, queue for hash verification
b0f4b486 1406 if (_config->FindB("Acquire::GzipIndexes",false))
ca7fd76c 1407 {
ea7682a0 1408 DestFile = GetPartialFileNameFromURI(RealURI + '.' + CurrentCompressionExtension);
1e8ba0d4 1409 EraseFileName = "";
651bddad 1410 Stage = STAGE_DECOMPRESS_AND_VERIFY;
ca7fd76c
MV
1411 Desc.URI = "copy:" + FileName;
1412 QueueURI(Desc);
4dbfe436 1413 SetActiveSubprocess("copy");
bb109d0b 1414 return;
1415 }
1416
e85b4cd5 1417 // get the binary name for your used compression type
651bddad 1418 string decompProg;
1e8ba0d4 1419 if(CurrentCompressionExtension == "uncompressed")
0d7a243d 1420 decompProg = "copy";
651bddad 1421 else
1e8ba0d4 1422 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(CurrentCompressionExtension),"");
651bddad
MV
1423 if(decompProg.empty() == true)
1424 {
1e8ba0d4 1425 _error->Error("Unsupported extension: %s", CurrentCompressionExtension.c_str());
debc84b2
MZ
1426 return;
1427 }
1428
651bddad
MV
1429 // queue uri for the next stage
1430 Stage = STAGE_DECOMPRESS_AND_VERIFY;
8b89e57f 1431 DestFile += ".decomp";
e85b4cd5 1432 Desc.URI = decompProg + ":" + FileName;
8267fe24 1433 QueueURI(Desc);
eeac6897 1434 SetActiveSubprocess(decompProg);
8b89e57f 1435}
8267fbd9
DK
1436 /*}}}*/
1437// pkgAcqIndex::StageDecompressDone - Final verification /*{{{*/
651bddad
MV
1438void pkgAcqIndex::StageDecompressDone(string Message,
1439 HashStringList const &Hashes,
1440 pkgAcquire::MethodConfig *Cfg)
1441{
1442 if (ExpectedHashes.usable() && ExpectedHashes != Hashes)
1443 {
1444 Desc.URI = RealURI;
1445 RenameOnError(HashSumMismatch);
1446 printHashSumComparision(RealURI, ExpectedHashes, Hashes);
1447 Failed(Message, Cfg);
1448 return;
1449 }
1450
1451 if(!ValidateFile(DestFile))
1452 {
1453 RenameOnError(InvalidFormat);
1454 Failed(Message, Cfg);
1455 return;
1456 }
8267fbd9 1457
1e8ba0d4
MV
1458 // remove the compressed version of the file
1459 unlink(EraseFileName.c_str());
8267fbd9 1460
651bddad
MV
1461 // Done, queue for rename on transaction finished
1462 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
8267fbd9 1463
651bddad
MV
1464 return;
1465}
92fcbfc1 1466 /*}}}*/
a52f938b
OS
1467// AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
1468// ---------------------------------------------------------------------
1469/* The Translation file is added to the queue */
1470pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
8267fbd9 1471 string URI,string URIDesc,string ShortDesc)
916b8910 1472 : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashStringList())
a52f938b 1473{
ab53c018 1474}
8267fbd9
DK
1475pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
1476 pkgAcqMetaBase *TransactionManager,
e05672e8 1477 IndexTarget const * const Target,
8267fbd9 1478 HashStringList const &ExpectedHashes,
e05672e8 1479 indexRecords *MetaIndexParser)
715c65de 1480 : pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser)
ab53c018 1481{
1dca8dc5
MV
1482 // load the filesize
1483 indexRecords::checkSum *Record = MetaIndexParser->Lookup(string(Target->MetaKey));
1484 if(Record)
1485 FileSize = Record->Size;
963b16dc
MV
1486}
1487 /*}}}*/
1488// AcqIndexTrans::Custom600Headers - Insert custom request headers /*{{{*/
b3501edb 1489string pkgAcqIndexTrans::Custom600Headers() const
963b16dc 1490{
3f073d44 1491 string Final = GetFinalFilename();
ca7fd76c 1492
c91d9a63
DK
1493 struct stat Buf;
1494 if (stat(Final.c_str(),&Buf) != 0)
a3f7fff8
MV
1495 return "\nFail-Ignore: true\nIndex-File: true";
1496 return "\nFail-Ignore: true\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
a52f938b 1497}
a52f938b
OS
1498 /*}}}*/
1499// AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
a52f938b
OS
1500void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1501{
651bddad 1502 size_t const nextExt = CompressionExtensions.find(' ');
5d885723
DK
1503 if (nextExt != std::string::npos)
1504 {
651bddad 1505 CompressionExtensions = CompressionExtensions.substr(nextExt+1);
5d885723
DK
1506 Init(RealURI, Desc.Description, Desc.ShortDesc);
1507 Status = StatIdle;
1508 return;
1509 }
1510
4dbfe436
DK
1511 Item::Failed(Message,Cnf);
1512
e05672e8 1513 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
8267fbd9 1514 if (Cnf->LocalOnly == true ||
a52f938b 1515 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
8267fbd9 1516 {
a52f938b
OS
1517 // Ignore this
1518 Status = StatDone;
a52f938b 1519 }
a52f938b
OS
1520}
1521 /*}}}*/
8267fbd9 1522// AcqMetaBase::Add - Add a item to the current Transaction /*{{{*/
715c65de 1523void pkgAcqMetaBase::Add(Item *I)
e6e89390 1524{
715c65de 1525 Transaction.push_back(I);
e6e89390 1526}
61aea84d 1527 /*}}}*/
8267fbd9 1528// AcqMetaBase::AbortTransaction - Abort the current Transaction /*{{{*/
715c65de
MV
1529void pkgAcqMetaBase::AbortTransaction()
1530{
1531 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1532 std::clog << "AbortTransaction: " << TransactionManager << std::endl;
1533
631a7dc7 1534 // ensure the toplevel is in error state too
715c65de
MV
1535 for (std::vector<Item*>::iterator I = Transaction.begin();
1536 I != Transaction.end(); ++I)
1537 {
1538 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1539 std::clog << " Cancel: " << (*I)->DestFile << std::endl;
1540 // the transaction will abort, so stop anything that is idle
1541 if ((*I)->Status == pkgAcquire::Item::StatIdle)
1542 (*I)->Status = pkgAcquire::Item::StatDone;
0b844e23
MV
1543
1544 // kill files in partial
ea7682a0 1545 std::string const PartialFile = GetPartialFileName(flNotDir((*I)->DestFile));
0b844e23
MV
1546 if(FileExists(PartialFile))
1547 Rename(PartialFile, PartialFile + ".FAILED");
715c65de
MV
1548 }
1549}
1550 /*}}}*/
8267fbd9 1551// AcqMetaBase::TransactionHasError - Check for errors in Transaction /*{{{*/
715c65de
MV
1552bool pkgAcqMetaBase::TransactionHasError()
1553{
1554 for (pkgAcquire::ItemIterator I = Transaction.begin();
1555 I != Transaction.end(); ++I)
1556 if((*I)->Status != pkgAcquire::Item::StatDone &&
1557 (*I)->Status != pkgAcquire::Item::StatIdle)
1558 return true;
1559
1560 return false;
1561}
61aea84d
MV
1562 /*}}}*/
1563// AcqMetaBase::CommitTransaction - Commit a transaction /*{{{*/
715c65de
MV
1564void pkgAcqMetaBase::CommitTransaction()
1565{
1566 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1567 std::clog << "CommitTransaction: " << this << std::endl;
1568
1569 // move new files into place *and* remove files that are not
1570 // part of the transaction but are still on disk
1571 for (std::vector<Item*>::iterator I = Transaction.begin();
1572 I != Transaction.end(); ++I)
1573 {
1574 if((*I)->PartialFile != "")
1575 {
5684f71f
DK
1576 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1577 std::clog << "mv " << (*I)->PartialFile << " -> "<< (*I)->DestFile << " "
1578 << (*I)->DescURI() << std::endl;
1579
1580 Rename((*I)->PartialFile, (*I)->DestFile);
ea7682a0 1581 ChangeOwnerAndPermissionOfFile("CommitTransaction", (*I)->DestFile.c_str(), "root", "root", 0644);
5684f71f 1582
715c65de
MV
1583 } else {
1584 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
5684f71f 1585 std::clog << "rm "
03bfbc96 1586 << (*I)->DestFile
5684f71f 1587 << " "
03bfbc96
MV
1588 << (*I)->DescURI()
1589 << std::endl;
715c65de
MV
1590 unlink((*I)->DestFile.c_str());
1591 }
1592 // mark that this transaction is finished
1593 (*I)->TransactionManager = 0;
1594 }
1595}
61aea84d 1596 /*}}}*/
61a360be 1597// AcqMetaBase::TransactionStageCopy - Stage a file for copying /*{{{*/
fa3a96a1
MV
1598void pkgAcqMetaBase::TransactionStageCopy(Item *I,
1599 const std::string &From,
1600 const std::string &To)
1601{
1602 I->PartialFile = From;
1603 I->DestFile = To;
1604}
61aea84d 1605 /*}}}*/
61a360be 1606// AcqMetaBase::TransactionStageRemoval - Sage a file for removal /*{{{*/
fa3a96a1
MV
1607void pkgAcqMetaBase::TransactionStageRemoval(Item *I,
1608 const std::string &FinalFile)
1609{
1610 I->PartialFile = "";
1611 I->DestFile = FinalFile;
1612}
61aea84d 1613 /*}}}*/
61aea84d 1614// AcqMetaBase::GenerateAuthWarning - Check gpg authentication error /*{{{*/
2d0a7bb4
MV
1615bool pkgAcqMetaBase::CheckStopAuthentication(const std::string &RealURI,
1616 const std::string &Message)
e6e89390 1617{
2d0a7bb4
MV
1618 // FIXME: this entire function can do now that we disallow going to
1619 // a unauthenticated state and can cleanly rollback
1620
e6e89390 1621 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
8267fbd9 1622
e6e89390
MV
1623 if(FileExists(Final))
1624 {
1625 Status = StatTransientNetworkError;
1626 _error->Warning(_("An error occurred during the signature "
1627 "verification. The repository is not updated "
1628 "and the previous index files will be used. "
1629 "GPG error: %s: %s\n"),
1630 Desc.Description.c_str(),
1631 LookupTag(Message,"Message").c_str());
1632 RunScripts("APT::Update::Auth-Failure");
1633 return true;
1634 } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
1635 /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
1636 _error->Error(_("GPG error: %s: %s"),
1637 Desc.Description.c_str(),
1638 LookupTag(Message,"Message").c_str());
1639 Status = StatError;
1640 return true;
1641 } else {
1642 _error->Warning(_("GPG error: %s: %s"),
1643 Desc.Description.c_str(),
1644 LookupTag(Message,"Message").c_str());
1645 }
8267fbd9 1646 // gpgv method failed
e6e89390
MV
1647 ReportMirrorFailure("GPGFailure");
1648 return false;
1649}
1650 /*}}}*/
8267fbd9 1651// AcqMetaSig::AcqMetaSig - Constructor /*{{{*/
61aea84d 1652pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,
715c65de 1653 pkgAcqMetaBase *TransactionManager,
b3d44315 1654 string URI,string URIDesc,string ShortDesc,
2737f28a 1655 string MetaIndexFile,
b3d44315
MV
1656 const vector<IndexTarget*>* IndexTargets,
1657 indexRecords* MetaIndexParser) :
8267fbd9 1658 pkgAcqMetaBase(Owner, IndexTargets, MetaIndexParser,
c045cc02
MV
1659 HashStringList(), TransactionManager),
1660 RealURI(URI), MetaIndexFile(MetaIndexFile), URIDesc(URIDesc),
fa3a96a1 1661 ShortDesc(ShortDesc)
0118833a 1662{
0a8a80e5 1663 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1ce24318 1664 DestFile += URItoFileName(RealURI);
b3d44315 1665
8267fbd9
DK
1666 // remove any partial downloaded sig-file in partial/.
1667 // it may confuse proxies and is too small to warrant a
47eb38f4 1668 // partial download anyway
f6237efd
MV
1669 unlink(DestFile.c_str());
1670
715c65de 1671 // set the TransactionManager
e05672e8 1672 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
715c65de
MV
1673 std::clog << "New pkgAcqMetaSig with TransactionManager "
1674 << TransactionManager << std::endl;
1f4dd8fd 1675
8267fe24 1676 // Create the item
b2e465d6 1677 Desc.Description = URIDesc;
8267fe24 1678 Desc.Owner = this;
b3d44315
MV
1679 Desc.ShortDesc = ShortDesc;
1680 Desc.URI = URI;
2737f28a 1681
8267fe24 1682 QueueURI(Desc);
ffcccd62
DK
1683}
1684 /*}}}*/
1685pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
1686{
0118833a
AL
1687}
1688 /*}}}*/
b3d44315 1689// pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 1690// ---------------------------------------------------------------------
b3501edb 1691string pkgAcqMetaSig::Custom600Headers() const
0118833a 1692{
1f4dd8fd
MV
1693 string FinalFile = _config->FindDir("Dir::State::lists");
1694 FinalFile += URItoFileName(RealURI);
1695
0a8a80e5 1696 struct stat Buf;
1f4dd8fd 1697 if (stat(FinalFile.c_str(),&Buf) != 0)
a72ace20 1698 return "\nIndex-File: true";
a789b983 1699
a72ace20 1700 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
0118833a 1701}
61aea84d 1702 /*}}}*/
8267fbd9 1703// pkgAcqMetaSig::Done - The signature was downloaded/verified /*{{{*/
61aea84d
MV
1704// ---------------------------------------------------------------------
1705/* The only header we use is the last-modified header. */
1706void pkgAcqMetaSig::Done(string Message,unsigned long long Size,
1707 HashStringList const &Hashes,
b3d44315 1708 pkgAcquire::MethodConfig *Cfg)
c88edf1d 1709{
b3501edb 1710 Item::Done(Message, Size, Hashes, Cfg);
c88edf1d 1711
1ce24318 1712 if(AuthPass == false)
c88edf1d 1713 {
f3097647 1714 if(CheckDownloadDone(Message, RealURI) == true)
1ce24318 1715 {
f3097647
MV
1716 // destfile will be modified to point to MetaIndexFile for the
1717 // gpgv method, so we need to save it here
1718 MetaIndexFileSignature = DestFile;
1719 QueueForSignatureVerify(MetaIndexFile, MetaIndexFileSignature);
1ce24318 1720 }
2737f28a
MV
1721 return;
1722 }
8267fbd9 1723 else
1f4dd8fd 1724 {
ba8a8421 1725 if(CheckAuthDone(Message, RealURI) == true)
f3097647
MV
1726 {
1727 std::string FinalFile = _config->FindDir("Dir::State::lists");
1728 FinalFile += URItoFileName(RealURI);
f3097647
MV
1729 TransactionManager->TransactionStageCopy(this, MetaIndexFileSignature, FinalFile);
1730 }
1ce24318 1731 }
c88edf1d
AL
1732}
1733 /*}}}*/
92fcbfc1 1734void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
681d76d0 1735{
47eb38f4 1736 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
e8b1db38 1737
673c9469 1738 // check if we need to fail at this point
2d0a7bb4 1739 if (AuthPass == true && CheckStopAuthentication(RealURI, Message))
e8b1db38 1740 return;
e8b1db38 1741
631a7dc7
MV
1742 // FIXME: meh, this is not really elegant
1743 string InReleaseURI = RealURI.replace(RealURI.rfind("Release.gpg"), 12,
1744 "InRelease");
1745 string FinalInRelease = _config->FindDir("Dir::State::lists") + URItoFileName(InReleaseURI);
1746
c99fe2e1 1747 if (RealFileExists(Final) || RealFileExists(FinalInRelease))
631a7dc7 1748 {
c99fe2e1
MV
1749 std::string downgrade_msg;
1750 strprintf(downgrade_msg, _("The repository '%s' is no longer signed."),
1751 URIDesc.c_str());
1752 if(_config->FindB("Acquire::AllowDowngradeToInsecureRepositories"))
1753 {
1754 // meh, the users wants to take risks (we still mark the packages
1755 // from this repository as unauthenticated)
1756 _error->Warning("%s", downgrade_msg.c_str());
1757 _error->Warning(_("This is normally not allowed, but the option "
1758 "Acquire::AllowDowngradeToInsecureRepositories was "
1759 "given to override it."));
1760
1761 } else {
1762 _error->Error("%s", downgrade_msg.c_str());
1763 Rename(MetaIndexFile, MetaIndexFile+".FAILED");
4dbfe436 1764 Item::Failed("Message: " + downgrade_msg, Cnf);
c99fe2e1
MV
1765 TransactionManager->AbortTransaction();
1766 return;
1767 }
631a7dc7 1768 }
7e5f33eb 1769
1f4dd8fd
MV
1770 // this ensures that any file in the lists/ dir is removed by the
1771 // transaction
ea7682a0 1772 DestFile = GetPartialFileNameFromURI(RealURI);
fa3a96a1 1773 TransactionManager->TransactionStageRemoval(this, DestFile);
24057ad6 1774
631a7dc7 1775 // only allow going further if the users explicitely wants it
c99fe2e1 1776 if(_config->FindB("Acquire::AllowInsecureRepositories") == true)
631a7dc7
MV
1777 {
1778 // we parse the indexes here because at this point the user wanted
1779 // a repository that may potentially harm him
1780 MetaIndexParser->Load(MetaIndexFile);
c045cc02 1781 QueueIndexes(true);
bca84917
MV
1782 }
1783 else
1784 {
c99fe2e1 1785 _error->Warning("Use --allow-insecure-repositories to force the update");
631a7dc7
MV
1786 }
1787
4dbfe436
DK
1788 Item::Failed(Message,Cnf);
1789
e05672e8 1790 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
4dbfe436 1791 if (Cnf->LocalOnly == true ||
e05672e8 1792 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
4dbfe436 1793 {
e05672e8
MV
1794 // Ignore this
1795 Status = StatDone;
e05672e8 1796 }
681d76d0 1797}
92fcbfc1
DK
1798 /*}}}*/
1799pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
715c65de 1800 pkgAcqMetaBase *TransactionManager,
b3d44315 1801 string URI,string URIDesc,string ShortDesc,
2737f28a 1802 string MetaIndexSigURI,string MetaIndexSigURIDesc, string MetaIndexSigShortDesc,
fa3b260f 1803 const vector<IndexTarget*>* IndexTargets,
b3d44315 1804 indexRecords* MetaIndexParser) :
c045cc02
MV
1805 pkgAcqMetaBase(Owner, IndexTargets, MetaIndexParser, HashStringList(),
1806 TransactionManager),
1807 RealURI(URI), URIDesc(URIDesc), ShortDesc(ShortDesc),
2737f28a
MV
1808 MetaIndexSigURI(MetaIndexSigURI), MetaIndexSigURIDesc(MetaIndexSigURIDesc),
1809 MetaIndexSigShortDesc(MetaIndexSigShortDesc)
b3d44315 1810{
715c65de
MV
1811 if(TransactionManager == NULL)
1812 {
1813 this->TransactionManager = this;
1814 this->TransactionManager->Add(this);
1815 }
e05672e8
MV
1816
1817 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
715c65de
MV
1818 std::clog << "New pkgAcqMetaIndex with TransactionManager "
1819 << this->TransactionManager << std::endl;
1820
b3d44315 1821
e05672e8
MV
1822 Init(URIDesc, ShortDesc);
1823}
1824 /*}}}*/
8267fbd9 1825// pkgAcqMetaIndex::Init - Delayed constructor /*{{{*/
e05672e8
MV
1826void pkgAcqMetaIndex::Init(std::string URIDesc, std::string ShortDesc)
1827{
ea7682a0 1828 DestFile = GetPartialFileNameFromURI(RealURI);
56472095 1829
b3d44315
MV
1830 // Create the item
1831 Desc.Description = URIDesc;
1832 Desc.Owner = this;
1833 Desc.ShortDesc = ShortDesc;
e05672e8 1834 Desc.URI = RealURI;
b3d44315 1835
d0cfa8ad
MV
1836 // we expect more item
1837 ExpectedAdditionalItems = IndexTargets->size();
b3d44315
MV
1838 QueueURI(Desc);
1839}
8267fbd9 1840 /*}}}*/
b3d44315
MV
1841// pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
1842// ---------------------------------------------------------------------
b3501edb 1843string pkgAcqMetaIndex::Custom600Headers() const
b3d44315
MV
1844{
1845 string Final = _config->FindDir("Dir::State::lists");
1846 Final += URItoFileName(RealURI);
1847
1848 struct stat Buf;
1849 if (stat(Final.c_str(),&Buf) != 0)
1850 return "\nIndex-File: true";
1851
1852 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1853}
92fcbfc1 1854 /*}}}*/
f3097647
MV
1855void pkgAcqMetaIndex::Done(string Message,unsigned long long Size, /*{{{*/
1856 HashStringList const &Hashes,
b3d44315
MV
1857 pkgAcquire::MethodConfig *Cfg)
1858{
b3501edb 1859 Item::Done(Message,Size,Hashes,Cfg);
b3d44315 1860
f3097647 1861 if(CheckDownloadDone(Message, RealURI))
b3d44315 1862 {
f3097647
MV
1863 // we have a Release file, now download the Signature, all further
1864 // verify/queue for additional downloads will be done in the
1865 // pkgAcqMetaSig::Done() code
1866 std::string MetaIndexFile = DestFile;
1867 new pkgAcqMetaSig(Owner, TransactionManager,
1868 MetaIndexSigURI, MetaIndexSigURIDesc,
1869 MetaIndexSigShortDesc, MetaIndexFile, IndexTargets,
1870 MetaIndexParser);
fce72602 1871
f3097647
MV
1872 string FinalFile = _config->FindDir("Dir::State::lists");
1873 FinalFile += URItoFileName(RealURI);
1874 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
b3d44315 1875 }
f3097647
MV
1876}
1877 /*}}}*/
ba8a8421 1878bool pkgAcqMetaBase::CheckAuthDone(string Message, const string &RealURI) /*{{{*/
f3097647
MV
1879{
1880 // At this point, the gpgv method has succeeded, so there is a
1881 // valid signature from a key in the trusted keyring. We
1882 // perform additional verification of its contents, and use them
1883 // to verify the indexes we are about to download
b3d44315 1884
f3097647
MV
1885 if (!MetaIndexParser->Load(DestFile))
1886 {
1887 Status = StatAuthError;
1888 ErrorText = MetaIndexParser->ErrorText;
1889 return false;
b3d44315 1890 }
56bc3358 1891
f3097647 1892 if (!VerifyVendor(Message, RealURI))
56bc3358 1893 {
f3097647 1894 return false;
56bc3358 1895 }
f3097647
MV
1896
1897 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1898 std::cerr << "Signature verification succeeded: "
1899 << DestFile << std::endl;
1900
1901 // Download further indexes with verification
1902 //
1903 // it would be really nice if we could simply do
1904 // if (IMSHit == false) QueueIndexes(true)
1905 // and skip the download if the Release file has not changed
1906 // - but right now the list cleaner will needs to be tricked
1907 // to not delete all our packages/source indexes in this case
1908 QueueIndexes(true);
1909
1910 return true;
1911}
1912 /*}}}*/
5684f71f 1913// pkgAcqMetaBase::QueueForSignatureVerify /*{{{*/
f3097647
MV
1914void pkgAcqMetaBase::QueueForSignatureVerify(const std::string &MetaIndexFile,
1915 const std::string &MetaIndexFileSignature)
1916{
1917 AuthPass = true;
1918 Desc.URI = "gpgv:" + MetaIndexFileSignature;
1919 DestFile = MetaIndexFile;
1920 QueueURI(Desc);
1921 SetActiveSubprocess("gpgv");
b3d44315 1922}
92fcbfc1 1923 /*}}}*/
5684f71f 1924// pkgAcqMetaBase::CheckDownloadDone /*{{{*/
f3097647
MV
1925bool pkgAcqMetaBase::CheckDownloadDone(const std::string &Message,
1926 const std::string &RealURI)
b3d44315
MV
1927{
1928 // We have just finished downloading a Release file (it is not
1929 // verified yet)
1930
1931 string FileName = LookupTag(Message,"Filename");
1932 if (FileName.empty() == true)
1933 {
1934 Status = StatError;
1935 ErrorText = "Method gave a blank filename";
f3097647 1936 return false;
b3d44315
MV
1937 }
1938
1939 if (FileName != DestFile)
1940 {
1941 Local = true;
1942 Desc.URI = "copy:" + FileName;
1943 QueueURI(Desc);
f3097647 1944 return false;
b3d44315
MV
1945 }
1946
fce72602 1947 // make sure to verify against the right file on I-M-S hit
f381d68d 1948 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
fce72602
MV
1949 if(IMSHit)
1950 {
1951 string FinalFile = _config->FindDir("Dir::State::lists");
1952 FinalFile += URItoFileName(RealURI);
1953 DestFile = FinalFile;
1954 }
2737f28a 1955
f3097647 1956 // set Item to complete as the remaining work is all local (verify etc)
b3d44315 1957 Complete = true;
b3d44315 1958
f3097647 1959 return true;
b3d44315 1960}
92fcbfc1 1961 /*}}}*/
c045cc02 1962void pkgAcqMetaBase::QueueIndexes(bool verify) /*{{{*/
b3d44315 1963{
8e3900d0
DK
1964 bool transInRelease = false;
1965 {
1966 std::vector<std::string> const keys = MetaIndexParser->MetaKeys();
1967 for (std::vector<std::string>::const_iterator k = keys.begin(); k != keys.end(); ++k)
1968 // FIXME: Feels wrong to check for hardcoded string here, but what should we do else…
1969 if (k->find("Translation-") != std::string::npos)
1970 {
1971 transInRelease = true;
1972 break;
1973 }
1974 }
1975
d0cfa8ad
MV
1976 // at this point the real Items are loaded in the fetcher
1977 ExpectedAdditionalItems = 0;
fa3b260f 1978 for (vector <IndexTarget*>::const_iterator Target = IndexTargets->begin();
b3d44315 1979 Target != IndexTargets->end();
f7f0d6c7 1980 ++Target)
b3d44315 1981 {
b3501edb
DK
1982 HashStringList ExpectedIndexHashes;
1983 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
a5b9f489 1984 bool compressedAvailable = false;
1207cf3f 1985 if (Record == NULL)
b3d44315 1986 {
a5b9f489
DK
1987 if ((*Target)->IsOptional() == true)
1988 {
1989 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
1990 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
e788a834 1991 if (MetaIndexParser->Exists((*Target)->MetaKey + "." + *t) == true)
a5b9f489
DK
1992 {
1993 compressedAvailable = true;
1994 break;
1995 }
1996 }
1997 else if (verify == true)
ab53c018 1998 {
1207cf3f
DK
1999 Status = StatAuthError;
2000 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str());
2001 return;
ab53c018 2002 }
1207cf3f
DK
2003 }
2004 else
2005 {
b3501edb 2006 ExpectedIndexHashes = Record->Hashes;
1207cf3f 2007 if (_config->FindB("Debug::pkgAcquire::Auth", false))
ab53c018 2008 {
b3501edb
DK
2009 std::cerr << "Queueing: " << (*Target)->URI << std::endl
2010 << "Expected Hash:" << std::endl;
2011 for (HashStringList::const_iterator hs = ExpectedIndexHashes.begin(); hs != ExpectedIndexHashes.end(); ++hs)
2012 std::cerr << "\t- " << hs->toStr() << std::endl;
1207cf3f
DK
2013 std::cerr << "For: " << Record->MetaKeyFilename << std::endl;
2014 }
b3501edb 2015 if (verify == true && ExpectedIndexHashes.empty() == true && (*Target)->IsOptional() == false)
1207cf3f
DK
2016 {
2017 Status = StatAuthError;
2018 strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str());
2019 return;
ab53c018
DK
2020 }
2021 }
2022
2023 if ((*Target)->IsOptional() == true)
2024 {
f456b60b 2025 if (transInRelease == false || Record != NULL || compressedAvailable == true)
8e3900d0 2026 {
f55602cb 2027 if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true &&
e788a834 2028 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true)
715c65de 2029 new pkgAcqDiffIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
f55602cb 2030 else
715c65de 2031 new pkgAcqIndexTrans(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
8e3900d0 2032 }
ab53c018 2033 continue;
b3d44315 2034 }
e1430400
DK
2035
2036 /* Queue Packages file (either diff or full packages files, depending
2037 on the users option) - we also check if the PDiff Index file is listed
2038 in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this
2039 instead, but passing the required info to it is to much hassle */
2040 if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
e788a834 2041 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true))
715c65de 2042 new pkgAcqDiffIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
e1430400 2043 else
715c65de 2044 new pkgAcqIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
b3d44315
MV
2045 }
2046}
92fcbfc1 2047 /*}}}*/
f3097647 2048bool pkgAcqMetaBase::VerifyVendor(string Message, const string &RealURI)/*{{{*/
b3d44315 2049{
ce424cd4
MV
2050 string::size_type pos;
2051
2052 // check for missing sigs (that where not fatal because otherwise we had
2053 // bombed earlier)
2054 string missingkeys;
400ad7a4 2055 string msg = _("There is no public key available for the "
ce424cd4
MV
2056 "following key IDs:\n");
2057 pos = Message.find("NO_PUBKEY ");
2058 if (pos != std::string::npos)
2059 {
2060 string::size_type start = pos+strlen("NO_PUBKEY ");
2061 string Fingerprint = Message.substr(start, Message.find("\n")-start);
2062 missingkeys += (Fingerprint);
2063 }
2064 if(!missingkeys.empty())
e788a834 2065 _error->Warning("%s", (msg + missingkeys).c_str());
b3d44315
MV
2066
2067 string Transformed = MetaIndexParser->GetExpectedDist();
2068
2069 if (Transformed == "../project/experimental")
2070 {
2071 Transformed = "experimental";
2072 }
2073
ce424cd4 2074 pos = Transformed.rfind('/');
b3d44315
MV
2075 if (pos != string::npos)
2076 {
2077 Transformed = Transformed.substr(0, pos);
2078 }
2079
2080 if (Transformed == ".")
2081 {
2082 Transformed = "";
2083 }
2084
0323317c
DK
2085 if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
2086 MetaIndexParser->GetValidUntil() > 0) {
2087 time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil();
2088 if (invalid_since > 0)
2089 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
2090 // the time since then the file is invalid - formated in the same way as in
2091 // the download progress display (e.g. 7d 3h 42min 1s)
457bea86
MV
2092 return _error->Error(
2093 _("Release file for %s is expired (invalid since %s). "
2094 "Updates for this repository will not be applied."),
2095 RealURI.c_str(), TimeToStr(invalid_since).c_str());
1ddb8596
DK
2096 }
2097
b3d44315
MV
2098 if (_config->FindB("Debug::pkgAcquire::Auth", false))
2099 {
2100 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
2101 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
2102 std::cerr << "Transformed Dist: " << Transformed << std::endl;
2103 }
2104
2105 if (MetaIndexParser->CheckDist(Transformed) == false)
2106 {
2107 // This might become fatal one day
2108// Status = StatAuthError;
2109// ErrorText = "Conflicting distribution; expected "
2110// + MetaIndexParser->GetExpectedDist() + " but got "
2111// + MetaIndexParser->GetDist();
2112// return false;
2113 if (!Transformed.empty())
2114 {
1ddb8596 2115 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
b3d44315
MV
2116 Desc.Description.c_str(),
2117 Transformed.c_str(),
2118 MetaIndexParser->GetDist().c_str());
2119 }
2120 }
2121
2122 return true;
2123}
92fcbfc1 2124 /*}}}*/
8267fbd9 2125// pkgAcqMetaIndex::Failed - no Release file present /*{{{*/
4dbfe436
DK
2126void pkgAcqMetaIndex::Failed(string Message,
2127 pkgAcquire::MethodConfig * Cnf)
b3d44315 2128{
4dbfe436
DK
2129 pkgAcquire::Item::Failed(Message, Cnf);
2130 Status = StatDone;
2131
673c9469 2132 string FinalFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
09475beb 2133
673c9469
MV
2134 _error->Warning(_("The repository '%s' does not have a Release file. "
2135 "This is deprecated, please contact the owner of the "
2136 "repository."), URIDesc.c_str());
c5fced38 2137
673c9469 2138 // No Release file was present so fall
b3d44315 2139 // back to queueing Packages files without verification
631a7dc7 2140 // only allow going further if the users explicitely wants it
c99fe2e1 2141 if(_config->FindB("Acquire::AllowInsecureRepositories") == true)
631a7dc7 2142 {
673c9469 2143 // Done, queue for rename on transaction finished
1d970e6c 2144 if (FileExists(DestFile))
1d970e6c 2145 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
1d970e6c 2146
673c9469 2147 // queue without any kind of hashsum support
631a7dc7 2148 QueueIndexes(false);
bca84917
MV
2149 } else {
2150 // warn if the repository is unsinged
c99fe2e1 2151 _error->Warning("Use --allow-insecure-repositories to force the update");
1d970e6c
MV
2152 TransactionManager->AbortTransaction();
2153 Status = StatError;
2154 return;
8267fbd9 2155 }
b3d44315 2156}
681d76d0 2157 /*}}}*/
8267fbd9 2158void pkgAcqMetaIndex::Finished() /*{{{*/
56472095
MV
2159{
2160 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
2161 std::clog << "Finished: " << DestFile <<std::endl;
715c65de
MV
2162 if(TransactionManager != NULL &&
2163 TransactionManager->TransactionHasError() == false)
2164 TransactionManager->CommitTransaction();
56472095 2165}
8267fbd9 2166 /*}}}*/
fe0f7911
DK
2167pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/
2168 string const &URI, string const &URIDesc, string const &ShortDesc,
2169 string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
2170 string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
fa3b260f 2171 const vector<IndexTarget*>* IndexTargets,
fe0f7911 2172 indexRecords* MetaIndexParser) :
715c65de 2173 pkgAcqMetaIndex(Owner, NULL, URI, URIDesc, ShortDesc, MetaSigURI, MetaSigURIDesc,MetaSigShortDesc, IndexTargets, MetaIndexParser),
2737f28a
MV
2174 MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
2175 MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
fe0f7911 2176{
d0cfa8ad
MV
2177 // index targets + (worst case:) Release/Release.gpg
2178 ExpectedAdditionalItems = IndexTargets->size() + 2;
2179
fe0f7911
DK
2180}
2181 /*}}}*/
ffcccd62
DK
2182pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
2183{
ffcccd62
DK
2184}
2185 /*}}}*/
8d6c5839
MV
2186// pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
2187// ---------------------------------------------------------------------
b3501edb 2188string pkgAcqMetaClearSig::Custom600Headers() const
8d6c5839
MV
2189{
2190 string Final = _config->FindDir("Dir::State::lists");
2191 Final += URItoFileName(RealURI);
2192
2193 struct stat Buf;
2194 if (stat(Final.c_str(),&Buf) != 0)
0aec7d5c 2195 {
0aec7d5c
DK
2196 if (stat(Final.c_str(),&Buf) != 0)
2197 return "\nIndex-File: true\nFail-Ignore: true\n";
2198 }
8d6c5839
MV
2199
2200 return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
2201}
2202 /*}}}*/
a9bb651a
MV
2203// pkgAcqMetaClearSig::Done - We got a file /*{{{*/
2204// ---------------------------------------------------------------------
0be13f1c
MV
2205void pkgAcqMetaClearSig::Done(std::string Message,unsigned long long /*Size*/,
2206 HashStringList const &/*Hashes*/,
a9bb651a 2207 pkgAcquire::MethodConfig *Cnf)
fe0f7911 2208{
e84d3803
MV
2209 // if we expect a ClearTextSignature (InRelase), ensure that
2210 // this is what we get and if not fail to queue a
2211 // Release/Release.gpg, see #346386
a9bb651a 2212 if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
e84d3803 2213 {
e84d3803 2214 pkgAcquire::Item::Failed(Message, Cnf);
631a7dc7
MV
2215 RenameOnError(NotClearsigned);
2216 TransactionManager->AbortTransaction();
e84d3803
MV
2217 return;
2218 }
f3097647
MV
2219
2220 if(AuthPass == false)
2221 {
2222 if(CheckDownloadDone(Message, RealURI) == true)
2223 QueueForSignatureVerify(DestFile, DestFile);
2224 return;
2225 }
2226 else
2227 {
ba8a8421 2228 if(CheckAuthDone(Message, RealURI) == true)
f3097647
MV
2229 {
2230 string FinalFile = _config->FindDir("Dir::State::lists");
2231 FinalFile += URItoFileName(RealURI);
2232
2233 // queue for copy in place
2234 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2235 }
2236 }
a9bb651a
MV
2237}
2238 /*}}}*/
2239void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
2240{
4dbfe436
DK
2241 Item::Failed(Message, Cnf);
2242
a9bb651a
MV
2243 // we failed, we will not get additional items from this method
2244 ExpectedAdditionalItems = 0;
e84d3803 2245
fe0f7911
DK
2246 if (AuthPass == false)
2247 {
7712d13b
MV
2248 // Queue the 'old' InRelease file for removal if we try Release.gpg
2249 // as otherwise the file will stay around and gives a false-auth
2250 // impression (CVE-2012-0214)
de498a52
DK
2251 string FinalFile = _config->FindDir("Dir::State::lists");
2252 FinalFile.append(URItoFileName(RealURI));
fa3a96a1 2253 TransactionManager->TransactionStageRemoval(this, FinalFile);
4dbfe436 2254 Status = StatDone;
de498a52 2255
715c65de 2256 new pkgAcqMetaIndex(Owner, TransactionManager,
fe0f7911 2257 MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
2737f28a 2258 MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
fe0f7911 2259 IndexTargets, MetaIndexParser);
fe0f7911
DK
2260 }
2261 else
673c9469 2262 {
2d0a7bb4 2263 if(CheckStopAuthentication(RealURI, Message))
673c9469
MV
2264 return;
2265
2266 _error->Warning(_("The data from '%s' is not signed. Packages "
2267 "from that repository can not be authenticated."),
2268 URIDesc.c_str());
2269
2270 // No Release file was present, or verification failed, so fall
2271 // back to queueing Packages files without verification
2272 // only allow going further if the users explicitely wants it
2273 if(_config->FindB("Acquire::AllowInsecureRepositories") == true)
2274 {
4dbfe436
DK
2275 Status = StatDone;
2276
673c9469
MV
2277 /* Always move the meta index, even if gpgv failed. This ensures
2278 * that PackageFile objects are correctly filled in */
4dbfe436 2279 if (FileExists(DestFile))
673c9469
MV
2280 {
2281 string FinalFile = _config->FindDir("Dir::State::lists");
2282 FinalFile += URItoFileName(RealURI);
2283 /* InRelease files become Release files, otherwise
2284 * they would be considered as trusted later on */
2285 RealURI = RealURI.replace(RealURI.rfind("InRelease"), 9,
2286 "Release");
2287 FinalFile = FinalFile.replace(FinalFile.rfind("InRelease"), 9,
2288 "Release");
4dbfe436 2289
673c9469
MV
2290 // Done, queue for rename on transaction finished
2291 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2292 }
2293 QueueIndexes(false);
2294 } else {
4dbfe436 2295 // warn if the repository is unsigned
673c9469
MV
2296 _error->Warning("Use --allow-insecure-repositories to force the update");
2297 TransactionManager->AbortTransaction();
2298 Status = StatError;
4dbfe436 2299 }
673c9469 2300 }
fe0f7911
DK
2301}
2302 /*}}}*/
03e39e59
AL
2303// AcqArchive::AcqArchive - Constructor /*{{{*/
2304// ---------------------------------------------------------------------
17caf1b1
AL
2305/* This just sets up the initial fetch environment and queues the first
2306 possibilitiy */
03e39e59 2307pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
30e1eab5
AL
2308 pkgRecords *Recs,pkgCache::VerIterator const &Version,
2309 string &StoreFilename) :
fa3b260f 2310 Item(Owner, HashStringList()), Version(Version), Sources(Sources), Recs(Recs),
b3d44315
MV
2311 StoreFilename(StoreFilename), Vf(Version.FileList()),
2312 Trusted(false)
03e39e59 2313{
7d8afa39 2314 Retries = _config->FindI("Acquire::Retries",0);
813c8eea
AL
2315
2316 if (Version.Arch() == 0)
bdae53f1 2317 {
d1f1f6a8 2318 _error->Error(_("I wasn't able to locate a file for the %s package. "
7a3c2ab0
AL
2319 "This might mean you need to manually fix this package. "
2320 "(due to missing arch)"),
40f8a8ba 2321 Version.ParentPkg().FullName().c_str());
bdae53f1
AL
2322 return;
2323 }
813c8eea 2324
b2e465d6
AL
2325 /* We need to find a filename to determine the extension. We make the
2326 assumption here that all the available sources for this version share
2327 the same extension.. */
2328 // Skip not source sources, they do not have file fields.
69c2ecbd 2329 for (; Vf.end() == false; ++Vf)
b2e465d6
AL
2330 {
2331 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2332 continue;
2333 break;
2334 }
2335
2336 // Does not really matter here.. we are going to fail out below
2337 if (Vf.end() != true)
2338 {
2339 // If this fails to get a file name we will bomb out below.
2340 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2341 if (_error->PendingError() == true)
2342 return;
2343
2344 // Generate the final file name as: package_version_arch.foo
2345 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
2346 QuoteString(Version.VerStr(),"_:") + '_' +
2347 QuoteString(Version.Arch(),"_:.") +
2348 "." + flExtension(Parse.FileName());
2349 }
b3d44315
MV
2350
2351 // check if we have one trusted source for the package. if so, switch
6c34ccca
DK
2352 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
2353 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
2354 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
2355 bool seenUntrusted = false;
f7f0d6c7 2356 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
b3d44315
MV
2357 {
2358 pkgIndexFile *Index;
2359 if (Sources->FindIndex(i.File(),Index) == false)
2360 continue;
6c34ccca
DK
2361
2362 if (debugAuth == true)
b3d44315 2363 std::cerr << "Checking index: " << Index->Describe()
6c34ccca
DK
2364 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
2365
2366 if (Index->IsTrusted() == true)
2367 {
b3d44315 2368 Trusted = true;
6c34ccca
DK
2369 if (allowUnauth == false)
2370 break;
b3d44315 2371 }
6c34ccca
DK
2372 else
2373 seenUntrusted = true;
b3d44315
MV
2374 }
2375
a3371852
MV
2376 // "allow-unauthenticated" restores apts old fetching behaviour
2377 // that means that e.g. unauthenticated file:// uris are higher
2378 // priority than authenticated http:// uris
6c34ccca 2379 if (allowUnauth == true && seenUntrusted == true)
a3371852
MV
2380 Trusted = false;
2381
03e39e59 2382 // Select a source
b185acc2 2383 if (QueueNext() == false && _error->PendingError() == false)
d57f6084
DK
2384 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2385 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
b185acc2
AL
2386}
2387 /*}}}*/
2388// AcqArchive::QueueNext - Queue the next file source /*{{{*/
2389// ---------------------------------------------------------------------
17caf1b1
AL
2390/* This queues the next available file version for download. It checks if
2391 the archive is already available in the cache and stashs the MD5 for
2392 checking later. */
b185acc2 2393bool pkgAcqArchive::QueueNext()
a722b2c5 2394{
f7f0d6c7 2395 for (; Vf.end() == false; ++Vf)
03e39e59
AL
2396 {
2397 // Ignore not source sources
2398 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2399 continue;
2400
2401 // Try to cross match against the source list
b2e465d6
AL
2402 pkgIndexFile *Index;
2403 if (Sources->FindIndex(Vf.File(),Index) == false)
2404 continue;
03e39e59 2405
b3d44315
MV
2406 // only try to get a trusted package from another source if that source
2407 // is also trusted
2408 if(Trusted && !Index->IsTrusted())
2409 continue;
2410
03e39e59
AL
2411 // Grab the text package record
2412 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2413 if (_error->PendingError() == true)
b185acc2 2414 return false;
b3501edb 2415
b2e465d6 2416 string PkgFile = Parse.FileName();
b3501edb
DK
2417 ExpectedHashes = Parse.Hashes();
2418
03e39e59 2419 if (PkgFile.empty() == true)
b2e465d6
AL
2420 return _error->Error(_("The package index files are corrupted. No Filename: "
2421 "field for package %s."),
2422 Version.ParentPkg().Name());
a6568219 2423
b3d44315
MV
2424 Desc.URI = Index->ArchiveURI(PkgFile);
2425 Desc.Description = Index->ArchiveInfo(Version);
2426 Desc.Owner = this;
40f8a8ba 2427 Desc.ShortDesc = Version.ParentPkg().FullName(true);
b3d44315 2428
17caf1b1 2429 // See if we already have the file. (Legacy filenames)
a6568219
AL
2430 FileSize = Version->Size;
2431 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2432 struct stat Buf;
2433 if (stat(FinalFile.c_str(),&Buf) == 0)
2434 {
2435 // Make sure the size matches
73da43e9 2436 if ((unsigned long long)Buf.st_size == Version->Size)
a6568219
AL
2437 {
2438 Complete = true;
2439 Local = true;
2440 Status = StatDone;
30e1eab5 2441 StoreFilename = DestFile = FinalFile;
b185acc2 2442 return true;
a6568219
AL
2443 }
2444
6b1ff003
AL
2445 /* Hmm, we have a file and its size does not match, this means it is
2446 an old style mismatched arch */
a6568219
AL
2447 unlink(FinalFile.c_str());
2448 }
17caf1b1
AL
2449
2450 // Check it again using the new style output filenames
2451 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
2452 if (stat(FinalFile.c_str(),&Buf) == 0)
2453 {
2454 // Make sure the size matches
73da43e9 2455 if ((unsigned long long)Buf.st_size == Version->Size)
17caf1b1
AL
2456 {
2457 Complete = true;
2458 Local = true;
2459 Status = StatDone;
2460 StoreFilename = DestFile = FinalFile;
2461 return true;
2462 }
2463
1e3f4083 2464 /* Hmm, we have a file and its size does not match, this shouldn't
17caf1b1
AL
2465 happen.. */
2466 unlink(FinalFile.c_str());
2467 }
2468
2469 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
6b1ff003
AL
2470
2471 // Check the destination file
2472 if (stat(DestFile.c_str(),&Buf) == 0)
2473 {
2474 // Hmm, the partial file is too big, erase it
73da43e9 2475 if ((unsigned long long)Buf.st_size > Version->Size)
6b1ff003
AL
2476 unlink(DestFile.c_str());
2477 else
5684f71f 2478 {
6b1ff003 2479 PartialSize = Buf.st_size;
ea7682a0 2480 ChangeOwnerAndPermissionOfFile("pkgAcqArchive::QueueNext", FinalFile.c_str(), "_apt", "root", 0600);
5684f71f 2481 }
6b1ff003 2482 }
de31189f
DK
2483
2484 // Disables download of archives - useful if no real installation follows,
2485 // e.g. if we are just interested in proposed installation order
2486 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
2487 {
2488 Complete = true;
2489 Local = true;
2490 Status = StatDone;
2491 StoreFilename = DestFile = FinalFile;
2492 return true;
2493 }
2494
03e39e59 2495 // Create the item
b2e465d6 2496 Local = false;
03e39e59 2497 QueueURI(Desc);
b185acc2 2498
f7f0d6c7 2499 ++Vf;
b185acc2 2500 return true;
03e39e59 2501 }
b185acc2
AL
2502 return false;
2503}
03e39e59
AL
2504 /*}}}*/
2505// AcqArchive::Done - Finished fetching /*{{{*/
2506// ---------------------------------------------------------------------
2507/* */
b3501edb 2508void pkgAcqArchive::Done(string Message,unsigned long long Size, HashStringList const &CalcHashes,
459681d3 2509 pkgAcquire::MethodConfig *Cfg)
03e39e59 2510{
b3501edb 2511 Item::Done(Message, Size, CalcHashes, Cfg);
03e39e59
AL
2512
2513 // Check the size
2514 if (Size != Version->Size)
2515 {
3c8030a4 2516 RenameOnError(SizeMismatch);
03e39e59
AL
2517 return;
2518 }
b3501edb 2519
0d29b9d4 2520 // FIXME: could this empty() check impose *any* sort of security issue?
b3501edb 2521 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
03e39e59 2522 {
3c8030a4 2523 RenameOnError(HashSumMismatch);
b3501edb 2524 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
495e5cb2 2525 return;
03e39e59 2526 }
a6568219
AL
2527
2528 // Grab the output filename
03e39e59
AL
2529 string FileName = LookupTag(Message,"Filename");
2530 if (FileName.empty() == true)
2531 {
2532 Status = StatError;
2533 ErrorText = "Method gave a blank filename";
2534 return;
2535 }
a6568219 2536
30e1eab5 2537 // Reference filename
a6568219
AL
2538 if (FileName != DestFile)
2539 {
30e1eab5 2540 StoreFilename = DestFile = FileName;
a6568219 2541 Local = true;
5684f71f 2542 Complete = true;
a6568219
AL
2543 return;
2544 }
5684f71f 2545
a6568219
AL
2546 // Done, move it into position
2547 string FinalFile = _config->FindDir("Dir::Cache::Archives");
17caf1b1 2548 FinalFile += flNotDir(StoreFilename);
a6568219 2549 Rename(DestFile,FinalFile);
ea7682a0 2550 ChangeOwnerAndPermissionOfFile("pkgAcqArchive::Done", FinalFile.c_str(), "root", "root", 0644);
30e1eab5 2551 StoreFilename = DestFile = FinalFile;
03e39e59
AL
2552 Complete = true;
2553}
2554 /*}}}*/
db890fdb
AL
2555// AcqArchive::Failed - Failure handler /*{{{*/
2556// ---------------------------------------------------------------------
2557/* Here we try other sources */
7d8afa39 2558void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
db890fdb
AL
2559{
2560 ErrorText = LookupTag(Message,"Message");
b2e465d6
AL
2561
2562 /* We don't really want to retry on failed media swaps, this prevents
2563 that. An interesting observation is that permanent failures are not
2564 recorded. */
2565 if (Cnf->Removable == true &&
2566 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2567 {
2568 // Vf = Version.FileList();
f7f0d6c7 2569 while (Vf.end() == false) ++Vf;
b2e465d6
AL
2570 StoreFilename = string();
2571 Item::Failed(Message,Cnf);
2572 return;
2573 }
2574
db890fdb 2575 if (QueueNext() == false)
7d8afa39
AL
2576 {
2577 // This is the retry counter
2578 if (Retries != 0 &&
2579 Cnf->LocalOnly == false &&
2580 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2581 {
2582 Retries--;
2583 Vf = Version.FileList();
2584 if (QueueNext() == true)
2585 return;
2586 }
2587
9dbb421f 2588 StoreFilename = string();
7d8afa39
AL
2589 Item::Failed(Message,Cnf);
2590 }
db890fdb
AL
2591}
2592 /*}}}*/
92fcbfc1 2593// AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
b3d44315 2594// ---------------------------------------------------------------------
b3501edb 2595APT_PURE bool pkgAcqArchive::IsTrusted() const
b3d44315
MV
2596{
2597 return Trusted;
2598}
92fcbfc1 2599 /*}}}*/
ab559b35
AL
2600// AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
2601// ---------------------------------------------------------------------
2602/* */
2603void pkgAcqArchive::Finished()
2604{
2605 if (Status == pkgAcquire::Item::StatDone &&
2606 Complete == true)
2607 return;
2608 StoreFilename = string();
2609}
2610 /*}}}*/
36375005
AL
2611// AcqFile::pkgAcqFile - Constructor /*{{{*/
2612// ---------------------------------------------------------------------
2613/* The file is added to the queue */
b3501edb 2614pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI, HashStringList const &Hashes,
73da43e9 2615 unsigned long long Size,string Dsc,string ShortDesc,
77278c2b
MV
2616 const string &DestDir, const string &DestFilename,
2617 bool IsIndexFile) :
fa3b260f 2618 Item(Owner, Hashes), IsIndexFile(IsIndexFile)
36375005 2619{
08cfc005
AL
2620 Retries = _config->FindI("Acquire::Retries",0);
2621
46e00f9d
MV
2622 if(!DestFilename.empty())
2623 DestFile = DestFilename;
2624 else if(!DestDir.empty())
2625 DestFile = DestDir + "/" + flNotDir(URI);
2626 else
2627 DestFile = flNotDir(URI);
2628
36375005
AL
2629 // Create the item
2630 Desc.URI = URI;
2631 Desc.Description = Dsc;
2632 Desc.Owner = this;
2633
2634 // Set the short description to the archive component
2635 Desc.ShortDesc = ShortDesc;
2636
2637 // Get the transfer sizes
2638 FileSize = Size;
2639 struct stat Buf;
2640 if (stat(DestFile.c_str(),&Buf) == 0)
2641 {
2642 // Hmm, the partial file is too big, erase it
ed9665ae 2643 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
36375005
AL
2644 unlink(DestFile.c_str());
2645 else
5684f71f 2646 {
36375005 2647 PartialSize = Buf.st_size;
ea7682a0 2648 ChangeOwnerAndPermissionOfFile("pkgAcqFile", DestFile.c_str(), "_apt", "root", 0600);
5684f71f 2649 }
36375005 2650 }
092ae175 2651
36375005
AL
2652 QueueURI(Desc);
2653}
2654 /*}}}*/
2655// AcqFile::Done - Item downloaded OK /*{{{*/
2656// ---------------------------------------------------------------------
2657/* */
b3501edb 2658void pkgAcqFile::Done(string Message,unsigned long long Size,HashStringList const &CalcHashes,
459681d3 2659 pkgAcquire::MethodConfig *Cnf)
36375005 2660{
b3501edb 2661 Item::Done(Message,Size,CalcHashes,Cnf);
495e5cb2 2662
8a8feb29 2663 // Check the hash
b3501edb 2664 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
b3c39978 2665 {
3c8030a4 2666 RenameOnError(HashSumMismatch);
b3501edb 2667 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
495e5cb2 2668 return;
b3c39978
AL
2669 }
2670
36375005
AL
2671 string FileName = LookupTag(Message,"Filename");
2672 if (FileName.empty() == true)
2673 {
2674 Status = StatError;
2675 ErrorText = "Method gave a blank filename";
2676 return;
2677 }
2678
2679 Complete = true;
2680
2681 // The files timestamp matches
2682 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2683 return;
2684
2685 // We have to copy it into place
2686 if (FileName != DestFile)
2687 {
2688 Local = true;
459681d3
AL
2689 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
2690 Cnf->Removable == true)
917ae805
AL
2691 {
2692 Desc.URI = "copy:" + FileName;
2693 QueueURI(Desc);
2694 return;
2695 }
2696
83ab33fc
AL
2697 // Erase the file if it is a symlink so we can overwrite it
2698 struct stat St;
2699 if (lstat(DestFile.c_str(),&St) == 0)
2700 {
2701 if (S_ISLNK(St.st_mode) != 0)
2702 unlink(DestFile.c_str());
2703 }
2704
2705 // Symlink the file
917ae805
AL
2706 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
2707 {
83ab33fc 2708 ErrorText = "Link to " + DestFile + " failure ";
917ae805
AL
2709 Status = StatError;
2710 Complete = false;
2711 }
36375005
AL
2712 }
2713}
2714 /*}}}*/
08cfc005
AL
2715// AcqFile::Failed - Failure handler /*{{{*/
2716// ---------------------------------------------------------------------
2717/* Here we try other sources */
2718void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2719{
2720 ErrorText = LookupTag(Message,"Message");
2721
2722 // This is the retry counter
2723 if (Retries != 0 &&
2724 Cnf->LocalOnly == false &&
2725 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2726 {
2727 Retries--;
2728 QueueURI(Desc);
2729 return;
2730 }
2731
2732 Item::Failed(Message,Cnf);
2733}
2734 /*}}}*/
77278c2b
MV
2735// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2736// ---------------------------------------------------------------------
2737/* The only header we use is the last-modified header. */
b3501edb 2738string pkgAcqFile::Custom600Headers() const
77278c2b
MV
2739{
2740 if (IsIndexFile)
2741 return "\nIndex-File: true";
61a07c57 2742 return "";
77278c2b
MV
2743}
2744 /*}}}*/