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