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