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