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