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