]> git.saurik.com Git - apt.git/blob - ftparchive/writer.cc
add sha512 support to apt-ftparchive
[apt.git] / ftparchive / writer.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: writer.cc,v 1.14 2004/03/24 01:40:43 mdz Exp $
4 /* ######################################################################
5
6 Writer
7
8 The file writer classes. These write various types of output, sources,
9 packages and contents.
10
11 ##################################################################### */
12 /*}}}*/
13 // Include Files /*{{{*/
14 #include "writer.h"
15
16 #include <apti18n.h>
17 #include <apt-pkg/strutl.h>
18 #include <apt-pkg/error.h>
19 #include <apt-pkg/configuration.h>
20 #include <apt-pkg/md5.h>
21 #include <apt-pkg/sha1.h>
22 #include <apt-pkg/sha256.h>
23 #include <apt-pkg/sha512.h>
24 #include <apt-pkg/deblistparser.h>
25
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <ctime>
29 #include <ftw.h>
30 #include <fnmatch.h>
31 #include <iostream>
32 #include <sstream>
33 #include <memory>
34
35 #include "cachedb.h"
36 #include "apt-ftparchive.h"
37 #include "multicompress.h"
38 /*}}}*/
39 using namespace std;
40 FTWScanner *FTWScanner::Owner;
41
42 // SetTFRewriteData - Helper for setting rewrite lists /*{{{*/
43 // ---------------------------------------------------------------------
44 /* */
45 inline void SetTFRewriteData(struct TFRewriteData &tfrd,
46 const char *tag,
47 const char *rewrite,
48 const char *newtag = 0)
49 {
50 tfrd.Tag = tag;
51 tfrd.Rewrite = rewrite;
52 tfrd.NewTag = newtag;
53 }
54 /*}}}*/
55
56 // FTWScanner::FTWScanner - Constructor /*{{{*/
57 // ---------------------------------------------------------------------
58 /* */
59 FTWScanner::FTWScanner(string const &Arch): Arch(Arch)
60 {
61 ErrorPrinted = false;
62 NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
63 }
64 /*}}}*/
65 // FTWScanner::Scanner - FTW Scanner /*{{{*/
66 // ---------------------------------------------------------------------
67 /* This is the FTW scanner, it processes each directory element in the
68 directory tree. */
69 int FTWScanner::ScannerFTW(const char *File,const struct stat *sb,int Flag)
70 {
71 if (Flag == FTW_DNR)
72 {
73 Owner->NewLine(1);
74 ioprintf(c1out, _("W: Unable to read directory %s\n"), File);
75 }
76 if (Flag == FTW_NS)
77 {
78 Owner->NewLine(1);
79 ioprintf(c1out, _("W: Unable to stat %s\n"), File);
80 }
81 if (Flag != FTW_F)
82 return 0;
83
84 return ScannerFile(File, true);
85 }
86 /*}}}*/
87 // FTWScanner::ScannerFile - File Scanner /*{{{*/
88 // ---------------------------------------------------------------------
89 /* */
90 int FTWScanner::ScannerFile(const char *File, bool const &ReadLink)
91 {
92 const char *LastComponent = strrchr(File, '/');
93 char *RealPath = NULL;
94
95 if (LastComponent == NULL)
96 LastComponent = File;
97 else
98 LastComponent++;
99
100 vector<string>::const_iterator I;
101 for(I = Owner->Patterns.begin(); I != Owner->Patterns.end(); ++I)
102 {
103 if (fnmatch((*I).c_str(), LastComponent, 0) == 0)
104 break;
105 }
106 if (I == Owner->Patterns.end())
107 return 0;
108
109 /* Process it. If the file is a link then resolve it into an absolute
110 name.. This works best if the directory components the scanner are
111 given are not links themselves. */
112 char Jnk[2];
113 Owner->OriginalPath = File;
114 if (ReadLink &&
115 readlink(File,Jnk,sizeof(Jnk)) != -1 &&
116 (RealPath = realpath(File,NULL)) != 0)
117 {
118 Owner->DoPackage(RealPath);
119 free(RealPath);
120 }
121 else
122 Owner->DoPackage(File);
123
124 if (_error->empty() == false)
125 {
126 // Print any errors or warnings found
127 string Err;
128 bool SeenPath = false;
129 while (_error->empty() == false)
130 {
131 Owner->NewLine(1);
132
133 bool const Type = _error->PopMessage(Err);
134 if (Type == true)
135 cerr << _("E: ") << Err << endl;
136 else
137 cerr << _("W: ") << Err << endl;
138
139 if (Err.find(File) != string::npos)
140 SeenPath = true;
141 }
142
143 if (SeenPath == false)
144 cerr << _("E: Errors apply to file ") << "'" << File << "'" << endl;
145 return 0;
146 }
147
148 return 0;
149 }
150 /*}}}*/
151 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
152 // ---------------------------------------------------------------------
153 /* */
154 bool FTWScanner::RecursiveScan(string const &Dir)
155 {
156 char *RealPath = NULL;
157 /* If noprefix is set then jam the scan root in, so we don't generate
158 link followed paths out of control */
159 if (InternalPrefix.empty() == true)
160 {
161 if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
162 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
163 InternalPrefix = RealPath;
164 free(RealPath);
165 }
166
167 // Do recursive directory searching
168 Owner = this;
169 int const Res = ftw(Dir.c_str(),ScannerFTW,30);
170
171 // Error treewalking?
172 if (Res != 0)
173 {
174 if (_error->PendingError() == false)
175 _error->Errno("ftw",_("Tree walking failed"));
176 return false;
177 }
178
179 return true;
180 }
181 /*}}}*/
182 // FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
183 // ---------------------------------------------------------------------
184 /* This is an alternative to using FTW to locate files, it reads the list
185 of files from another file. */
186 bool FTWScanner::LoadFileList(string const &Dir, string const &File)
187 {
188 char *RealPath = NULL;
189 /* If noprefix is set then jam the scan root in, so we don't generate
190 link followed paths out of control */
191 if (InternalPrefix.empty() == true)
192 {
193 if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
194 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
195 InternalPrefix = RealPath;
196 free(RealPath);
197 }
198
199 Owner = this;
200 FILE *List = fopen(File.c_str(),"r");
201 if (List == 0)
202 return _error->Errno("fopen",_("Failed to open %s"),File.c_str());
203
204 /* We are a tad tricky here.. We prefix the buffer with the directory
205 name, that way if we need a full path with just use line.. Sneaky and
206 fully evil. */
207 char Line[1000];
208 char *FileStart;
209 if (Dir.empty() == true || Dir.end()[-1] != '/')
210 FileStart = Line + snprintf(Line,sizeof(Line),"%s/",Dir.c_str());
211 else
212 FileStart = Line + snprintf(Line,sizeof(Line),"%s",Dir.c_str());
213 while (fgets(FileStart,sizeof(Line) - (FileStart - Line),List) != 0)
214 {
215 char *FileName = _strstrip(FileStart);
216 if (FileName[0] == 0)
217 continue;
218
219 if (FileName[0] != '/')
220 {
221 if (FileName != FileStart)
222 memmove(FileStart,FileName,strlen(FileStart));
223 FileName = Line;
224 }
225
226 #if 0
227 struct stat St;
228 int Flag = FTW_F;
229 if (stat(FileName,&St) != 0)
230 Flag = FTW_NS;
231 #endif
232
233 if (ScannerFile(FileName, false) != 0)
234 break;
235 }
236
237 fclose(List);
238 return true;
239 }
240 /*}}}*/
241 // FTWScanner::Delink - Delink symlinks /*{{{*/
242 // ---------------------------------------------------------------------
243 /* */
244 bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
245 unsigned long &DeLinkBytes,
246 off_t const &FileSize)
247 {
248 // See if this isn't an internaly prefix'd file name.
249 if (InternalPrefix.empty() == false &&
250 InternalPrefix.length() < FileName.length() &&
251 stringcmp(FileName.begin(),FileName.begin() + InternalPrefix.length(),
252 InternalPrefix.begin(),InternalPrefix.end()) != 0)
253 {
254 if (DeLinkLimit != 0 && DeLinkBytes/1024 < DeLinkLimit)
255 {
256 // Tidy up the display
257 if (DeLinkBytes == 0)
258 cout << endl;
259
260 NewLine(1);
261 ioprintf(c1out, _(" DeLink %s [%s]\n"), (OriginalPath + InternalPrefix.length()),
262 SizeToStr(FileSize).c_str());
263 c1out << flush;
264
265 if (NoLinkAct == false)
266 {
267 char OldLink[400];
268 if (readlink(OriginalPath,OldLink,sizeof(OldLink)) == -1)
269 _error->Errno("readlink",_("Failed to readlink %s"),OriginalPath);
270 else
271 {
272 if (unlink(OriginalPath) != 0)
273 _error->Errno("unlink",_("Failed to unlink %s"),OriginalPath);
274 else
275 {
276 if (link(FileName.c_str(),OriginalPath) != 0)
277 {
278 // Panic! Restore the symlink
279 symlink(OldLink,OriginalPath);
280 return _error->Errno("link",_("*** Failed to link %s to %s"),
281 FileName.c_str(),
282 OriginalPath);
283 }
284 }
285 }
286 }
287
288 DeLinkBytes += FileSize;
289 if (DeLinkBytes/1024 >= DeLinkLimit)
290 ioprintf(c1out, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes).c_str());
291 }
292
293 FileName = OriginalPath;
294 }
295
296 return true;
297 }
298 /*}}}*/
299
300 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
301 // ---------------------------------------------------------------------
302 /* */
303 PackagesWriter::PackagesWriter(string const &DB,string const &Overrides,string const &ExtOverrides,
304 string const &Arch) :
305 FTWScanner(Arch), Db(DB), Stats(Db.Stats), TransWriter(NULL)
306 {
307 Output = stdout;
308 SetExts(".deb .udeb");
309 DeLinkLimit = 0;
310
311 // Process the command line options
312 DoMD5 = _config->FindB("APT::FTPArchive::MD5",true);
313 DoSHA1 = _config->FindB("APT::FTPArchive::SHA1",true);
314 DoSHA256 = _config->FindB("APT::FTPArchive::SHA256",true);
315 DoSHA256 = _config->FindB("APT::FTPArchive::SHA512",true);
316 DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
317 DoContents = _config->FindB("APT::FTPArchive::Contents",true);
318 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
319 LongDescription = _config->FindB("APT::FTPArchive::LongDescription",true);
320
321 if (Db.Loaded() == false)
322 DoContents = false;
323
324 // Read the override file
325 if (Overrides.empty() == false && Over.ReadOverride(Overrides) == false)
326 return;
327 else
328 NoOverride = true;
329
330 if (ExtOverrides.empty() == false)
331 Over.ReadExtraOverride(ExtOverrides);
332
333 _error->DumpErrors();
334 }
335 /*}}}*/
336 // FTWScanner::SetExts - Set extensions to support /*{{{*/
337 // ---------------------------------------------------------------------
338 /* */
339 bool FTWScanner::SetExts(string const &Vals)
340 {
341 ClearPatterns();
342 string::size_type Start = 0;
343 while (Start <= Vals.length()-1)
344 {
345 string::size_type const Space = Vals.find(' ',Start);
346 string::size_type const Length = ((Space == string::npos) ? Vals.length() : Space) - Start;
347 if ( Arch.empty() == false )
348 {
349 AddPattern(string("*_") + Arch + Vals.substr(Start, Length));
350 AddPattern(string("*_all") + Vals.substr(Start, Length));
351 }
352 else
353 AddPattern(string("*") + Vals.substr(Start, Length));
354
355 Start += Length + 1;
356 }
357
358 return true;
359 }
360
361 /*}}}*/
362 // PackagesWriter::DoPackage - Process a single package /*{{{*/
363 // ---------------------------------------------------------------------
364 /* This method takes a package and gets its control information and
365 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
366 rewritten and the path/size/hash appended. */
367 bool PackagesWriter::DoPackage(string FileName)
368 {
369 // Pull all the data we need form the DB
370 if (Db.GetFileInfo(FileName, true, DoContents, true, DoMD5, DoSHA1, DoSHA256, DoSHA512, DoAlwaysStat)
371 == false)
372 {
373 return false;
374 }
375
376 off_t FileSize = Db.GetFileSize();
377 if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,FileSize) == false)
378 return false;
379
380 // Lookup the overide information
381 pkgTagSection &Tags = Db.Control.Section;
382 string Package = Tags.FindS("Package");
383 string Architecture;
384 // if we generate a Packages file for a given arch, we use it to
385 // look for overrides. if we run in "simple" mode without the
386 // "Architecures" variable in the config we use the architecure value
387 // from the deb file
388 if(Arch != "")
389 Architecture = Arch;
390 else
391 Architecture = Tags.FindS("Architecture");
392 auto_ptr<Override::Item> OverItem(Over.GetItem(Package,Architecture));
393
394 if (Package.empty() == true)
395 return _error->Error(_("Archive had no package field"));
396
397 // If we need to do any rewriting of the header do it now..
398 if (OverItem.get() == 0)
399 {
400 if (NoOverride == false)
401 {
402 NewLine(1);
403 ioprintf(c1out, _(" %s has no override entry\n"), Package.c_str());
404 }
405
406 OverItem = auto_ptr<Override::Item>(new Override::Item);
407 OverItem->FieldOverride["Section"] = Tags.FindS("Section");
408 OverItem->Priority = Tags.FindS("Priority");
409 }
410
411 char Size[40];
412 sprintf(Size,"%lu", (unsigned long) FileSize);
413
414 // Strip the DirStrip prefix from the FileName and add the PathPrefix
415 string NewFileName;
416 if (DirStrip.empty() == false &&
417 FileName.length() > DirStrip.length() &&
418 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
419 DirStrip.begin(),DirStrip.end()) == 0)
420 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
421 else
422 NewFileName = FileName;
423 if (PathPrefix.empty() == false)
424 NewFileName = flCombine(PathPrefix,NewFileName);
425
426 /* Configuration says we don't want to include the long Description
427 in the package file - instead we want to ship a separated file */
428 string desc;
429 if (LongDescription == false) {
430 desc = Tags.FindS("Description").append("\n");
431 OverItem->FieldOverride["Description"] = desc.substr(0, desc.find('\n')).c_str();
432 }
433
434 // This lists all the changes to the fields we are going to make.
435 // (7 hardcoded + maintainer + suggests + end marker)
436 TFRewriteData Changes[6+2+OverItem->FieldOverride.size()+1+1];
437
438 unsigned int End = 0;
439 SetTFRewriteData(Changes[End++], "Size", Size);
440 SetTFRewriteData(Changes[End++], "MD5sum", Db.MD5Res.c_str());
441 SetTFRewriteData(Changes[End++], "SHA1", Db.SHA1Res.c_str());
442 SetTFRewriteData(Changes[End++], "SHA256", Db.SHA256Res.c_str());
443 SetTFRewriteData(Changes[End++], "SHA512", Db.SHA512Res.c_str());
444 SetTFRewriteData(Changes[End++], "Filename", NewFileName.c_str());
445 SetTFRewriteData(Changes[End++], "Priority", OverItem->Priority.c_str());
446 SetTFRewriteData(Changes[End++], "Status", 0);
447 SetTFRewriteData(Changes[End++], "Optional", 0);
448
449 string DescriptionMd5;
450 if (LongDescription == false) {
451 MD5Summation descmd5;
452 descmd5.Add(desc.c_str());
453 DescriptionMd5 = descmd5.Result().Value();
454 SetTFRewriteData(Changes[End++], "Description-md5", DescriptionMd5.c_str());
455 if (TransWriter != NULL)
456 TransWriter->DoPackage(Package, desc, DescriptionMd5);
457 }
458
459 // Rewrite the maintainer field if necessary
460 bool MaintFailed;
461 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
462 if (MaintFailed == true)
463 {
464 if (NoOverride == false)
465 {
466 NewLine(1);
467 ioprintf(c1out, _(" %s maintainer is %s not %s\n"),
468 Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
469 }
470 }
471
472 if (NewMaint.empty() == false)
473 SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
474
475 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
476 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
477 but dpkg does this append bit. So we do the append bit, at least that way the
478 status file and package file will remain similar. There are other transforms
479 but optional is the only legacy one still in use for some lazy reason. */
480 string OptionalStr = Tags.FindS("Optional");
481 if (OptionalStr.empty() == false)
482 {
483 if (Tags.FindS("Suggests").empty() == false)
484 OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
485 SetTFRewriteData(Changes[End++], "Suggests", OptionalStr.c_str());
486 }
487
488 for (map<string,string>::const_iterator I = OverItem->FieldOverride.begin();
489 I != OverItem->FieldOverride.end(); I++)
490 SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
491
492 SetTFRewriteData(Changes[End++], 0, 0);
493
494 // Rewrite and store the fields.
495 if (TFRewrite(Output,Tags,TFRewritePackageOrder,Changes) == false)
496 return false;
497 fprintf(Output,"\n");
498
499 return Db.Finish();
500 }
501 /*}}}*/
502
503 // TranslationWriter::TranslationWriter - Constructor /*{{{*/
504 // ---------------------------------------------------------------------
505 /* Create a Translation-Master file for this Packages file */
506 TranslationWriter::TranslationWriter(string const &File, string const &TransCompress,
507 mode_t const &Permissions) : Output(NULL),
508 RefCounter(0)
509 {
510 if (File.empty() == true)
511 return;
512
513 Comp = new MultiCompress(File, TransCompress, Permissions);
514 Output = Comp->Input;
515 }
516 /*}}}*/
517 // TranslationWriter::DoPackage - Process a single package /*{{{*/
518 // ---------------------------------------------------------------------
519 /* Create a Translation-Master file for this Packages file */
520 bool TranslationWriter::DoPackage(string const &Pkg, string const &Desc,
521 string const &MD5)
522 {
523 if (Output == NULL)
524 return true;
525
526 // Different archs can include different versions and therefore
527 // different descriptions - so we need to check for both name and md5.
528 string const Record = Pkg + ":" + MD5;
529
530 if (Included.find(Record) != Included.end())
531 return true;
532
533 fprintf(Output, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
534 Pkg.c_str(), MD5.c_str(), Desc.c_str());
535
536 Included.insert(Record);
537 return true;
538 }
539 /*}}}*/
540 // TranslationWriter::~TranslationWriter - Destructor /*{{{*/
541 // ---------------------------------------------------------------------
542 /* */
543 TranslationWriter::~TranslationWriter()
544 {
545 if (Comp == NULL)
546 return;
547
548 delete Comp;
549 }
550 /*}}}*/
551
552 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
553 // ---------------------------------------------------------------------
554 /* */
555 SourcesWriter::SourcesWriter(string const &BOverrides,string const &SOverrides,
556 string const &ExtOverrides)
557 {
558 Output = stdout;
559 AddPattern("*.dsc");
560 DeLinkLimit = 0;
561 Buffer = 0;
562 BufSize = 0;
563
564 // Process the command line options
565 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
566
567 // Read the override file
568 if (BOverrides.empty() == false && BOver.ReadOverride(BOverrides) == false)
569 return;
570 else
571 NoOverride = true;
572
573 // WTF?? The logic above: if we can't read binary overrides, don't even try
574 // reading source overrides. if we can read binary overrides, then say there
575 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
576
577 if (ExtOverrides.empty() == false)
578 SOver.ReadExtraOverride(ExtOverrides);
579
580 if (SOverrides.empty() == false && FileExists(SOverrides) == true)
581 SOver.ReadOverride(SOverrides,true);
582 }
583 /*}}}*/
584 // SourcesWriter::DoPackage - Process a single package /*{{{*/
585 // ---------------------------------------------------------------------
586 /* */
587 bool SourcesWriter::DoPackage(string FileName)
588 {
589 // Open the archive
590 FileFd F(FileName,FileFd::ReadOnly);
591 if (_error->PendingError() == true)
592 return false;
593
594 // Stat the file for later
595 struct stat St;
596 if (fstat(F.Fd(),&St) != 0)
597 return _error->Errno("fstat","Failed to stat %s",FileName.c_str());
598
599 if (St.st_size > 128*1024)
600 return _error->Error("DSC file '%s' is too large!",FileName.c_str());
601
602 if (BufSize < (unsigned)St.st_size+1)
603 {
604 BufSize = St.st_size+1;
605 Buffer = (char *)realloc(Buffer,St.st_size+1);
606 }
607
608 if (F.Read(Buffer,St.st_size) == false)
609 return false;
610
611 // Hash the file
612 char *Start = Buffer;
613 char *BlkEnd = Buffer + St.st_size;
614 MD5Summation MD5;
615 MD5.Add((unsigned char *)Start,BlkEnd - Start);
616
617 SHA1Summation SHA1;
618 SHA256Summation SHA256;
619 SHA512Summation SHA512;
620 SHA1.Add((unsigned char *)Start,BlkEnd - Start);
621 SHA256.Add((unsigned char *)Start,BlkEnd - Start);
622 SHA512.Add((unsigned char *)Start,BlkEnd - Start);
623
624 // Add an extra \n to the end, just in case
625 *BlkEnd++ = '\n';
626
627 /* Remove the PGP trailer. Some .dsc's have this without a blank line
628 before */
629 const char *Key = "-----BEGIN PGP SIGNATURE-----";
630 for (char *MsgEnd = Start; MsgEnd < BlkEnd - strlen(Key) -1; MsgEnd++)
631 {
632 if (*MsgEnd == '\n' && strncmp(MsgEnd+1,Key,strlen(Key)) == 0)
633 {
634 MsgEnd[1] = '\n';
635 break;
636 }
637 }
638
639 /* Read records until we locate the Source record. This neatly skips the
640 GPG header (which is RFC822 formed) without any trouble. */
641 pkgTagSection Tags;
642 do
643 {
644 unsigned Pos;
645 if (Tags.Scan(Start,BlkEnd - Start) == false)
646 return _error->Error("Could not find a record in the DSC '%s'",FileName.c_str());
647 if (Tags.Find("Source",Pos) == true)
648 break;
649 Start += Tags.size();
650 }
651 while (1);
652 Tags.Trim();
653
654 // Lookup the overide information, finding first the best priority.
655 string BestPrio;
656 string Bins = Tags.FindS("Binary");
657 char Buffer[Bins.length() + 1];
658 auto_ptr<Override::Item> OverItem(0);
659 if (Bins.empty() == false)
660 {
661 strcpy(Buffer,Bins.c_str());
662
663 // Ignore too-long errors.
664 char *BinList[400];
665 TokSplitString(',',Buffer,BinList,sizeof(BinList)/sizeof(BinList[0]));
666
667 // Look at all the binaries
668 unsigned char BestPrioV = pkgCache::State::Extra;
669 for (unsigned I = 0; BinList[I] != 0; I++)
670 {
671 auto_ptr<Override::Item> Itm(BOver.GetItem(BinList[I]));
672 if (Itm.get() == 0)
673 continue;
674
675 unsigned char NewPrioV = debListParser::GetPrio(Itm->Priority);
676 if (NewPrioV < BestPrioV || BestPrio.empty() == true)
677 {
678 BestPrioV = NewPrioV;
679 BestPrio = Itm->Priority;
680 }
681
682 if (OverItem.get() == 0)
683 OverItem = Itm;
684 }
685 }
686
687 // If we need to do any rewriting of the header do it now..
688 if (OverItem.get() == 0)
689 {
690 if (NoOverride == false)
691 {
692 NewLine(1);
693 ioprintf(c1out, _(" %s has no override entry\n"), Tags.FindS("Source").c_str());
694 }
695
696 OverItem = auto_ptr<Override::Item>(new Override::Item);
697 }
698
699 auto_ptr<Override::Item> SOverItem(SOver.GetItem(Tags.FindS("Source")));
700 // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
701 if (SOverItem.get() == 0)
702 {
703 ioprintf(c1out, _(" %s has no source override entry\n"), Tags.FindS("Source").c_str());
704 SOverItem = auto_ptr<Override::Item>(BOver.GetItem(Tags.FindS("Source")));
705 if (SOverItem.get() == 0)
706 {
707 ioprintf(c1out, _(" %s has no binary override entry either\n"), Tags.FindS("Source").c_str());
708 SOverItem = auto_ptr<Override::Item>(new Override::Item);
709 *SOverItem = *OverItem;
710 }
711 }
712
713 // Add the dsc to the files hash list
714 string const strippedName = flNotDir(FileName);
715 std::ostringstream ostreamFiles;
716 if (Tags.Exists("Files"))
717 ostreamFiles << "\n " << string(MD5.Result()) << " " << St.st_size << " "
718 << strippedName << "\n " << Tags.FindS("Files");
719 string const Files = ostreamFiles.str();
720
721 std::ostringstream ostreamSha1;
722 if (Tags.Exists("Checksums-Sha1"))
723 ostreamSha1 << "\n " << string(SHA1.Result()) << " " << St.st_size << " "
724 << strippedName << "\n " << Tags.FindS("Checksums-Sha1");
725 string const ChecksumsSha1 = ostreamSha1.str();
726
727 std::ostringstream ostreamSha256;
728 if (Tags.Exists("Checksums-Sha256"))
729 ostreamSha256 << "\n " << string(SHA256.Result()) << " " << St.st_size << " "
730 << strippedName << "\n " << Tags.FindS("Checksums-Sha256");
731 string const ChecksumsSha256 = ostreamSha256.str();
732
733 std::ostringstream ostreamSha512;
734 if (Tags.Exists("Checksums-Sha512"))
735 ostreamSha512 << "\n " << string(SHA512.Result()) << " " << St.st_size << " "
736 << strippedName << "\n " << Tags.FindS("Checksums-Sha512");
737 string const ChecksumsSha512 = ostreamSha512.str();
738
739 // Strip the DirStrip prefix from the FileName and add the PathPrefix
740 string NewFileName;
741 if (DirStrip.empty() == false &&
742 FileName.length() > DirStrip.length() &&
743 stringcmp(DirStrip,OriginalPath,OriginalPath + DirStrip.length()) == 0)
744 NewFileName = string(OriginalPath + DirStrip.length());
745 else
746 NewFileName = OriginalPath;
747 if (PathPrefix.empty() == false)
748 NewFileName = flCombine(PathPrefix,NewFileName);
749
750 string Directory = flNotFile(OriginalPath);
751 string Package = Tags.FindS("Source");
752
753 // Perform the delinking operation over all of the files
754 string ParseJnk;
755 const char *C = Files.c_str();
756 char *RealPath = NULL;
757 for (;isspace(*C); C++);
758 while (*C != 0)
759 {
760 // Parse each of the elements
761 if (ParseQuoteWord(C,ParseJnk) == false ||
762 ParseQuoteWord(C,ParseJnk) == false ||
763 ParseQuoteWord(C,ParseJnk) == false)
764 return _error->Error("Error parsing file record");
765
766 char Jnk[2];
767 string OriginalPath = Directory + ParseJnk;
768 if (readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
769 (RealPath = realpath(OriginalPath.c_str(),NULL)) != 0)
770 {
771 string RP = RealPath;
772 free(RealPath);
773 if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St.st_size) == false)
774 return false;
775 }
776 }
777
778 Directory = flNotFile(NewFileName);
779 if (Directory.length() > 2)
780 Directory.erase(Directory.end()-1);
781
782 // This lists all the changes to the fields we are going to make.
783 // (5 hardcoded + checksums + maintainer + end marker)
784 TFRewriteData Changes[5+2+1+SOverItem->FieldOverride.size()+1];
785
786 unsigned int End = 0;
787 SetTFRewriteData(Changes[End++],"Source",Package.c_str(),"Package");
788 SetTFRewriteData(Changes[End++],"Files",Files.c_str());
789 SetTFRewriteData(Changes[End++],"Checksums-Sha1",ChecksumsSha1.c_str());
790 SetTFRewriteData(Changes[End++],"Checksums-Sha256",ChecksumsSha256.c_str());
791 SetTFRewriteData(Changes[End++],"Checksums-Sha512",ChecksumsSha512.c_str());
792 if (Directory != "./")
793 SetTFRewriteData(Changes[End++],"Directory",Directory.c_str());
794 SetTFRewriteData(Changes[End++],"Priority",BestPrio.c_str());
795 SetTFRewriteData(Changes[End++],"Status",0);
796
797 // Rewrite the maintainer field if necessary
798 bool MaintFailed;
799 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
800 if (MaintFailed == true)
801 {
802 if (NoOverride == false)
803 {
804 NewLine(1);
805 ioprintf(c1out, _(" %s maintainer is %s not %s\n"), Package.c_str(),
806 Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
807 }
808 }
809 if (NewMaint.empty() == false)
810 SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
811
812 for (map<string,string>::const_iterator I = SOverItem->FieldOverride.begin();
813 I != SOverItem->FieldOverride.end(); I++)
814 SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
815
816 SetTFRewriteData(Changes[End++], 0, 0);
817
818 // Rewrite and store the fields.
819 if (TFRewrite(Output,Tags,TFRewriteSourceOrder,Changes) == false)
820 return false;
821 fprintf(Output,"\n");
822
823 Stats.Packages++;
824
825 return true;
826 }
827 /*}}}*/
828
829 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
830 // ---------------------------------------------------------------------
831 /* */
832 ContentsWriter::ContentsWriter(string const &DB, string const &Arch) :
833 FTWScanner(Arch), Db(DB), Stats(Db.Stats)
834
835 {
836 SetExts(".deb");
837 Output = stdout;
838 }
839 /*}}}*/
840 // ContentsWriter::DoPackage - Process a single package /*{{{*/
841 // ---------------------------------------------------------------------
842 /* If Package is the empty string the control record will be parsed to
843 determine what the package name is. */
844 bool ContentsWriter::DoPackage(string FileName, string Package)
845 {
846 if (!Db.GetFileInfo(FileName, Package.empty(), true, false, false, false, false, false))
847 {
848 return false;
849 }
850
851 // Parse the package name
852 if (Package.empty() == true)
853 {
854 Package = Db.Control.Section.FindS("Package");
855 }
856
857 Db.Contents.Add(Gen,Package);
858
859 return Db.Finish();
860 }
861 /*}}}*/
862 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
863 // ---------------------------------------------------------------------
864 /* */
865 bool ContentsWriter::ReadFromPkgs(string const &PkgFile,string const &PkgCompress)
866 {
867 MultiCompress Pkgs(PkgFile,PkgCompress,0,false);
868 if (_error->PendingError() == true)
869 return false;
870
871 // Open the package file
872 int CompFd = -1;
873 pid_t Proc = -1;
874 if (Pkgs.OpenOld(CompFd,Proc) == false)
875 return false;
876
877 // No auto-close FD
878 FileFd Fd(CompFd,false);
879 pkgTagFile Tags(&Fd);
880 if (_error->PendingError() == true)
881 {
882 Pkgs.CloseOld(CompFd,Proc);
883 return false;
884 }
885
886 // Parse.
887 pkgTagSection Section;
888 while (Tags.Step(Section) == true)
889 {
890 string File = flCombine(Prefix,Section.FindS("FileName"));
891 string Package = Section.FindS("Section");
892 if (Package.empty() == false && Package.end()[-1] != '/')
893 {
894 Package += '/';
895 Package += Section.FindS("Package");
896 }
897 else
898 Package += Section.FindS("Package");
899
900 DoPackage(File,Package);
901 if (_error->empty() == false)
902 {
903 _error->Error("Errors apply to file '%s'",File.c_str());
904 _error->DumpErrors();
905 }
906 }
907
908 // Tidy the compressor
909 if (Pkgs.CloseOld(CompFd,Proc) == false)
910 return false;
911
912 return true;
913 }
914
915 /*}}}*/
916
917 // ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
918 // ---------------------------------------------------------------------
919 /* */
920 ReleaseWriter::ReleaseWriter(string const &DB)
921 {
922 if (_config->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
923 {
924 AddPattern("Packages");
925 AddPattern("Packages.gz");
926 AddPattern("Packages.bz2");
927 AddPattern("Packages.lzma");
928 AddPattern("Sources");
929 AddPattern("Sources.gz");
930 AddPattern("Sources.bz2");
931 AddPattern("Sources.lzma");
932 AddPattern("Release");
933 AddPattern("Index");
934 AddPattern("md5sum.txt");
935 }
936 AddPatterns(_config->FindVector("APT::FTPArchive::Release::Patterns"));
937
938 Output = stdout;
939 time_t const now = time(NULL);
940 char datestr[128];
941 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S UTC",
942 gmtime(&now)) == 0)
943 {
944 datestr[0] = '\0';
945 }
946
947 time_t const validuntil = now + _config->FindI("APT::FTPArchive::Release::ValidTime", 0);
948 char validstr[128];
949 if (now == validuntil ||
950 strftime(validstr, sizeof(validstr), "%a, %d %b %Y %H:%M:%S UTC",
951 gmtime(&validuntil)) == 0)
952 {
953 validstr[0] = '\0';
954 }
955
956 map<string,string> Fields;
957 Fields["Origin"] = "";
958 Fields["Label"] = "";
959 Fields["Suite"] = "";
960 Fields["Version"] = "";
961 Fields["Codename"] = "";
962 Fields["Date"] = datestr;
963 Fields["Valid-Until"] = validstr;
964 Fields["Architectures"] = "";
965 Fields["Components"] = "";
966 Fields["Description"] = "";
967
968 for(map<string,string>::const_iterator I = Fields.begin();
969 I != Fields.end();
970 ++I)
971 {
972 string Config = string("APT::FTPArchive::Release::") + (*I).first;
973 string Value = _config->Find(Config, (*I).second.c_str());
974 if (Value == "")
975 continue;
976
977 fprintf(Output, "%s: %s\n", (*I).first.c_str(), Value.c_str());
978 }
979 }
980 /*}}}*/
981 // ReleaseWriter::DoPackage - Process a single package /*{{{*/
982 // ---------------------------------------------------------------------
983 bool ReleaseWriter::DoPackage(string FileName)
984 {
985 // Strip the DirStrip prefix from the FileName and add the PathPrefix
986 string NewFileName;
987 if (DirStrip.empty() == false &&
988 FileName.length() > DirStrip.length() &&
989 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
990 DirStrip.begin(),DirStrip.end()) == 0)
991 {
992 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
993 while (NewFileName[0] == '/')
994 NewFileName = string(NewFileName.begin() + 1,NewFileName.end());
995 }
996 else
997 NewFileName = FileName;
998
999 if (PathPrefix.empty() == false)
1000 NewFileName = flCombine(PathPrefix,NewFileName);
1001
1002 FileFd fd(FileName, FileFd::ReadOnly);
1003
1004 if (!fd.IsOpen())
1005 {
1006 return false;
1007 }
1008
1009 CheckSums[NewFileName].size = fd.Size();
1010
1011 MD5Summation MD5;
1012 MD5.AddFD(fd.Fd(), fd.Size());
1013 CheckSums[NewFileName].MD5 = MD5.Result();
1014
1015 fd.Seek(0);
1016 SHA1Summation SHA1;
1017 SHA1.AddFD(fd.Fd(), fd.Size());
1018 CheckSums[NewFileName].SHA1 = SHA1.Result();
1019
1020 fd.Seek(0);
1021 SHA256Summation SHA256;
1022 SHA256.AddFD(fd.Fd(), fd.Size());
1023 CheckSums[NewFileName].SHA256 = SHA256.Result();
1024
1025 SHA256Summation SHA512;
1026 SHA256.AddFD(fd.Fd(), fd.Size());
1027 CheckSums[NewFileName].SHA512 = SHA512.Result();
1028
1029 fd.Close();
1030
1031 return true;
1032 }
1033
1034 /*}}}*/
1035 // ReleaseWriter::Finish - Output the checksums /*{{{*/
1036 // ---------------------------------------------------------------------
1037 void ReleaseWriter::Finish()
1038 {
1039 fprintf(Output, "MD5Sum:\n");
1040 for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
1041 I != CheckSums.end();
1042 ++I)
1043 {
1044 fprintf(Output, " %s %16ld %s\n",
1045 (*I).second.MD5.c_str(),
1046 (*I).second.size,
1047 (*I).first.c_str());
1048 }
1049
1050 fprintf(Output, "SHA1:\n");
1051 for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
1052 I != CheckSums.end();
1053 ++I)
1054 {
1055 fprintf(Output, " %s %16ld %s\n",
1056 (*I).second.SHA1.c_str(),
1057 (*I).second.size,
1058 (*I).first.c_str());
1059 }
1060
1061 fprintf(Output, "SHA256:\n");
1062 for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
1063 I != CheckSums.end();
1064 ++I)
1065 {
1066 fprintf(Output, " %s %16ld %s\n",
1067 (*I).second.SHA256.c_str(),
1068 (*I).second.size,
1069 (*I).first.c_str());
1070 }
1071
1072 fprintf(Output, "SHA512:\n");
1073 for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
1074 I != CheckSums.end();
1075 ++I)
1076 {
1077 fprintf(Output, " %s %32ld %s\n",
1078 (*I).second.SHA512.c_str(),
1079 (*I).second.size,
1080 (*I).first.c_str());
1081 }
1082
1083 }
1084