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