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