]> git.saurik.com Git - apt.git/blame - apt-pkg/indexcopy.cc
merged from debian-sid
[apt.git] / apt-pkg / indexcopy.cc
CommitLineData
143abaeb
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
90f057fd 3// $Id: indexcopy.cc,v 1.10 2002/03/26 07:38:58 jgg Exp $
143abaeb
AL
4/* ######################################################################
5
6 Index Copying - Aid for copying and verifying the index files
7
8 This class helps apt-cache reconstruct a damaged index files.
9
10 ##################################################################### */
11 /*}}}*/
12// Include Files /*{{{*/
ea542140 13#include<config.h>
143abaeb
AL
14
15#include <apt-pkg/error.h>
16#include <apt-pkg/progress.h>
17#include <apt-pkg/strutl.h>
18#include <apt-pkg/fileutl.h>
78c9276d 19#include <apt-pkg/aptconfiguration.h>
143abaeb
AL
20#include <apt-pkg/configuration.h>
21#include <apt-pkg/tagfile.h>
a75c6a6e
MZ
22#include <apt-pkg/indexrecords.h>
23#include <apt-pkg/md5.h>
24#include <apt-pkg/cdrom.h>
143abaeb 25
90f057fd 26#include <iostream>
a75c6a6e 27#include <sstream>
143abaeb
AL
28#include <unistd.h>
29#include <sys/stat.h>
cf440fac
DK
30#include <sys/types.h>
31#include <fcntl.h>
143abaeb 32#include <stdio.h>
650faab0 33#include <stdlib.h>
ea542140
DK
34
35#include "indexcopy.h"
36#include <apti18n.h>
143abaeb
AL
37 /*}}}*/
38
076d01b0
AL
39using namespace std;
40
143abaeb
AL
41// IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/
42// ---------------------------------------------------------------------
43/* */
a75c6a6e
MZ
44bool IndexCopy::CopyPackages(string CDROM,string Name,vector<string> &List,
45 pkgCdromStatus *log)
143abaeb 46{
a75c6a6e 47 OpProgress *Progress = NULL;
f7f0d6c7 48 if (List.empty() == true)
143abaeb
AL
49 return true;
50
a75c6a6e
MZ
51 if(log)
52 Progress = log->GetOpProgress();
143abaeb
AL
53
54 bool NoStat = _config->FindB("APT::CDROM::Fast",false);
55 bool Debug = _config->FindB("Debug::aptcdrom",false);
56
57 // Prepare the progress indicator
650faab0 58 off_t TotalSize = 0;
78c9276d 59 std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
f7f0d6c7 60 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
143abaeb
AL
61 {
62 struct stat Buf;
78c9276d
DK
63 bool found = false;
64 std::string file = std::string(*I).append(GetFileName());
65 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
66 c != compressor.end(); ++c)
67 {
68 if (stat(std::string(file + c->Extension).c_str(), &Buf) != 0)
69 continue;
70 found = true;
71 break;
72 }
73
74 if (found == false)
75 return _error->Errno("stat", "Stat failed for %s", file.c_str());
143abaeb 76 TotalSize += Buf.st_size;
78c9276d 77 }
143abaeb 78
650faab0 79 off_t CurrentSize = 0;
143abaeb
AL
80 unsigned int NotFound = 0;
81 unsigned int WrongSize = 0;
82 unsigned int Packages = 0;
f7f0d6c7 83 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
143abaeb
AL
84 {
85 string OrigPath = string(*I,CDROM.length());
143abaeb
AL
86
87 // Open the package file
144353a9 88 FileFd Pkg(*I + GetFileName(), FileFd::ReadOnly, FileFd::Auto);
699b209e 89 off_t const FileSize = Pkg.Size();
01366a44 90
b2e465d6 91 pkgTagFile Parser(&Pkg);
143abaeb
AL
92 if (_error->PendingError() == true)
93 return false;
94
95 // Open the output file
96 char S[400];
20ebd488
AL
97 snprintf(S,sizeof(S),"cdrom:[%s]/%s%s",Name.c_str(),
98 (*I).c_str() + CDROM.length(),GetFileName());
143abaeb
AL
99 string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
100 TargetF += URItoFileName(S);
8deb53ab 101 FileFd Target;
143abaeb 102 if (_config->FindB("APT::CDROM::NoAct",false) == true)
8deb53ab 103 {
143abaeb 104 TargetF = "/dev/null";
8deb53ab
MV
105 Target.Open(TargetF,FileFd::WriteExists);
106 } else {
107 Target.Open(TargetF,FileFd::WriteAtomic);
108 }
b2e465d6 109 FILE *TargetFl = fdopen(dup(Target.Fd()),"w");
143abaeb
AL
110 if (_error->PendingError() == true)
111 return false;
b2e465d6
AL
112 if (TargetFl == 0)
113 return _error->Errno("fdopen","Failed to reopen fd");
143abaeb
AL
114
115 // Setup the progress meter
a75c6a6e
MZ
116 if(Progress)
117 Progress->OverallProgress(CurrentSize,TotalSize,FileSize,
118 string("Reading ") + Type() + " Indexes");
143abaeb
AL
119
120 // Parse
a75c6a6e
MZ
121 if(Progress)
122 Progress->SubProgress(Pkg.Size());
143abaeb
AL
123 pkgTagSection Section;
124 this->Section = &Section;
125 string Prefix;
126 unsigned long Hits = 0;
127 unsigned long Chop = 0;
128 while (Parser.Step(Section) == true)
129 {
a75c6a6e
MZ
130 if(Progress)
131 Progress->Progress(Parser.Offset());
143abaeb 132 string File;
650faab0 133 unsigned long long Size;
143abaeb 134 if (GetFile(File,Size) == false)
b2e465d6
AL
135 {
136 fclose(TargetFl);
143abaeb 137 return false;
b2e465d6 138 }
143abaeb
AL
139
140 if (Chop != 0)
141 File = OrigPath + ChopDirs(File,Chop);
142
143 // See if the file exists
144 bool Mangled = false;
145 if (NoStat == false || Hits < 10)
146 {
147 // Attempt to fix broken structure
148 if (Hits == 0)
149 {
150 if (ReconstructPrefix(Prefix,OrigPath,CDROM,File) == false &&
151 ReconstructChop(Chop,*I,File) == false)
152 {
153 if (Debug == true)
154 clog << "Missed: " << File << endl;
155 NotFound++;
156 continue;
157 }
158 if (Chop != 0)
159 File = OrigPath + ChopDirs(File,Chop);
160 }
161
162 // Get the size
163 struct stat Buf;
164 if (stat(string(CDROM + Prefix + File).c_str(),&Buf) != 0 ||
165 Buf.st_size == 0)
166 {
167 // Attempt to fix busted symlink support for one instance
168 string OrigFile = File;
169 string::size_type Start = File.find("binary-");
170 string::size_type End = File.find("/",Start+3);
171 if (Start != string::npos && End != string::npos)
172 {
173 File.replace(Start,End-Start,"binary-all");
174 Mangled = true;
175 }
176
177 if (Mangled == false ||
178 stat(string(CDROM + Prefix + File).c_str(),&Buf) != 0)
179 {
180 if (Debug == true)
181 clog << "Missed(2): " << OrigFile << endl;
182 NotFound++;
183 continue;
184 }
185 }
186
187 // Size match
650faab0 188 if ((unsigned long long)Buf.st_size != Size)
143abaeb
AL
189 {
190 if (Debug == true)
191 clog << "Wrong Size: " << File << endl;
192 WrongSize++;
193 continue;
194 }
195 }
196
197 Packages++;
198 Hits++;
199
b2e465d6 200 if (RewriteEntry(TargetFl,File) == false)
143abaeb 201 {
b2e465d6
AL
202 fclose(TargetFl);
203 return false;
143abaeb 204 }
143abaeb 205 }
b2e465d6 206 fclose(TargetFl);
143abaeb
AL
207
208 if (Debug == true)
209 cout << " Processed by using Prefix '" << Prefix << "' and chop " << Chop << endl;
210
211 if (_config->FindB("APT::CDROM::NoAct",false) == false)
212 {
213 // Move out of the partial directory
214 Target.Close();
215 string FinalF = _config->FindDir("Dir::State::lists");
216 FinalF += URItoFileName(S);
217 if (rename(TargetF.c_str(),FinalF.c_str()) != 0)
218 return _error->Errno("rename","Failed to rename");
143abaeb 219 }
a75c6a6e 220
143abaeb
AL
221 /* Mangle the source to be in the proper notation with
222 prefix dist [component] */
223 *I = string(*I,Prefix.length());
224 ConvertToSourceList(CDROM,*I);
225 *I = Prefix + ' ' + *I;
226
227 CurrentSize += FileSize;
228 }
a75c6a6e
MZ
229 if(Progress)
230 Progress->Done();
143abaeb
AL
231
232 // Some stats
a75c6a6e
MZ
233 if(log) {
234 stringstream msg;
235 if(NotFound == 0 && WrongSize == 0)
236 ioprintf(msg, _("Wrote %i records.\n"), Packages);
237 else if (NotFound != 0 && WrongSize == 0)
238 ioprintf(msg, _("Wrote %i records with %i missing files.\n"),
239 Packages, NotFound);
240 else if (NotFound == 0 && WrongSize != 0)
db0db9fe 241 ioprintf(msg, _("Wrote %i records with %i mismatched files\n"),
a75c6a6e
MZ
242 Packages, WrongSize);
243 if (NotFound != 0 && WrongSize != 0)
db0db9fe 244 ioprintf(msg, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages, NotFound, WrongSize);
a75c6a6e 245 }
143abaeb
AL
246
247 if (Packages == 0)
dd27443e
AL
248 _error->Warning("No valid records were found.");
249
143abaeb 250 if (NotFound + WrongSize > 10)
46e39c8e 251 _error->Warning("A lot of entries were discarded, something may be wrong.\n");
a75c6a6e 252
143abaeb
AL
253
254 return true;
255}
256 /*}}}*/
257// IndexCopy::ChopDirs - Chop off the leading directory components /*{{{*/
258// ---------------------------------------------------------------------
259/* */
260string IndexCopy::ChopDirs(string Path,unsigned int Depth)
261{
262 string::size_type I = 0;
263 do
264 {
265 I = Path.find('/',I+1);
266 Depth--;
267 }
268 while (I != string::npos && Depth != 0);
269
270 if (I == string::npos)
271 return string();
272
273 return string(Path,I+1);
274}
275 /*}}}*/
276// IndexCopy::ReconstructPrefix - Fix strange prefixing /*{{{*/
277// ---------------------------------------------------------------------
278/* This prepends dir components from the path to the package files to
279 the path to the deb until it is found */
280bool IndexCopy::ReconstructPrefix(string &Prefix,string OrigPath,string CD,
281 string File)
282{
283 bool Debug = _config->FindB("Debug::aptcdrom",false);
284 unsigned int Depth = 1;
285 string MyPrefix = Prefix;
286 while (1)
287 {
288 struct stat Buf;
289 if (stat(string(CD + MyPrefix + File).c_str(),&Buf) != 0)
290 {
291 if (Debug == true)
292 cout << "Failed, " << CD + MyPrefix + File << endl;
293 if (GrabFirst(OrigPath,MyPrefix,Depth++) == true)
294 continue;
295
296 return false;
297 }
298 else
299 {
300 Prefix = MyPrefix;
301 return true;
302 }
303 }
304 return false;
305}
306 /*}}}*/
307// IndexCopy::ReconstructChop - Fixes bad source paths /*{{{*/
308// ---------------------------------------------------------------------
309/* This removes path components from the filename and prepends the location
310 of the package files until a file is found */
311bool IndexCopy::ReconstructChop(unsigned long &Chop,string Dir,string File)
312{
313 // Attempt to reconstruct the filename
314 unsigned long Depth = 0;
315 while (1)
316 {
317 struct stat Buf;
318 if (stat(string(Dir + File).c_str(),&Buf) != 0)
319 {
320 File = ChopDirs(File,1);
321 Depth++;
322 if (File.empty() == false)
323 continue;
324 return false;
325 }
326 else
327 {
328 Chop = Depth;
329 return true;
330 }
331 }
332 return false;
333}
334 /*}}}*/
335// IndexCopy::ConvertToSourceList - Convert a Path to a sourcelist /*{{{*/
336// ---------------------------------------------------------------------
337/* We look for things in dists/ notation and convert them to
338 <dist> <component> form otherwise it is left alone. This also strips
0f770a0c
AL
339 the CD path.
340
341 This implements a regex sort of like:
342 (.*)/dists/([^/]*)/(.*)/binary-*
343 ^ ^ ^- Component
344 | |-------- Distribution
345 |------------------- Path
346
347 It was deciced to use only a single word for dist (rather than say
348 unstable/non-us) to increase the chance that each CD gets a single
349 line in sources.list.
350 */
143abaeb
AL
351void IndexCopy::ConvertToSourceList(string CD,string &Path)
352{
143abaeb
AL
353 // Strip the cdrom base path
354 Path = string(Path,CD.length());
355 if (Path.empty() == true)
356 Path = "/";
357
358 // Too short to be a dists/ type
359 if (Path.length() < strlen("dists/"))
360 return;
361
362 // Not a dists type.
076d01b0 363 if (stringcmp(Path.c_str(),Path.c_str()+strlen("dists/"),"dists/") != 0)
143abaeb 364 return;
7834cb57 365
143abaeb
AL
366 // Isolate the dist
367 string::size_type Slash = strlen("dists/");
368 string::size_type Slash2 = Path.find('/',Slash + 1);
369 if (Slash2 == string::npos || Slash2 + 2 >= Path.length())
370 return;
371 string Dist = string(Path,Slash,Slash2 - Slash);
372
373 // Isolate the component
0f770a0c
AL
374 Slash = Slash2;
375 for (unsigned I = 0; I != 10; I++)
376 {
377 Slash = Path.find('/',Slash+1);
378 if (Slash == string::npos || Slash + 2 >= Path.length())
379 return;
380 string Comp = string(Path,Slash2+1,Slash - Slash2-1);
381
382 // Verify the trailing binary- bit
383 string::size_type BinSlash = Path.find('/',Slash + 1);
384 if (Slash == string::npos)
385 return;
386 string Binary = string(Path,Slash+1,BinSlash - Slash-1);
387
d29a5330
DK
388 if (strncmp(Binary.c_str(), "binary-", strlen("binary-")) == 0)
389 {
390 Binary.erase(0, strlen("binary-"));
391 if (APT::Configuration::checkArchitecture(Binary) == false)
392 continue;
393 }
394 else if (Binary != "source")
0f770a0c
AL
395 continue;
396
397 Path = Dist + ' ' + Comp;
143abaeb 398 return;
0f770a0c 399 }
143abaeb
AL
400}
401 /*}}}*/
402// IndexCopy::GrabFirst - Return the first Depth path components /*{{{*/
403// ---------------------------------------------------------------------
404/* */
405bool IndexCopy::GrabFirst(string Path,string &To,unsigned int Depth)
406{
407 string::size_type I = 0;
408 do
409 {
410 I = Path.find('/',I+1);
411 Depth--;
412 }
413 while (I != string::npos && Depth != 0);
414
415 if (I == string::npos)
416 return false;
417
418 To = string(Path,0,I+1);
419 return true;
420}
421 /*}}}*/
143abaeb
AL
422// PackageCopy::GetFile - Get the file information from the section /*{{{*/
423// ---------------------------------------------------------------------
424/* */
650faab0 425bool PackageCopy::GetFile(string &File,unsigned long long &Size)
143abaeb
AL
426{
427 File = Section->FindS("Filename");
428 Size = Section->FindI("Size");
429 if (File.empty() || Size == 0)
430 return _error->Error("Cannot find filename or size tag");
431 return true;
432}
433 /*}}}*/
434// PackageCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
435// ---------------------------------------------------------------------
436/* */
b2e465d6 437bool PackageCopy::RewriteEntry(FILE *Target,string File)
143abaeb 438{
b2e465d6
AL
439 TFRewriteData Changes[] = {{"Filename",File.c_str()},
440 {}};
441
442 if (TFRewrite(Target,*Section,TFRewritePackageOrder,Changes) == false)
443 return false;
444 fputc('\n',Target);
445 return true;
143abaeb
AL
446}
447 /*}}}*/
448// SourceCopy::GetFile - Get the file information from the section /*{{{*/
449// ---------------------------------------------------------------------
450/* */
650faab0 451bool SourceCopy::GetFile(string &File,unsigned long long &Size)
143abaeb
AL
452{
453 string Files = Section->FindS("Files");
454 if (Files.empty() == true)
455 return false;
456
457 // Stash the / terminated directory prefix
458 string Base = Section->FindS("Directory");
459 if (Base.empty() == false && Base[Base.length()-1] != '/')
460 Base += '/';
461
b2e465d6 462 // Read the first file triplet
143abaeb
AL
463 const char *C = Files.c_str();
464 string sSize;
465 string MD5Hash;
466
467 // Parse each of the elements
468 if (ParseQuoteWord(C,MD5Hash) == false ||
469 ParseQuoteWord(C,sSize) == false ||
470 ParseQuoteWord(C,File) == false)
471 return _error->Error("Error parsing file record");
472
473 // Parse the size and append the directory
650faab0 474 Size = strtoull(sSize.c_str(), NULL, 10);
143abaeb
AL
475 File = Base + File;
476 return true;
477}
478 /*}}}*/
479// SourceCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
480// ---------------------------------------------------------------------
481/* */
b2e465d6 482bool SourceCopy::RewriteEntry(FILE *Target,string File)
143abaeb 483{
b2e465d6
AL
484 string Dir(File,0,File.rfind('/'));
485 TFRewriteData Changes[] = {{"Directory",Dir.c_str()},
486 {}};
487
488 if (TFRewrite(Target,*Section,TFRewriteSourceOrder,Changes) == false)
489 return false;
490 fputc('\n',Target);
491 return true;
143abaeb
AL
492}
493 /*}}}*/
22f8568d
MV
494// SigVerify::Verify - Verify a files md5sum against its metaindex /*{{{*/
495// ---------------------------------------------------------------------
496/* */
a75c6a6e
MZ
497bool SigVerify::Verify(string prefix, string file, indexRecords *MetaIndex)
498{
499 const indexRecords::checkSum *Record = MetaIndex->Lookup(file);
12c7078f 500 bool const Debug = _config->FindB("Debug::aptcdrom",false);
a75c6a6e 501
12c7078f
DK
502 // we skip non-existing files in the verifcation of the Release file
503 // as non-existing files do not harm, but a warning scares people and
504 // makes it hard to strip unneeded files from an ISO like uncompressed
505 // indexes as it is done on the mirrors (see also LP: #255545 )
8220213e 506 if(!RealFileExists(prefix+file))
8d357c52 507 {
12c7078f
DK
508 if (Debug == true)
509 cout << "Skipping nonexistent in " << prefix << " file " << file << std::endl;
8d357c52
MV
510 return true;
511 }
512
12c7078f 513 if (!Record)
a75c6a6e 514 {
a1e42d1f 515 _error->Warning(_("Can't find authentication record for: %s"), file.c_str());
a75c6a6e
MZ
516 return false;
517 }
518
495e5cb2 519 if (!Record->Hash.VerifyFile(prefix+file))
a75c6a6e 520 {
a1e42d1f 521 _error->Warning(_("Hash mismatch for: %s"),file.c_str());
a75c6a6e
MZ
522 return false;
523 }
524
12c7078f 525 if(Debug == true)
a75c6a6e
MZ
526 {
527 cout << "File: " << prefix+file << endl;
495e5cb2 528 cout << "Expected Hash " << Record->Hash.toStr() << endl;
a75c6a6e
MZ
529 }
530
531 return true;
532}
92fcbfc1
DK
533 /*}}}*/
534bool SigVerify::CopyMetaIndex(string CDROM, string CDName, /*{{{*/
a75c6a6e
MZ
535 string prefix, string file)
536{
537 char S[400];
538 snprintf(S,sizeof(S),"cdrom:[%s]/%s%s",CDName.c_str(),
539 (prefix).c_str() + CDROM.length(),file.c_str());
540 string TargetF = _config->FindDir("Dir::State::lists");
541 TargetF += URItoFileName(S);
542
543 FileFd Target;
544 FileFd Rel;
22041bd2 545 Target.Open(TargetF,FileFd::WriteAtomic);
a75c6a6e
MZ
546 Rel.Open(prefix + file,FileFd::ReadOnly);
547 if (_error->PendingError() == true)
548 return false;
549 if (CopyFile(Rel,Target) == false)
550 return false;
551
552 return true;
553}
92fcbfc1
DK
554 /*}}}*/
555bool SigVerify::CopyAndVerify(string CDROM,string Name,vector<string> &SigList, /*{{{*/
a75c6a6e
MZ
556 vector<string> PkgList,vector<string> SrcList)
557{
f7f0d6c7 558 if (SigList.empty() == true)
a75c6a6e
MZ
559 return true;
560
561 bool Debug = _config->FindB("Debug::aptcdrom",false);
562
563 // Read all Release files
f7f0d6c7 564 for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); ++I)
a75c6a6e
MZ
565 {
566 if(Debug)
567 cout << "Signature verify for: " << *I << endl;
568
569 indexRecords *MetaIndex = new indexRecords;
570 string prefix = *I;
571
a319c4ee
DK
572 string const releasegpg = *I+"Release.gpg";
573 string const release = *I+"Release";
212080b8
DK
574 string const inrelease = *I+"InRelease";
575 bool useInRelease = true;
a319c4ee 576
a75c6a6e 577 // a Release.gpg without a Release should never happen
212080b8
DK
578 if (RealFileExists(inrelease) == true)
579 ;
580 else if(RealFileExists(release) == false || RealFileExists(releasegpg) == false)
cfb3d242
DK
581 {
582 delete MetaIndex;
a75c6a6e 583 continue;
cfb3d242 584 }
212080b8
DK
585 else
586 useInRelease = false;
a75c6a6e 587
a75c6a6e
MZ
588 pid_t pid = ExecFork();
589 if(pid < 0) {
590 _error->Error("Fork failed");
591 return false;
592 }
cf440fac 593 if(pid == 0)
212080b8
DK
594 {
595 if (useInRelease == true)
596 RunGPGV(inrelease, inrelease);
597 else
598 RunGPGV(release, releasegpg);
599 }
cf440fac 600
a75c6a6e
MZ
601 if(!ExecWait(pid, "gpgv")) {
602 _error->Warning("Signature verification failed for: %s",
212080b8 603 (useInRelease ? inrelease.c_str() : releasegpg.c_str()));
a75c6a6e
MZ
604 // something went wrong, don't copy the Release.gpg
605 // FIXME: delete any existing gpg file?
606 continue;
607 }
608
609 // Open the Release file and add it to the MetaIndex
a319c4ee 610 if(!MetaIndex->Load(release))
a75c6a6e 611 {
9b5d79ec 612 _error->Error("%s",MetaIndex->ErrorText.c_str());
a75c6a6e
MZ
613 return false;
614 }
615
616 // go over the Indexfiles and see if they verify
617 // if so, remove them from our copy of the lists
618 vector<string> keys = MetaIndex->MetaKeys();
f7f0d6c7 619 for (vector<string>::iterator I = keys.begin(); I != keys.end(); ++I)
a75c6a6e
MZ
620 {
621 if(!Verify(prefix,*I, MetaIndex)) {
622 // something went wrong, don't copy the Release.gpg
623 // FIXME: delete any existing gpg file?
7efdcd3a 624 _error->Discard();
a75c6a6e
MZ
625 continue;
626 }
627 }
628
629 // we need a fresh one for the Release.gpg
630 delete MetaIndex;
631
632 // everything was fine, copy the Release and Release.gpg file
212080b8
DK
633 if (useInRelease == true)
634 CopyMetaIndex(CDROM, Name, prefix, "InRelease");
635 else
636 {
637 CopyMetaIndex(CDROM, Name, prefix, "Release");
638 CopyMetaIndex(CDROM, Name, prefix, "Release.gpg");
639 }
a75c6a6e
MZ
640 }
641
642 return true;
643}
92fcbfc1 644 /*}}}*/
cf440fac 645// SigVerify::RunGPGV - returns the command needed for verify /*{{{*/
a319c4ee
DK
646// ---------------------------------------------------------------------
647/* Generating the commandline for calling gpgv is somehow complicated as
648 we need to add multiple keyrings and user supplied options. Also, as
649 the cdrom code currently can not use the gpgv method we have two places
650 these need to be done - so the place for this method is wrong but better
651 than code duplication… */
cf440fac
DK
652bool SigVerify::RunGPGV(std::string const &File, std::string const &FileGPG,
653 int const &statusfd, int fd[2])
a319c4ee 654{
89c4c588
MV
655 if (File == FileGPG)
656 {
89c4c588
MV
657 FILE* gpg = fopen(File.c_str(), "r");
658 if (gpg == NULL)
659 return _error->Errno("RunGPGV", _("Could not open file %s"), File.c_str());
89c4c588 660 fclose(gpg);
fe5804fc 661 if (!StartsWithGPGClearTextSignature(File))
89c4c588 662 return _error->Error(_("File %s doesn't start with a clearsigned message"), File.c_str());
89c4c588
MV
663 }
664
665
a319c4ee
DK
666 string const gpgvpath = _config->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
667 // FIXME: remove support for deprecated APT::GPGV setting
1dc03a86 668 string const trustedFile = _config->Find("APT::GPGV::TrustedKeyring", _config->FindFile("Dir::Etc::Trusted"));
4368851d 669 string const trustedPath = _config->FindDir("Dir::Etc::TrustedParts");
a319c4ee 670
cf440fac
DK
671 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
672
673 if (Debug == true)
a319c4ee
DK
674 {
675 std::clog << "gpgv path: " << gpgvpath << std::endl;
676 std::clog << "Keyring file: " << trustedFile << std::endl;
677 std::clog << "Keyring path: " << trustedPath << std::endl;
678 }
679
5b777e8f
MV
680 std::vector<string> keyrings;
681 if (DirectoryExists(trustedPath))
3d661e4a 682 keyrings = GetListOfFilesInDir(trustedPath, "gpg", false, true);
8220213e 683 if (RealFileExists(trustedFile) == true)
3d661e4a 684 keyrings.push_back(trustedFile);
a319c4ee
DK
685
686 std::vector<const char *> Args;
687 Args.reserve(30);
688
689 if (keyrings.empty() == true)
89c4c588
MV
690 {
691 // TRANSLATOR: %s is the trusted keyring parts directory
692 return _error->Error(_("No keyring installed in %s."),
693 _config->FindDir("Dir::Etc::TrustedParts").c_str());
694 }
a319c4ee
DK
695
696 Args.push_back(gpgvpath.c_str());
697 Args.push_back("--ignore-time-conflict");
698
cf440fac
DK
699 if (statusfd != -1)
700 {
701 Args.push_back("--status-fd");
702 char fd[10];
703 snprintf(fd, sizeof(fd), "%i", statusfd);
704 Args.push_back(fd);
705 }
706
a319c4ee
DK
707 for (vector<string>::const_iterator K = keyrings.begin();
708 K != keyrings.end(); ++K)
709 {
710 Args.push_back("--keyring");
711 Args.push_back(K->c_str());
712 }
713
714 Configuration::Item const *Opts;
715 Opts = _config->Tree("Acquire::gpgv::Options");
716 if (Opts != 0)
717 {
718 Opts = Opts->Child;
719 for (; Opts != 0; Opts = Opts->Next)
720 {
721 if (Opts->Value.empty() == true)
722 continue;
723 Args.push_back(Opts->Value.c_str());
724 }
725 }
726
cf440fac 727 Args.push_back(FileGPG.c_str());
fe0f7911
DK
728 if (FileGPG != File)
729 Args.push_back(File.c_str());
cf440fac
DK
730 Args.push_back(NULL);
731
732 if (Debug == true)
733 {
734 std::clog << "Preparing to exec: " << gpgvpath;
735 for (std::vector<const char *>::const_iterator a = Args.begin(); *a != NULL; ++a)
736 std::clog << " " << *a;
737 std::clog << std::endl;
738 }
739
740 if (statusfd != -1)
741 {
742 int const nullfd = open("/dev/null", O_RDONLY);
743 close(fd[0]);
744 // Redirect output to /dev/null; we read from the status fd
745 dup2(nullfd, STDOUT_FILENO);
746 dup2(nullfd, STDERR_FILENO);
747 // Redirect the pipe to the status fd (3)
748 dup2(fd[1], statusfd);
749
750 putenv((char *)"LANG=");
751 putenv((char *)"LC_ALL=");
752 putenv((char *)"LC_MESSAGES=");
753 }
754
755 execvp(gpgvpath.c_str(), (char **) &Args[0]);
756 return true;
a319c4ee
DK
757}
758 /*}}}*/
92fcbfc1
DK
759bool TranslationsCopy::CopyTranslations(string CDROM,string Name, /*{{{*/
760 vector<string> &List, pkgCdromStatus *log)
22f8568d
MV
761{
762 OpProgress *Progress = NULL;
f7f0d6c7 763 if (List.empty() == true)
22f8568d
MV
764 return true;
765
766 if(log)
767 Progress = log->GetOpProgress();
768
769 bool Debug = _config->FindB("Debug::aptcdrom",false);
770
771 // Prepare the progress indicator
650faab0 772 off_t TotalSize = 0;
78c9276d 773 std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
f7f0d6c7 774 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
22f8568d
MV
775 {
776 struct stat Buf;
78c9276d
DK
777 bool found = false;
778 std::string file = *I;
779 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
780 c != compressor.end(); ++c)
781 {
782 if (stat(std::string(file + c->Extension).c_str(), &Buf) != 0)
783 continue;
784 found = true;
785 break;
786 }
787
788 if (found == false)
789 return _error->Errno("stat", "Stat failed for %s", file.c_str());
22f8568d 790 TotalSize += Buf.st_size;
78c9276d 791 }
22f8568d 792
650faab0 793 off_t CurrentSize = 0;
22f8568d
MV
794 unsigned int NotFound = 0;
795 unsigned int WrongSize = 0;
796 unsigned int Packages = 0;
f7f0d6c7 797 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
22f8568d
MV
798 {
799 string OrigPath = string(*I,CDROM.length());
699b209e 800
22f8568d 801 // Open the package file
144353a9 802 FileFd Pkg(*I, FileFd::ReadOnly, FileFd::Auto);
699b209e
DK
803 off_t const FileSize = Pkg.Size();
804
22f8568d
MV
805 pkgTagFile Parser(&Pkg);
806 if (_error->PendingError() == true)
807 return false;
808
809 // Open the output file
810 char S[400];
811 snprintf(S,sizeof(S),"cdrom:[%s]/%s",Name.c_str(),
812 (*I).c_str() + CDROM.length());
813 string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
814 TargetF += URItoFileName(S);
d5da93b8 815 FileFd Target;
22f8568d 816 if (_config->FindB("APT::CDROM::NoAct",false) == true)
d5da93b8 817 {
22f8568d 818 TargetF = "/dev/null";
d5da93b8
DK
819 Target.Open(TargetF,FileFd::WriteExists);
820 } else {
821 Target.Open(TargetF,FileFd::WriteAtomic);
822 }
22f8568d
MV
823 FILE *TargetFl = fdopen(dup(Target.Fd()),"w");
824 if (_error->PendingError() == true)
825 return false;
826 if (TargetFl == 0)
827 return _error->Errno("fdopen","Failed to reopen fd");
828
829 // Setup the progress meter
830 if(Progress)
831 Progress->OverallProgress(CurrentSize,TotalSize,FileSize,
832 string("Reading Translation Indexes"));
833
834 // Parse
835 if(Progress)
836 Progress->SubProgress(Pkg.Size());
837 pkgTagSection Section;
838 this->Section = &Section;
839 string Prefix;
840 unsigned long Hits = 0;
22f8568d
MV
841 while (Parser.Step(Section) == true)
842 {
843 if(Progress)
844 Progress->Progress(Parser.Offset());
845
846 const char *Start;
847 const char *Stop;
848 Section.GetSection(Start,Stop);
849 fwrite(Start,Stop-Start, 1, TargetFl);
850 fputc('\n',TargetFl);
851
852 Packages++;
853 Hits++;
854 }
855 fclose(TargetFl);
856
857 if (Debug == true)
91c03d37 858 cout << " Processed by using Prefix '" << Prefix << "' and chop " << endl;
22f8568d
MV
859
860 if (_config->FindB("APT::CDROM::NoAct",false) == false)
861 {
862 // Move out of the partial directory
863 Target.Close();
864 string FinalF = _config->FindDir("Dir::State::lists");
865 FinalF += URItoFileName(S);
866 if (rename(TargetF.c_str(),FinalF.c_str()) != 0)
867 return _error->Errno("rename","Failed to rename");
868 }
869
870
871 CurrentSize += FileSize;
872 }
873 if(Progress)
874 Progress->Done();
875
876 // Some stats
877 if(log) {
878 stringstream msg;
879 if(NotFound == 0 && WrongSize == 0)
880 ioprintf(msg, _("Wrote %i records.\n"), Packages);
881 else if (NotFound != 0 && WrongSize == 0)
882 ioprintf(msg, _("Wrote %i records with %i missing files.\n"),
883 Packages, NotFound);
884 else if (NotFound == 0 && WrongSize != 0)
885 ioprintf(msg, _("Wrote %i records with %i mismatched files\n"),
886 Packages, WrongSize);
887 if (NotFound != 0 && WrongSize != 0)
888 ioprintf(msg, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages, NotFound, WrongSize);
889 }
890
891 if (Packages == 0)
892 _error->Warning("No valid records were found.");
893
894 if (NotFound + WrongSize > 10)
46e39c8e 895 _error->Warning("A lot of entries were discarded, something may be wrong.\n");
22f8568d
MV
896
897
898 return true;
899}
92fcbfc1 900 /*}}}*/