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