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