]> git.saurik.com Git - apt.git/blame - ftparchive/writer.cc
merged from the mvo branch
[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/* */
57FTWScanner::FTWScanner()
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/* */
88int FTWScanner::ScannerFile(const char *File, bool ReadLink)
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
98 vector<string>::iterator I;
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
131 bool Type = _error->PopMessage(Err);
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/* */
152bool FTWScanner::RecursiveScan(string Dir)
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;
cde41ae8 167 int 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. */
184bool FTWScanner::LoadFileList(string Dir,string File)
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,
cde41ae8 244 off_t 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/* */
0b41e0e7
MV
301PackagesWriter::PackagesWriter(string DB,string Overrides,string ExtOverrides,
302 string aArch) :
303 Db(DB),Stats(Db.Stats), Arch(aArch)
b2e465d6
AL
304{
305 Output = stdout;
d6689735 306 SetExts(".deb .udeb .foo .bar .baz");
98953965 307 AddPattern("*.deb");
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);
b2e465d6
AL
314 DoContents = _config->FindB("APT::FTPArchive::Contents",true);
315 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
9d20d8c8 316 LongDescription = _config->FindB("APT::FTPArchive::LongDescription",true);
b2e465d6
AL
317
318 if (Db.Loaded() == false)
319 DoContents = false;
64177f17 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/* */
336bool FTWScanner::SetExts(string Vals)
337{
338 ClearPatterns();
339 string::size_type Start = 0;
d6689735 340 while (Start <= Vals.length()-1)
98953965 341 {
d6689735
AL
342 string::size_type Space = Vals.find(' ',Start);
343 string::size_type Length;
344 if (Space == string::npos)
98953965 345 {
d6689735 346 Length = Vals.length()-Start;
98953965 347 }
d6689735
AL
348 else
349 {
350 Length = Space-Start;
351 }
352 AddPattern(string("*") + Vals.substr(Start, Length));
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
cde41ae8
MV
368 if (Db.GetFileInfo(FileName, true, DoContents, true, DoMD5, DoSHA1, DoSHA256)
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);
9d20d8c8
MV
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)
9d20d8c8 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
9d20d8c8
MV
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());
452 }
453
b2e465d6
AL
454 // Rewrite the maintainer field if necessary
455 bool MaintFailed;
456 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
457 if (MaintFailed == true)
458 {
459 if (NoOverride == false)
460 {
461 NewLine(1);
dc738e7a
AL
462 ioprintf(c1out, _(" %s maintainer is %s not %s\n"),
463 Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
b2e465d6
AL
464 }
465 }
466
467 if (NewMaint.empty() == false)
64177f17 468 SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
b2e465d6
AL
469
470 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
ca4907db 471 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
b2e465d6
AL
472 but dpkg does this append bit. So we do the append bit, at least that way the
473 status file and package file will remain similar. There are other transforms
474 but optional is the only legacy one still in use for some lazy reason. */
475 string OptionalStr = Tags.FindS("Optional");
476 if (OptionalStr.empty() == false)
477 {
478 if (Tags.FindS("Suggests").empty() == false)
479 OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
64177f17 480 SetTFRewriteData(Changes[End++], "Suggests", OptionalStr.c_str());
b2e465d6 481 }
64177f17
AL
482
483 for (map<string,string>::iterator I = OverItem->FieldOverride.begin();
484 I != OverItem->FieldOverride.end(); I++)
485 SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
486
487 SetTFRewriteData(Changes[End++], 0, 0);
488
b2e465d6
AL
489 // Rewrite and store the fields.
490 if (TFRewrite(Output,Tags,TFRewritePackageOrder,Changes) == false)
491 return false;
492 fprintf(Output,"\n");
493
494 return Db.Finish();
495}
496 /*}}}*/
497
498// SourcesWriter::SourcesWriter - Constructor /*{{{*/
499// ---------------------------------------------------------------------
500/* */
64177f17
AL
501SourcesWriter::SourcesWriter(string BOverrides,string SOverrides,
502 string ExtOverrides)
b2e465d6
AL
503{
504 Output = stdout;
98953965 505 AddPattern("*.dsc");
b2e465d6
AL
506 DeLinkLimit = 0;
507 Buffer = 0;
508 BufSize = 0;
509
510 // Process the command line options
511 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
512
513 // Read the override file
514 if (BOverrides.empty() == false && BOver.ReadOverride(BOverrides) == false)
515 return;
516 else
517 NoOverride = true;
64177f17 518
cde41ae8
MV
519 // WTF?? The logic above: if we can't read binary overrides, don't even try
520 // reading source overrides. if we can read binary overrides, then say there
521 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
522
64177f17
AL
523 if (ExtOverrides.empty() == false)
524 SOver.ReadExtraOverride(ExtOverrides);
b2e465d6 525
64177f17
AL
526 if (SOverrides.empty() == false && FileExists(SOverrides) == true)
527 SOver.ReadOverride(SOverrides,true);
b2e465d6
AL
528}
529 /*}}}*/
530// SourcesWriter::DoPackage - Process a single package /*{{{*/
531// ---------------------------------------------------------------------
532/* */
533bool SourcesWriter::DoPackage(string FileName)
534{
535 // Open the archive
536 FileFd F(FileName,FileFd::ReadOnly);
537 if (_error->PendingError() == true)
538 return false;
539
540 // Stat the file for later
541 struct stat St;
542 if (fstat(F.Fd(),&St) != 0)
543 return _error->Errno("fstat","Failed to stat %s",FileName.c_str());
544
545 if (St.st_size > 128*1024)
546 return _error->Error("DSC file '%s' is too large!",FileName.c_str());
547
548 if (BufSize < (unsigned)St.st_size+1)
549 {
550 BufSize = St.st_size+1;
551 Buffer = (char *)realloc(Buffer,St.st_size+1);
552 }
553
554 if (F.Read(Buffer,St.st_size) == false)
555 return false;
556
557 // Hash the file
558 char *Start = Buffer;
559 char *BlkEnd = Buffer + St.st_size;
560 MD5Summation MD5;
561 MD5.Add((unsigned char *)Start,BlkEnd - Start);
2893f7b5
MV
562
563 SHA1Summation SHA1;
564 SHA256Summation SHA256;
565 SHA1.Add((unsigned char *)Start,BlkEnd - Start);
566 SHA256.Add((unsigned char *)Start,BlkEnd - Start);
567
b2e465d6
AL
568 // Add an extra \n to the end, just in case
569 *BlkEnd++ = '\n';
570
571 /* Remove the PGP trailer. Some .dsc's have this without a blank line
572 before */
573 const char *Key = "-----BEGIN PGP SIGNATURE-----";
574 for (char *MsgEnd = Start; MsgEnd < BlkEnd - strlen(Key) -1; MsgEnd++)
575 {
576 if (*MsgEnd == '\n' && strncmp(MsgEnd+1,Key,strlen(Key)) == 0)
577 {
578 MsgEnd[1] = '\n';
579 break;
580 }
581 }
582
583 /* Read records until we locate the Source record. This neatly skips the
584 GPG header (which is RFC822 formed) without any trouble. */
585 pkgTagSection Tags;
586 do
587 {
588 unsigned Pos;
589 if (Tags.Scan(Start,BlkEnd - Start) == false)
590 return _error->Error("Could not find a record in the DSC '%s'",FileName.c_str());
591 if (Tags.Find("Source",Pos) == true)
592 break;
593 Start += Tags.size();
594 }
595 while (1);
596 Tags.Trim();
597
598 // Lookup the overide information, finding first the best priority.
599 string BestPrio;
b2e465d6 600 string Bins = Tags.FindS("Binary");
5200ec6f 601 char Buffer[Bins.length() + 1];
0b41e0e7 602 auto_ptr<Override::Item> OverItem(0);
5200ec6f 603 if (Bins.empty() == false)
b2e465d6
AL
604 {
605 strcpy(Buffer,Bins.c_str());
606
607 // Ignore too-long errors.
608 char *BinList[400];
609 TokSplitString(',',Buffer,BinList,sizeof(BinList)/sizeof(BinList[0]));
610
611 // Look at all the binaries
612 unsigned char BestPrioV = pkgCache::State::Extra;
613 for (unsigned I = 0; BinList[I] != 0; I++)
614 {
0b41e0e7
MV
615 auto_ptr<Override::Item> Itm(BOver.GetItem(BinList[I]));
616 if (Itm.get() == 0)
b2e465d6 617 continue;
b2e465d6
AL
618
619 unsigned char NewPrioV = debListParser::GetPrio(Itm->Priority);
620 if (NewPrioV < BestPrioV || BestPrio.empty() == true)
621 {
622 BestPrioV = NewPrioV;
623 BestPrio = Itm->Priority;
624 }
7524e348
MV
625
626 if (OverItem.get() == 0)
627 OverItem = Itm;
b2e465d6
AL
628 }
629 }
630
631 // If we need to do any rewriting of the header do it now..
0b41e0e7 632 if (OverItem.get() == 0)
b2e465d6
AL
633 {
634 if (NoOverride == false)
635 {
636 NewLine(1);
dc738e7a 637 ioprintf(c1out, _(" %s has no override entry\n"), Tags.FindS("Source").c_str());
b2e465d6
AL
638 }
639
0b41e0e7 640 OverItem = auto_ptr<Override::Item>(new Override::Item);
b2e465d6
AL
641 }
642
0b41e0e7 643 auto_ptr<Override::Item> SOverItem(SOver.GetItem(Tags.FindS("Source")));
cde41ae8 644 // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
0b41e0e7 645 if (SOverItem.get() == 0)
b2e465d6 646 {
cde41ae8 647 ioprintf(c1out, _(" %s has no source override entry\n"), Tags.FindS("Source").c_str());
0b41e0e7
MV
648 SOverItem = auto_ptr<Override::Item>(BOver.GetItem(Tags.FindS("Source")));
649 if (SOverItem.get() == 0)
650 {
cde41ae8 651 ioprintf(c1out, _(" %s has no binary override entry either\n"), Tags.FindS("Source").c_str());
0b41e0e7
MV
652 SOverItem = auto_ptr<Override::Item>(new Override::Item);
653 *SOverItem = *OverItem;
654 }
b2e465d6
AL
655 }
656
657 // Add the dsc to the files hash list
2893f7b5 658 string const strippedName = flNotDir(FileName);
b2e465d6
AL
659 char Files[1000];
660 snprintf(Files,sizeof(Files),"\n %s %lu %s\n %s",
661 string(MD5.Result()).c_str(),St.st_size,
2893f7b5 662 strippedName.c_str(),
b2e465d6 663 Tags.FindS("Files").c_str());
2893f7b5
MV
664
665 char ChecksumsSha1[1000];
666 snprintf(ChecksumsSha1,sizeof(ChecksumsSha1),"\n %s %lu %s\n %s",
667 string(SHA1.Result()).c_str(),St.st_size,
668 strippedName.c_str(),
669 Tags.FindS("Checksums-Sha1").c_str());
670
671 char ChecksumsSha256[1000];
672 snprintf(ChecksumsSha256,sizeof(ChecksumsSha256),"\n %s %lu %s\n %s",
673 string(SHA256.Result()).c_str(),St.st_size,
674 strippedName.c_str(),
675 Tags.FindS("Checksums-Sha256").c_str());
676
b2e465d6
AL
677 // Strip the DirStrip prefix from the FileName and add the PathPrefix
678 string NewFileName;
679 if (DirStrip.empty() == false &&
680 FileName.length() > DirStrip.length() &&
8c58f506 681 stringcmp(DirStrip,OriginalPath,OriginalPath + DirStrip.length()) == 0)
b2e465d6
AL
682 NewFileName = string(OriginalPath + DirStrip.length());
683 else
684 NewFileName = OriginalPath;
685 if (PathPrefix.empty() == false)
686 NewFileName = flCombine(PathPrefix,NewFileName);
171c45bc 687
b2e465d6
AL
688 string Directory = flNotFile(OriginalPath);
689 string Package = Tags.FindS("Source");
171c45bc 690
b2e465d6
AL
691 // Perform the delinking operation over all of the files
692 string ParseJnk;
693 const char *C = Files;
ab3846c0 694 char *RealPath = NULL;
b2e465d6
AL
695 for (;isspace(*C); C++);
696 while (*C != 0)
697 {
698 // Parse each of the elements
699 if (ParseQuoteWord(C,ParseJnk) == false ||
700 ParseQuoteWord(C,ParseJnk) == false ||
701 ParseQuoteWord(C,ParseJnk) == false)
702 return _error->Error("Error parsing file record");
703
704 char Jnk[2];
705 string OriginalPath = Directory + ParseJnk;
ab3846c0
MV
706 if (readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
707 (RealPath = realpath(OriginalPath.c_str(),NULL)) != 0)
b2e465d6
AL
708 {
709 string RP = RealPath;
ab3846c0 710 free(RealPath);
cde41ae8 711 if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St.st_size) == false)
b2e465d6
AL
712 return false;
713 }
714 }
715
716 Directory = flNotFile(NewFileName);
717 if (Directory.length() > 2)
718 Directory.erase(Directory.end()-1);
171c45bc 719
b2e465d6 720 // This lists all the changes to the fields we are going to make.
2893f7b5
MV
721 // (5 hardcoded + checksums + maintainer + end marker)
722 TFRewriteData Changes[5+2+1+SOverItem->FieldOverride.size()+1];
64177f17 723
b2e465d6 724 unsigned int End = 0;
64177f17
AL
725 SetTFRewriteData(Changes[End++],"Source",Package.c_str(),"Package");
726 SetTFRewriteData(Changes[End++],"Files",Files);
2893f7b5
MV
727 SetTFRewriteData(Changes[End++],"Checksums-Sha1",ChecksumsSha1);
728 SetTFRewriteData(Changes[End++],"Checksums-Sha256",ChecksumsSha256);
171c45bc
AL
729 if (Directory != "./")
730 SetTFRewriteData(Changes[End++],"Directory",Directory.c_str());
64177f17
AL
731 SetTFRewriteData(Changes[End++],"Priority",BestPrio.c_str());
732 SetTFRewriteData(Changes[End++],"Status",0);
b2e465d6
AL
733
734 // Rewrite the maintainer field if necessary
735 bool MaintFailed;
736 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
737 if (MaintFailed == true)
738 {
739 if (NoOverride == false)
740 {
741 NewLine(1);
dc738e7a
AL
742 ioprintf(c1out, _(" %s maintainer is %s not %s\n"), Package.c_str(),
743 Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
b2e465d6
AL
744 }
745 }
746 if (NewMaint.empty() == false)
64177f17
AL
747 SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
748
749 for (map<string,string>::iterator I = SOverItem->FieldOverride.begin();
750 I != SOverItem->FieldOverride.end(); I++)
751 SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
752
753 SetTFRewriteData(Changes[End++], 0, 0);
b2e465d6
AL
754
755 // Rewrite and store the fields.
756 if (TFRewrite(Output,Tags,TFRewriteSourceOrder,Changes) == false)
757 return false;
758 fprintf(Output,"\n");
759
760 Stats.Packages++;
761
762 return true;
763}
764 /*}}}*/
765
766// ContentsWriter::ContentsWriter - Constructor /*{{{*/
767// ---------------------------------------------------------------------
768/* */
769ContentsWriter::ContentsWriter(string DB) :
770 Db(DB), Stats(Db.Stats)
771
772{
98953965 773 AddPattern("*.deb");
b2e465d6
AL
774 Output = stdout;
775}
776 /*}}}*/
777// ContentsWriter::DoPackage - Process a single package /*{{{*/
778// ---------------------------------------------------------------------
779/* If Package is the empty string the control record will be parsed to
780 determine what the package name is. */
781bool ContentsWriter::DoPackage(string FileName,string Package)
782{
cde41ae8
MV
783 if (!Db.GetFileInfo(FileName, Package.empty(), true, false, false, false, false))
784 {
b2e465d6 785 return false;
cde41ae8 786 }
b2e465d6
AL
787
788 // Parse the package name
789 if (Package.empty() == true)
790 {
b2e465d6
AL
791 Package = Db.Control.Section.FindS("Package");
792 }
793
794 Db.Contents.Add(Gen,Package);
795
796 return Db.Finish();
797}
798 /*}}}*/
799// ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
800// ---------------------------------------------------------------------
801/* */
802bool ContentsWriter::ReadFromPkgs(string PkgFile,string PkgCompress)
803{
804 MultiCompress Pkgs(PkgFile,PkgCompress,0,false);
805 if (_error->PendingError() == true)
806 return false;
807
808 // Open the package file
809 int CompFd = -1;
b3d44315 810 pid_t Proc = -1;
b2e465d6
AL
811 if (Pkgs.OpenOld(CompFd,Proc) == false)
812 return false;
813
814 // No auto-close FD
815 FileFd Fd(CompFd,false);
816 pkgTagFile Tags(&Fd);
817 if (_error->PendingError() == true)
818 {
819 Pkgs.CloseOld(CompFd,Proc);
820 return false;
821 }
822
823 // Parse.
824 pkgTagSection Section;
825 while (Tags.Step(Section) == true)
826 {
827 string File = flCombine(Prefix,Section.FindS("FileName"));
828 string Package = Section.FindS("Section");
829 if (Package.empty() == false && Package.end()[-1] != '/')
830 {
831 Package += '/';
832 Package += Section.FindS("Package");
833 }
834 else
835 Package += Section.FindS("Package");
836
837 DoPackage(File,Package);
838 if (_error->empty() == false)
839 {
840 _error->Error("Errors apply to file '%s'",File.c_str());
841 _error->DumpErrors();
842 }
843 }
844
845 // Tidy the compressor
846 if (Pkgs.CloseOld(CompFd,Proc) == false)
847 return false;
848
849 return true;
850}
98953965 851
b2e465d6 852 /*}}}*/
98953965
AL
853
854// ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
855// ---------------------------------------------------------------------
856/* */
857ReleaseWriter::ReleaseWriter(string DB)
858{
859 AddPattern("Packages");
860 AddPattern("Packages.gz");
187492a6 861 AddPattern("Packages.bz2");
e85b4cd5 862 AddPattern("Packages.lzma");
187492a6
AL
863 AddPattern("Sources");
864 AddPattern("Sources.gz");
865 AddPattern("Sources.bz2");
e85b4cd5 866 AddPattern("Sources.lzma");
187492a6
AL
867 AddPattern("Release");
868 AddPattern("md5sum.txt");
869
98953965
AL
870 Output = stdout;
871 time_t now = time(NULL);
872 char datestr[128];
873 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S UTC",
874 gmtime(&now)) == 0)
875 {
876 datestr[0] = '\0';
877 }
878
879 map<string,string> Fields;
880 Fields["Origin"] = "";
881 Fields["Label"] = "";
882 Fields["Suite"] = "";
883 Fields["Version"] = "";
884 Fields["Codename"] = "";
885 Fields["Date"] = datestr;
886 Fields["Architectures"] = "";
887 Fields["Components"] = "";
888 Fields["Description"] = "";
889
890 for(map<string,string>::const_iterator I = Fields.begin();
891 I != Fields.end();
892 ++I)
893 {
894 string Config = string("APT::FTPArchive::Release::") + (*I).first;
895 string Value = _config->Find(Config, (*I).second.c_str());
896 if (Value == "")
897 continue;
898
899 fprintf(Output, "%s: %s\n", (*I).first.c_str(), Value.c_str());
900 }
98953965
AL
901}
902 /*}}}*/
903// ReleaseWriter::DoPackage - Process a single package /*{{{*/
904// ---------------------------------------------------------------------
905bool ReleaseWriter::DoPackage(string FileName)
906{
907 // Strip the DirStrip prefix from the FileName and add the PathPrefix
908 string NewFileName;
909 if (DirStrip.empty() == false &&
910 FileName.length() > DirStrip.length() &&
911 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
912 DirStrip.begin(),DirStrip.end()) == 0)
c0eb6bc6 913 {
98953965 914 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
c0eb6bc6 915 while (NewFileName[0] == '/')
9202409d 916 NewFileName = string(NewFileName.begin() + 1,NewFileName.end());
c0eb6bc6 917 }
98953965
AL
918 else
919 NewFileName = FileName;
c0eb6bc6 920
98953965
AL
921 if (PathPrefix.empty() == false)
922 NewFileName = flCombine(PathPrefix,NewFileName);
923
924 FileFd fd(FileName, FileFd::ReadOnly);
925
926 if (!fd.IsOpen())
927 {
928 return false;
929 }
930
c0eb6bc6 931 CheckSums[NewFileName].size = fd.Size();
f7291f62 932
98953965
AL
933 MD5Summation MD5;
934 MD5.AddFD(fd.Fd(), fd.Size());
c0eb6bc6 935 CheckSums[NewFileName].MD5 = MD5.Result();
98953965 936
f7291f62
AL
937 fd.Seek(0);
938 SHA1Summation SHA1;
939 SHA1.AddFD(fd.Fd(), fd.Size());
c0eb6bc6 940 CheckSums[NewFileName].SHA1 = SHA1.Result();
98953965 941
cde41ae8
MV
942 fd.Seek(0);
943 SHA256Summation SHA256;
944 SHA256.AddFD(fd.Fd(), fd.Size());
945 CheckSums[NewFileName].SHA256 = SHA256.Result();
946
98953965
AL
947 fd.Close();
948
949 return true;
950}
f7291f62
AL
951
952 /*}}}*/
953// ReleaseWriter::Finish - Output the checksums /*{{{*/
954// ---------------------------------------------------------------------
955void ReleaseWriter::Finish()
956{
957 fprintf(Output, "MD5Sum:\n");
958 for(map<string,struct CheckSum>::iterator I = CheckSums.begin();
959 I != CheckSums.end();
960 ++I)
961 {
962 fprintf(Output, " %s %16ld %s\n",
963 (*I).second.MD5.c_str(),
964 (*I).second.size,
965 (*I).first.c_str());
966 }
967
968 fprintf(Output, "SHA1:\n");
969 for(map<string,struct CheckSum>::iterator I = CheckSums.begin();
970 I != CheckSums.end();
971 ++I)
972 {
973 fprintf(Output, " %s %16ld %s\n",
974 (*I).second.SHA1.c_str(),
975 (*I).second.size,
976 (*I).first.c_str());
977 }
cde41ae8
MV
978
979 fprintf(Output, "SHA256:\n");
980 for(map<string,struct CheckSum>::iterator I = CheckSums.begin();
981 I != CheckSums.end();
982 ++I)
983 {
984 fprintf(Output, " %s %16ld %s\n",
985 (*I).second.SHA256.c_str(),
986 (*I).second.size,
987 (*I).first.c_str());
988 }
f7291f62
AL
989}
990