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