* fix a crash in apt-ftparchive
[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 #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/deblistparser.h>
27
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <ctime>
31 #include <ftw.h>
32 #include <fnmatch.h>
33 #include <iostream>
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()
60 {
61 ErrorPrinted = false;
62 NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
63 RealPath = 0;
64 long PMax = pathconf(".",_PC_PATH_MAX);
65 if (PMax > 0)
66 RealPath = new char[PMax];
67 }
68 /*}}}*/
69 // FTWScanner::Scanner - FTW Scanner /*{{{*/
70 // ---------------------------------------------------------------------
71 /* This is the FTW scanner, it processes each directory element in the
72 directory tree. */
73 int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
74 {
75 if (Flag == FTW_DNR)
76 {
77 Owner->NewLine(1);
78 ioprintf(c1out, _("W: Unable to read directory %s\n"), File);
79 }
80 if (Flag == FTW_NS)
81 {
82 Owner->NewLine(1);
83 ioprintf(c1out, _("W: Unable to stat %s\n"), File);
84 }
85 if (Flag != FTW_F)
86 return 0;
87
88 const char *LastComponent = strrchr(File, '/');
89 if (LastComponent == NULL)
90 LastComponent = File;
91 else
92 LastComponent++;
93
94 vector<string>::iterator I;
95 for(I = Owner->Patterns.begin(); I != Owner->Patterns.end(); ++I)
96 {
97 if (fnmatch((*I).c_str(), LastComponent, 0) == 0)
98 break;
99 }
100 if (I == Owner->Patterns.end())
101 return 0;
102
103 /* Process it. If the file is a link then resolve it into an absolute
104 name.. This works best if the directory components the scanner are
105 given are not links themselves. */
106 char Jnk[2];
107 Owner->OriginalPath = File;
108 if (Owner->RealPath != 0 && readlink(File,Jnk,sizeof(Jnk)) != -1 &&
109 realpath(File,Owner->RealPath) != 0)
110 Owner->DoPackage(Owner->RealPath);
111 else
112 Owner->DoPackage(File);
113
114 if (_error->empty() == false)
115 {
116 // Print any errors or warnings found
117 string Err;
118 bool SeenPath = false;
119 while (_error->empty() == false)
120 {
121 Owner->NewLine(1);
122
123 bool Type = _error->PopMessage(Err);
124 if (Type == true)
125 cerr << _("E: ") << Err << endl;
126 else
127 cerr << _("W: ") << Err << endl;
128
129 if (Err.find(File) != string::npos)
130 SeenPath = true;
131 }
132
133 if (SeenPath == false)
134 cerr << _("E: Errors apply to file ") << "'" << File << "'" << endl;
135 return 0;
136 }
137
138 return 0;
139 }
140 /*}}}*/
141 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
142 // ---------------------------------------------------------------------
143 /* */
144 bool FTWScanner::RecursiveScan(string Dir)
145 {
146 /* If noprefix is set then jam the scan root in, so we don't generate
147 link followed paths out of control */
148 if (InternalPrefix.empty() == true)
149 {
150 if (realpath(Dir.c_str(),RealPath) == 0)
151 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
152 InternalPrefix = RealPath;
153 }
154
155 // Do recursive directory searching
156 Owner = this;
157 int Res = ftw(Dir.c_str(),Scanner,30);
158
159 // Error treewalking?
160 if (Res != 0)
161 {
162 if (_error->PendingError() == false)
163 _error->Errno("ftw",_("Tree walking failed"));
164 return false;
165 }
166
167 return true;
168 }
169 /*}}}*/
170 // FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
171 // ---------------------------------------------------------------------
172 /* This is an alternative to using FTW to locate files, it reads the list
173 of files from another file. */
174 bool FTWScanner::LoadFileList(string Dir,string File)
175 {
176 /* If noprefix is set then jam the scan root in, so we don't generate
177 link followed paths out of control */
178 if (InternalPrefix.empty() == true)
179 {
180 if (realpath(Dir.c_str(),RealPath) == 0)
181 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
182 InternalPrefix = RealPath;
183 }
184
185 Owner = this;
186 FILE *List = fopen(File.c_str(),"r");
187 if (List == 0)
188 return _error->Errno("fopen",_("Failed to open %s"),File.c_str());
189
190 /* We are a tad tricky here.. We prefix the buffer with the directory
191 name, that way if we need a full path with just use line.. Sneaky and
192 fully evil. */
193 char Line[1000];
194 char *FileStart;
195 if (Dir.empty() == true || Dir.end()[-1] != '/')
196 FileStart = Line + snprintf(Line,sizeof(Line),"%s/",Dir.c_str());
197 else
198 FileStart = Line + snprintf(Line,sizeof(Line),"%s",Dir.c_str());
199 while (fgets(FileStart,sizeof(Line) - (FileStart - Line),List) != 0)
200 {
201 char *FileName = _strstrip(FileStart);
202 if (FileName[0] == 0)
203 continue;
204
205 if (FileName[0] != '/')
206 {
207 if (FileName != FileStart)
208 memmove(FileStart,FileName,strlen(FileStart));
209 FileName = Line;
210 }
211
212 struct stat St;
213 int Flag = FTW_F;
214 if (stat(FileName,&St) != 0)
215 Flag = FTW_NS;
216
217 if (Scanner(FileName,&St,Flag) != 0)
218 break;
219 }
220
221 fclose(List);
222 return true;
223 }
224 /*}}}*/
225 // FTWScanner::Delink - Delink symlinks /*{{{*/
226 // ---------------------------------------------------------------------
227 /* */
228 bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
229 unsigned long &DeLinkBytes,
230 struct stat &St)
231 {
232 // See if this isn't an internaly prefix'd file name.
233 if (InternalPrefix.empty() == false &&
234 InternalPrefix.length() < FileName.length() &&
235 stringcmp(FileName.begin(),FileName.begin() + InternalPrefix.length(),
236 InternalPrefix.begin(),InternalPrefix.end()) != 0)
237 {
238 if (DeLinkLimit != 0 && DeLinkBytes/1024 < DeLinkLimit)
239 {
240 // Tidy up the display
241 if (DeLinkBytes == 0)
242 cout << endl;
243
244 NewLine(1);
245 ioprintf(c1out, _(" DeLink %s [%s]\n"), (OriginalPath + InternalPrefix.length()),
246 SizeToStr(St.st_size).c_str());
247 c1out << flush;
248
249 if (NoLinkAct == false)
250 {
251 char OldLink[400];
252 if (readlink(OriginalPath,OldLink,sizeof(OldLink)) == -1)
253 _error->Errno("readlink",_("Failed to readlink %s"),OriginalPath);
254 else
255 {
256 if (unlink(OriginalPath) != 0)
257 _error->Errno("unlink",_("Failed to unlink %s"),OriginalPath);
258 else
259 {
260 if (link(FileName.c_str(),OriginalPath) != 0)
261 {
262 // Panic! Restore the symlink
263 symlink(OldLink,OriginalPath);
264 return _error->Errno("link",_("*** Failed to link %s to %s"),
265 FileName.c_str(),
266 OriginalPath);
267 }
268 }
269 }
270 }
271
272 DeLinkBytes += St.st_size;
273 if (DeLinkBytes/1024 >= DeLinkLimit)
274 ioprintf(c1out, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes).c_str());
275 }
276
277 FileName = OriginalPath;
278 }
279
280 return true;
281 }
282 /*}}}*/
283
284 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
285 // ---------------------------------------------------------------------
286 /* */
287 PackagesWriter::PackagesWriter(string DB,string Overrides,string ExtOverrides,
288 string aArch) :
289 Db(DB),Stats(Db.Stats), Arch(aArch)
290 {
291 Output = stdout;
292 SetExts(".deb .udeb .foo .bar .baz");
293 AddPattern("*.deb");
294 DeLinkLimit = 0;
295
296 // Process the command line options
297 DoMD5 = _config->FindB("APT::FTPArchive::MD5",true);
298 DoContents = _config->FindB("APT::FTPArchive::Contents",true);
299 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
300
301 if (Db.Loaded() == false)
302 DoContents = false;
303
304 // Read the override file
305 if (Overrides.empty() == false && Over.ReadOverride(Overrides) == false)
306 return;
307 else
308 NoOverride = true;
309
310 if (ExtOverrides.empty() == false)
311 Over.ReadExtraOverride(ExtOverrides);
312
313 _error->DumpErrors();
314 }
315 /*}}}*/
316 // FTWScanner::SetExts - Set extensions to support /*{{{*/
317 // ---------------------------------------------------------------------
318 /* */
319 bool FTWScanner::SetExts(string Vals)
320 {
321 ClearPatterns();
322 string::size_type Start = 0;
323 while (Start <= Vals.length()-1)
324 {
325 string::size_type Space = Vals.find(' ',Start);
326 string::size_type Length;
327 if (Space == string::npos)
328 {
329 Length = Vals.length()-Start;
330 }
331 else
332 {
333 Length = Space-Start;
334 }
335 AddPattern(string("*") + Vals.substr(Start, Length));
336 Start += Length + 1;
337 }
338
339 return true;
340 }
341
342 /*}}}*/
343 // PackagesWriter::DoPackage - Process a single package /*{{{*/
344 // ---------------------------------------------------------------------
345 /* This method takes a package and gets its control information and
346 MD5 then writes out a control record with the proper fields rewritten
347 and the path/size/hash appended. */
348 bool PackagesWriter::DoPackage(string FileName)
349 {
350 // Open the archive
351 FileFd F(FileName,FileFd::ReadOnly);
352 if (_error->PendingError() == true)
353 return false;
354
355 // Stat the file for later
356 struct stat St;
357 if (fstat(F.Fd(),&St) != 0)
358 return _error->Errno("fstat",_("Failed to stat %s"),FileName.c_str());
359
360 // Pull all the data we need form the DB
361 string MD5Res;
362 if (Db.SetFile(FileName,St,&F) == false ||
363 Db.LoadControl() == false ||
364 (DoContents == true && Db.LoadContents(true) == false) ||
365 (DoMD5 == true && Db.GetMD5(MD5Res,false) == false))
366 return false;
367
368 if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,St) == false)
369 return false;
370
371 // Lookup the overide information
372 pkgTagSection &Tags = Db.Control.Section;
373 string Package = Tags.FindS("Package");
374 string Architecture;
375 // if we generate a Packages file for a given arch, we use it to
376 // look for overrides. if we run in "simple" mode without the
377 // "Architecures" variable in the config we use the architecure value
378 // from the deb file
379 if(Arch != "")
380 Architecture = Arch;
381 else
382 Architecture = Tags.FindS("Architecture");
383 auto_ptr<Override::Item> OverItem(Over.GetItem(Package,Architecture));
384
385 if (Package.empty() == true)
386 return _error->Error(_("Archive had no package field"));
387
388 // If we need to do any rewriting of the header do it now..
389 if (OverItem.get() == 0)
390 {
391 if (NoOverride == false)
392 {
393 NewLine(1);
394 ioprintf(c1out, _(" %s has no override entry\n"), Package.c_str());
395 }
396
397 OverItem = auto_ptr<Override::Item>(new Override::Item);
398 OverItem->FieldOverride["Section"] = Tags.FindS("Section");
399 OverItem->Priority = Tags.FindS("Priority");
400 }
401
402 char Size[40];
403 sprintf(Size,"%lu",St.st_size);
404
405 // Strip the DirStrip prefix from the FileName and add the PathPrefix
406 string NewFileName;
407 if (DirStrip.empty() == false &&
408 FileName.length() > DirStrip.length() &&
409 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
410 DirStrip.begin(),DirStrip.end()) == 0)
411 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
412 else
413 NewFileName = FileName;
414 if (PathPrefix.empty() == false)
415 NewFileName = flCombine(PathPrefix,NewFileName);
416
417 // This lists all the changes to the fields we are going to make.
418 // (7 hardcoded + maintainer + suggests + end marker)
419 TFRewriteData Changes[6+2+OverItem->FieldOverride.size()+1];
420
421 unsigned int End = 0;
422 SetTFRewriteData(Changes[End++], "Size", Size);
423 SetTFRewriteData(Changes[End++], "MD5sum", MD5Res.c_str());
424 SetTFRewriteData(Changes[End++], "Filename", NewFileName.c_str());
425 SetTFRewriteData(Changes[End++], "Priority", OverItem->Priority.c_str());
426 SetTFRewriteData(Changes[End++], "Status", 0);
427 SetTFRewriteData(Changes[End++], "Optional", 0);
428
429 // Rewrite the maintainer field if necessary
430 bool MaintFailed;
431 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
432 if (MaintFailed == true)
433 {
434 if (NoOverride == false)
435 {
436 NewLine(1);
437 ioprintf(c1out, _(" %s maintainer is %s not %s\n"),
438 Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
439 }
440 }
441
442 if (NewMaint.empty() == false)
443 SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
444
445 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
446 dpkg-scanpackages does.. Well sort of. dpkg-scanpackages just does renaming
447 but dpkg does this append bit. So we do the append bit, at least that way the
448 status file and package file will remain similar. There are other transforms
449 but optional is the only legacy one still in use for some lazy reason. */
450 string OptionalStr = Tags.FindS("Optional");
451 if (OptionalStr.empty() == false)
452 {
453 if (Tags.FindS("Suggests").empty() == false)
454 OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
455 SetTFRewriteData(Changes[End++], "Suggests", OptionalStr.c_str());
456 }
457
458 for (map<string,string>::iterator I = OverItem->FieldOverride.begin();
459 I != OverItem->FieldOverride.end(); I++)
460 SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
461
462 SetTFRewriteData(Changes[End++], 0, 0);
463
464 // Rewrite and store the fields.
465 if (TFRewrite(Output,Tags,TFRewritePackageOrder,Changes) == false)
466 return false;
467 fprintf(Output,"\n");
468
469 return Db.Finish();
470 }
471 /*}}}*/
472
473 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
474 // ---------------------------------------------------------------------
475 /* */
476 SourcesWriter::SourcesWriter(string BOverrides,string SOverrides,
477 string ExtOverrides)
478 {
479 Output = stdout;
480 AddPattern("*.dsc");
481 DeLinkLimit = 0;
482 Buffer = 0;
483 BufSize = 0;
484
485 // Process the command line options
486 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
487
488 // Read the override file
489 if (BOverrides.empty() == false && BOver.ReadOverride(BOverrides) == false)
490 return;
491 else
492 NoOverride = true;
493
494 if (ExtOverrides.empty() == false)
495 SOver.ReadExtraOverride(ExtOverrides);
496
497 if (SOverrides.empty() == false && FileExists(SOverrides) == true)
498 SOver.ReadOverride(SOverrides,true);
499 }
500 /*}}}*/
501 // SourcesWriter::DoPackage - Process a single package /*{{{*/
502 // ---------------------------------------------------------------------
503 /* */
504 bool SourcesWriter::DoPackage(string FileName)
505 {
506 // Open the archive
507 FileFd F(FileName,FileFd::ReadOnly);
508 if (_error->PendingError() == true)
509 return false;
510
511 // Stat the file for later
512 struct stat St;
513 if (fstat(F.Fd(),&St) != 0)
514 return _error->Errno("fstat","Failed to stat %s",FileName.c_str());
515
516 if (St.st_size > 128*1024)
517 return _error->Error("DSC file '%s' is too large!",FileName.c_str());
518
519 if (BufSize < (unsigned)St.st_size+1)
520 {
521 BufSize = St.st_size+1;
522 Buffer = (char *)realloc(Buffer,St.st_size+1);
523 }
524
525 if (F.Read(Buffer,St.st_size) == false)
526 return false;
527
528 // Hash the file
529 char *Start = Buffer;
530 char *BlkEnd = Buffer + St.st_size;
531 MD5Summation MD5;
532 MD5.Add((unsigned char *)Start,BlkEnd - Start);
533
534 // Add an extra \n to the end, just in case
535 *BlkEnd++ = '\n';
536
537 /* Remove the PGP trailer. Some .dsc's have this without a blank line
538 before */
539 const char *Key = "-----BEGIN PGP SIGNATURE-----";
540 for (char *MsgEnd = Start; MsgEnd < BlkEnd - strlen(Key) -1; MsgEnd++)
541 {
542 if (*MsgEnd == '\n' && strncmp(MsgEnd+1,Key,strlen(Key)) == 0)
543 {
544 MsgEnd[1] = '\n';
545 break;
546 }
547 }
548
549 /* Read records until we locate the Source record. This neatly skips the
550 GPG header (which is RFC822 formed) without any trouble. */
551 pkgTagSection Tags;
552 do
553 {
554 unsigned Pos;
555 if (Tags.Scan(Start,BlkEnd - Start) == false)
556 return _error->Error("Could not find a record in the DSC '%s'",FileName.c_str());
557 if (Tags.Find("Source",Pos) == true)
558 break;
559 Start += Tags.size();
560 }
561 while (1);
562 Tags.Trim();
563
564 // Lookup the overide information, finding first the best priority.
565 string BestPrio;
566 string Bins = Tags.FindS("Binary");
567 char Buffer[Bins.length() + 1];
568 auto_ptr<Override::Item> OverItem(0);
569 if (Bins.empty() == false)
570 {
571 strcpy(Buffer,Bins.c_str());
572
573 // Ignore too-long errors.
574 char *BinList[400];
575 TokSplitString(',',Buffer,BinList,sizeof(BinList)/sizeof(BinList[0]));
576
577 // Look at all the binaries
578 unsigned char BestPrioV = pkgCache::State::Extra;
579 for (unsigned I = 0; BinList[I] != 0; I++)
580 {
581 auto_ptr<Override::Item> Itm(BOver.GetItem(BinList[I]));
582 if (Itm.get() == 0)
583 continue;
584
585 unsigned char NewPrioV = debListParser::GetPrio(Itm->Priority);
586 if (NewPrioV < BestPrioV || BestPrio.empty() == true)
587 {
588 BestPrioV = NewPrioV;
589 BestPrio = Itm->Priority;
590 }
591
592 if (OverItem.get() == 0)
593 OverItem = Itm;
594 }
595 }
596
597 // If we need to do any rewriting of the header do it now..
598 if (OverItem.get() == 0)
599 {
600 if (NoOverride == false)
601 {
602 NewLine(1);
603 ioprintf(c1out, _(" %s has no override entry\n"), Tags.FindS("Source").c_str());
604 }
605
606 OverItem = auto_ptr<Override::Item>(new Override::Item);
607 }
608
609 auto_ptr<Override::Item> SOverItem(SOver.GetItem(Tags.FindS("Source")));
610 const auto_ptr<Override::Item> autoSOverItem(SOverItem);
611 if (SOverItem.get() == 0)
612 {
613 SOverItem = auto_ptr<Override::Item>(BOver.GetItem(Tags.FindS("Source")));
614 if (SOverItem.get() == 0)
615 {
616 SOverItem = auto_ptr<Override::Item>(new Override::Item);
617 *SOverItem = *OverItem;
618 }
619 }
620
621 // Add the dsc to the files hash list
622 char Files[1000];
623 snprintf(Files,sizeof(Files),"\n %s %lu %s\n %s",
624 string(MD5.Result()).c_str(),St.st_size,
625 flNotDir(FileName).c_str(),
626 Tags.FindS("Files").c_str());
627
628 // Strip the DirStrip prefix from the FileName and add the PathPrefix
629 string NewFileName;
630 if (DirStrip.empty() == false &&
631 FileName.length() > DirStrip.length() &&
632 stringcmp(DirStrip,OriginalPath,OriginalPath + DirStrip.length()) == 0)
633 NewFileName = string(OriginalPath + DirStrip.length());
634 else
635 NewFileName = OriginalPath;
636 if (PathPrefix.empty() == false)
637 NewFileName = flCombine(PathPrefix,NewFileName);
638
639 string Directory = flNotFile(OriginalPath);
640 string Package = Tags.FindS("Source");
641
642 // Perform the delinking operation over all of the files
643 string ParseJnk;
644 const char *C = Files;
645 for (;isspace(*C); C++);
646 while (*C != 0)
647 {
648 // Parse each of the elements
649 if (ParseQuoteWord(C,ParseJnk) == false ||
650 ParseQuoteWord(C,ParseJnk) == false ||
651 ParseQuoteWord(C,ParseJnk) == false)
652 return _error->Error("Error parsing file record");
653
654 char Jnk[2];
655 string OriginalPath = Directory + ParseJnk;
656 if (RealPath != 0 && readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
657 realpath(OriginalPath.c_str(),RealPath) != 0)
658 {
659 string RP = RealPath;
660 if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St) == false)
661 return false;
662 }
663 }
664
665 Directory = flNotFile(NewFileName);
666 if (Directory.length() > 2)
667 Directory.erase(Directory.end()-1);
668
669 // This lists all the changes to the fields we are going to make.
670 // (5 hardcoded + maintainer + end marker)
671 TFRewriteData Changes[5+1+SOverItem->FieldOverride.size()+1];
672
673 unsigned int End = 0;
674 SetTFRewriteData(Changes[End++],"Source",Package.c_str(),"Package");
675 SetTFRewriteData(Changes[End++],"Files",Files);
676 if (Directory != "./")
677 SetTFRewriteData(Changes[End++],"Directory",Directory.c_str());
678 SetTFRewriteData(Changes[End++],"Priority",BestPrio.c_str());
679 SetTFRewriteData(Changes[End++],"Status",0);
680
681 // Rewrite the maintainer field if necessary
682 bool MaintFailed;
683 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
684 if (MaintFailed == true)
685 {
686 if (NoOverride == false)
687 {
688 NewLine(1);
689 ioprintf(c1out, _(" %s maintainer is %s not %s\n"), Package.c_str(),
690 Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
691 }
692 }
693 if (NewMaint.empty() == false)
694 SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
695
696 for (map<string,string>::iterator I = SOverItem->FieldOverride.begin();
697 I != SOverItem->FieldOverride.end(); I++)
698 SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
699
700 SetTFRewriteData(Changes[End++], 0, 0);
701
702 // Rewrite and store the fields.
703 if (TFRewrite(Output,Tags,TFRewriteSourceOrder,Changes) == false)
704 return false;
705 fprintf(Output,"\n");
706
707 Stats.Packages++;
708
709 return true;
710 }
711 /*}}}*/
712
713 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
714 // ---------------------------------------------------------------------
715 /* */
716 ContentsWriter::ContentsWriter(string DB) :
717 Db(DB), Stats(Db.Stats)
718
719 {
720 AddPattern("*.deb");
721 Output = stdout;
722 }
723 /*}}}*/
724 // ContentsWriter::DoPackage - Process a single package /*{{{*/
725 // ---------------------------------------------------------------------
726 /* If Package is the empty string the control record will be parsed to
727 determine what the package name is. */
728 bool ContentsWriter::DoPackage(string FileName,string Package)
729 {
730 // Open the archive
731 FileFd F(FileName,FileFd::ReadOnly);
732 if (_error->PendingError() == true)
733 return false;
734
735 // Stat the file for later
736 struct stat St;
737 if (fstat(F.Fd(),&St) != 0)
738 return _error->Errno("fstat","Failed too stat %s",FileName.c_str());
739
740 // Ready the DB
741 if (Db.SetFile(FileName,St,&F) == false ||
742 Db.LoadContents(false) == false)
743 return false;
744
745 // Parse the package name
746 if (Package.empty() == true)
747 {
748 if (Db.LoadControl() == false)
749 return false;
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.Close();
900
901 return true;
902 }
903
904 /*}}}*/
905 // ReleaseWriter::Finish - Output the checksums /*{{{*/
906 // ---------------------------------------------------------------------
907 void ReleaseWriter::Finish()
908 {
909 fprintf(Output, "MD5Sum:\n");
910 for(map<string,struct CheckSum>::iterator I = CheckSums.begin();
911 I != CheckSums.end();
912 ++I)
913 {
914 fprintf(Output, " %s %16ld %s\n",
915 (*I).second.MD5.c_str(),
916 (*I).second.size,
917 (*I).first.c_str());
918 }
919
920 fprintf(Output, "SHA1:\n");
921 for(map<string,struct CheckSum>::iterator I = CheckSums.begin();
922 I != CheckSums.end();
923 ++I)
924 {
925 fprintf(Output, " %s %16ld %s\n",
926 (*I).second.SHA1.c_str(),
927 (*I).second.size,
928 (*I).first.c_str());
929 }
930 }
931