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