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