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