]> git.saurik.com Git - apt.git/blob - apt-pkg/deb/deblistparser.cc
Fix buffer overflow in debListParser::VersionHash()
[apt.git] / apt-pkg / deb / deblistparser.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: deblistparser.cc,v 1.29.2.5 2004/01/06 01:43:44 mdz Exp $
4 /* ######################################################################
5
6 Package Cache Generator - Generator for the cache structure.
7
8 This builds the cache structure from the abstract package list parser.
9
10 ##################################################################### */
11 /*}}}*/
12 // Include Files /*{{{*/
13 #include <config.h>
14
15 #include <apt-pkg/deblistparser.h>
16 #include <apt-pkg/error.h>
17 #include <apt-pkg/configuration.h>
18 #include <apt-pkg/cachefilter.h>
19 #include <apt-pkg/aptconfiguration.h>
20 #include <apt-pkg/strutl.h>
21 #include <apt-pkg/crc-16.h>
22 #include <apt-pkg/md5.h>
23 #include <apt-pkg/pkgcache.h>
24 #include <apt-pkg/cacheiterators.h>
25 #include <apt-pkg/tagfile.h>
26 #include <apt-pkg/macros.h>
27
28 #include <stddef.h>
29 #include <string.h>
30 #include <algorithm>
31 #include <string>
32 #include <vector>
33 #include <ctype.h>
34 /*}}}*/
35
36 using std::string;
37 using APT::StringView;
38
39 static const debListParser::WordList PrioList[] = {
40 {"required",pkgCache::State::Required},
41 {"important",pkgCache::State::Important},
42 {"standard",pkgCache::State::Standard},
43 {"optional",pkgCache::State::Optional},
44 {"extra",pkgCache::State::Extra},
45 {"", 0}};
46
47 // ListParser::debListParser - Constructor /*{{{*/
48 // ---------------------------------------------------------------------
49 /* Provide an architecture and only this one and "all" will be accepted
50 in Step(), if no Architecture is given we will accept every arch
51 we would accept in general with checkArchitecture() */
52 debListParser::debListParser(FileFd *File) :
53 pkgCacheListParser(), d(NULL), Tags(File)
54 {
55 }
56 /*}}}*/
57 // ListParser::Package - Return the package name /*{{{*/
58 // ---------------------------------------------------------------------
59 /* This is to return the name of the package this section describes */
60 string debListParser::Package() {
61 string Result = Section.Find("Package").to_string();
62
63 // Normalize mixed case package names to lower case, like dpkg does
64 // See Bug#807012 for details
65 std::transform(Result.begin(), Result.end(), Result.begin(), tolower_ascii);
66
67 if(unlikely(Result.empty() == true))
68 _error->Error("Encountered a section with no Package: header");
69 return Result;
70 }
71 /*}}}*/
72 // ListParser::Architecture - Return the package arch /*{{{*/
73 // ---------------------------------------------------------------------
74 /* This will return the Architecture of the package this section describes */
75 APT::StringView debListParser::Architecture() {
76 auto const Arch = Section.Find("Architecture");
77 return Arch.empty() ? "none" : Arch;
78 }
79 /*}}}*/
80 // ListParser::ArchitectureAll /*{{{*/
81 // ---------------------------------------------------------------------
82 /* */
83 bool debListParser::ArchitectureAll() {
84 return Section.Find("Architecture") == "all";
85 }
86 /*}}}*/
87 // ListParser::Version - Return the version string /*{{{*/
88 // ---------------------------------------------------------------------
89 /* This is to return the string describing the version in debian form,
90 epoch:upstream-release. If this returns the blank string then the
91 entry is assumed to only describe package properties */
92 APT::StringView debListParser::Version()
93 {
94 return Section.Find("Version");
95 }
96 /*}}}*/
97 unsigned char debListParser::ParseMultiArch(bool const showErrors) /*{{{*/
98 {
99 unsigned char MA;
100 auto const MultiArch = Section.Find("Multi-Arch");
101 if (MultiArch.empty() == true || MultiArch == "no")
102 MA = pkgCache::Version::No;
103 else if (MultiArch == "same") {
104 if (ArchitectureAll() == true)
105 {
106 if (showErrors == true)
107 _error->Warning("Architecture: all package '%s' can't be Multi-Arch: same",
108 Section.FindS("Package").c_str());
109 MA = pkgCache::Version::No;
110 }
111 else
112 MA = pkgCache::Version::Same;
113 }
114 else if (MultiArch == "foreign")
115 MA = pkgCache::Version::Foreign;
116 else if (MultiArch == "allowed")
117 MA = pkgCache::Version::Allowed;
118 else
119 {
120 if (showErrors == true)
121 _error->Warning("Unknown Multi-Arch type '%s' for package '%s'",
122 MultiArch.to_string().c_str(), Section.FindS("Package").c_str());
123 MA = pkgCache::Version::No;
124 }
125
126 if (ArchitectureAll() == true)
127 MA |= pkgCache::Version::All;
128
129 return MA;
130 }
131 /*}}}*/
132 // ListParser::NewVersion - Fill in the version structure /*{{{*/
133 // ---------------------------------------------------------------------
134 /* */
135 bool debListParser::NewVersion(pkgCache::VerIterator &Ver)
136 {
137 const char *Start;
138 const char *Stop;
139
140 // Parse the section
141 if (Section.Find("Section",Start,Stop) == true)
142 {
143 map_stringitem_t const idx = StoreString(pkgCacheGenerator::SECTION, Start, Stop - Start);
144 Ver->Section = idx;
145 }
146 // Parse the source package name
147 pkgCache::GrpIterator G = Ver.ParentPkg().Group();
148 Ver->SourcePkgName = G->Name;
149 Ver->SourceVerStr = Ver->VerStr;
150 if (Section.Find("Source",Start,Stop) == true)
151 {
152 const char * const Space = (const char * const) memchr(Start, ' ', Stop - Start);
153 pkgCache::VerIterator V;
154
155 if (Space != NULL)
156 {
157 const char * const Open = (const char * const) memchr(Space, '(', Stop - Space);
158 if (likely(Open != NULL))
159 {
160 const char * const Close = (const char * const) memchr(Open, ')', Stop - Open);
161 if (likely(Close != NULL))
162 {
163 APT::StringView const version(Open + 1, (Close - Open) - 1);
164 if (version != Ver.VerStr())
165 {
166 map_stringitem_t const idx = StoreString(pkgCacheGenerator::VERSIONNUMBER, version);
167 G = Ver.ParentPkg().Group();
168 Ver->SourceVerStr = idx;
169 }
170 }
171 }
172 Stop = Space;
173 }
174
175 APT::StringView const pkgname(Start, Stop - Start);
176 if (pkgname != G.Name())
177 {
178 for (pkgCache::PkgIterator P = G.PackageList(); P.end() == false; P = G.NextPkg(P))
179 {
180 for (V = P.VersionList(); V.end() == false; ++V)
181 {
182 if (pkgname == V.SourcePkgName())
183 {
184 Ver->SourcePkgName = V->SourcePkgName;
185 break;
186 }
187 }
188 if (V.end() == false)
189 break;
190 }
191 if (V.end() == true)
192 {
193 map_stringitem_t const idx = StoreString(pkgCacheGenerator::PKGNAME, pkgname);
194 G = Ver.ParentPkg().Group();
195 Ver->SourcePkgName = idx;
196 }
197 }
198 }
199
200 Ver->MultiArch = ParseMultiArch(true);
201 // Archive Size
202 Ver->Size = Section.FindULL("Size");
203 // Unpacked Size (in K)
204 Ver->InstalledSize = Section.FindULL("Installed-Size");
205 Ver->InstalledSize *= 1024;
206
207 // Priority
208 if (Section.Find("Priority",Start,Stop) == true)
209 {
210 if (GrabWord(StringView(Start,Stop-Start),PrioList,Ver->Priority) == false)
211 Ver->Priority = pkgCache::State::Extra;
212 }
213
214 if (ParseDepends(Ver,"Pre-Depends",pkgCache::Dep::PreDepends) == false)
215 return false;
216 if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false)
217 return false;
218 if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false)
219 return false;
220 if (ParseDepends(Ver,"Breaks",pkgCache::Dep::DpkgBreaks) == false)
221 return false;
222 if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false)
223 return false;
224 if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false)
225 return false;
226 if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Replaces) == false)
227 return false;
228 if (ParseDepends(Ver,"Enhances",pkgCache::Dep::Enhances) == false)
229 return false;
230 // Obsolete.
231 if (ParseDepends(Ver,"Optional",pkgCache::Dep::Suggests) == false)
232 return false;
233
234 if (ParseProvides(Ver) == false)
235 return false;
236
237 return true;
238 }
239 /*}}}*/
240 // ListParser::AvailableDescriptionLanguages /*{{{*/
241 std::vector<std::string> debListParser::AvailableDescriptionLanguages()
242 {
243 std::vector<std::string> const understood = APT::Configuration::getLanguages();
244 std::vector<std::string> avail;
245 static constexpr int prefixLen = 12;
246 static constexpr int avgLanguageLen = 5;
247 std::string tagname;
248
249 tagname.reserve(prefixLen + avgLanguageLen);
250 tagname.assign("Description-");
251 if (Section.Exists("Description") == true)
252 avail.push_back("");
253 for (std::vector<std::string>::const_iterator lang = understood.begin(); lang != understood.end(); ++lang)
254 {
255 tagname.resize(prefixLen);
256 tagname.append(*lang);
257 if (Section.Exists(tagname) == true)
258 avail.push_back(*lang);
259 }
260 return avail;
261 }
262 /*}}}*/
263 // ListParser::Description_md5 - Return the description_md5 MD5SumValue /*{{{*/
264 // ---------------------------------------------------------------------
265 /* This is to return the md5 string to allow the check if it is the right
266 description. If no Description-md5 is found in the section it will be
267 calculated.
268 */
269 MD5SumValue debListParser::Description_md5()
270 {
271 StringView const value = Section.Find("Description-md5");
272 if (value.empty() == true)
273 {
274 StringView const desc = Section.Find("Description");
275 if (desc == "\n")
276 return MD5SumValue();
277
278 MD5Summation md5;
279 md5.Add(desc.data(), desc.size());
280 md5.Add("\n");
281 return md5.Result();
282 }
283 else if (likely(value.size() == 32))
284 {
285 MD5SumValue sumvalue;
286 if (sumvalue.Set(value))
287 return sumvalue;
288
289 _error->Error("Malformed Description-md5 line; includes invalid character '%.*s'", (int)value.length(), value.data());
290 return MD5SumValue();
291 }
292 _error->Error("Malformed Description-md5 line; doesn't have the required length (32 != %d) '%.*s'", (int)value.size(), (int)value.length(), value.data());
293 return MD5SumValue();
294 }
295 /*}}}*/
296 // ListParser::UsePackage - Update a package structure /*{{{*/
297 // ---------------------------------------------------------------------
298 /* This is called to update the package with any new information
299 that might be found in the section */
300 bool debListParser::UsePackage(pkgCache::PkgIterator &Pkg,
301 pkgCache::VerIterator &Ver)
302 {
303 string const static myArch = _config->Find("APT::Architecture");
304 // Possible values are: "all", "native", "installed" and "none"
305 // The "installed" mode is handled by ParseStatus(), See #544481 and friends.
306 string const static essential = _config->Find("pkgCacheGen::Essential", "all");
307 if (essential == "all" ||
308 (essential == "native" && Pkg->Arch != 0 && myArch == Pkg.Arch()))
309 if (Section.FindFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
310 return false;
311 if (Section.FindFlag("Important",Pkg->Flags,pkgCache::Flag::Important) == false)
312 return false;
313
314 if (strcmp(Pkg.Name(),"apt") == 0)
315 {
316 if ((essential == "native" && Pkg->Arch != 0 && myArch == Pkg.Arch()) ||
317 essential == "all")
318 Pkg->Flags |= pkgCache::Flag::Essential | pkgCache::Flag::Important;
319 else
320 Pkg->Flags |= pkgCache::Flag::Important;
321 }
322
323 if (ParseStatus(Pkg,Ver) == false)
324 return false;
325 return true;
326 }
327 /*}}}*/
328 // ListParser::VersionHash - Compute a unique hash for this version /*{{{*/
329 // ---------------------------------------------------------------------
330 /* */
331 unsigned short debListParser::VersionHash()
332 {
333 static const StringView Sections[] ={"Installed-Size",
334 "Depends",
335 "Pre-Depends",
336 // "Suggests",
337 // "Recommends",
338 "Conflicts",
339 "Breaks",
340 "Replaces"};
341 unsigned long Result = INIT_FCS;
342 char S[1024];
343 for (StringView I : Sections)
344 {
345 const char *Start;
346 const char *End;
347 if (Section.Find(I,Start,End) == false || End - Start >= (signed)sizeof(S))
348 continue;
349
350 /* Strip out any spaces from the text, this undoes dpkgs reformatting
351 of certain fields. dpkg also has the rather interesting notion of
352 reformatting depends operators < -> <= */
353 char *J = S;
354 for (; Start != End; ++Start)
355 {
356 if (isspace_ascii(*Start) != 0)
357 continue;
358 *J++ = tolower_ascii(*Start);
359
360 /* Normalize <= to < and >= to >. This is the wrong way around, but
361 * more efficient that the right way. And since we're only hashing
362 * it does not matter which way we normalize. */
363 if ((*Start == '<' || *Start == '>') && Start[1] == '=') {
364 Start++;
365 }
366 }
367
368 Result = AddCRC16(Result,S,J - S);
369 }
370
371 return Result;
372 }
373 /*}}}*/
374 // StatusListParser::ParseStatus - Parse the status field /*{{{*/
375 // ---------------------------------------------------------------------
376 /* Status lines are of the form,
377 Status: want flag status
378 want = unknown, install, hold, deinstall, purge
379 flag = ok, reinstreq
380 status = not-installed, config-files, half-installed, unpacked,
381 half-configured, triggers-awaited, triggers-pending, installed
382 */
383 bool debListParser::ParseStatus(pkgCache::PkgIterator &,
384 pkgCache::VerIterator &)
385 {
386 return true;
387 }
388 bool debStatusListParser::ParseStatus(pkgCache::PkgIterator &Pkg,
389 pkgCache::VerIterator &Ver)
390 {
391 const char *Start;
392 const char *Stop;
393 if (Section.Find("Status",Start,Stop) == false)
394 return true;
395
396 // UsePackage() is responsible for setting the flag in the default case
397 bool const static essential = _config->Find("pkgCacheGen::Essential", "") == "installed";
398 if (essential == true &&
399 Section.FindFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
400 return false;
401
402 // Isolate the first word
403 const char *I = Start;
404 for(; I < Stop && *I != ' '; I++);
405 if (I >= Stop || *I != ' ')
406 return _error->Error("Malformed Status line");
407
408 // Process the want field
409 WordList WantList[] = {{"unknown",pkgCache::State::Unknown},
410 {"install",pkgCache::State::Install},
411 {"hold",pkgCache::State::Hold},
412 {"deinstall",pkgCache::State::DeInstall},
413 {"purge",pkgCache::State::Purge},
414 {"", 0}};
415 if (GrabWord(StringView(Start,I-Start),WantList,Pkg->SelectedState) == false)
416 return _error->Error("Malformed 1st word in the Status line");
417
418 // Isloate the next word
419 I++;
420 Start = I;
421 for(; I < Stop && *I != ' '; I++);
422 if (I >= Stop || *I != ' ')
423 return _error->Error("Malformed status line, no 2nd word");
424
425 // Process the flag field
426 WordList FlagList[] = {{"ok",pkgCache::State::Ok},
427 {"reinstreq",pkgCache::State::ReInstReq},
428 {"hold",pkgCache::State::HoldInst},
429 {"hold-reinstreq",pkgCache::State::HoldReInstReq},
430 {"", 0}};
431 if (GrabWord(StringView(Start,I-Start),FlagList,Pkg->InstState) == false)
432 return _error->Error("Malformed 2nd word in the Status line");
433
434 // Isloate the last word
435 I++;
436 Start = I;
437 for(; I < Stop && *I != ' '; I++);
438 if (I != Stop)
439 return _error->Error("Malformed Status line, no 3rd word");
440
441 // Process the flag field
442 WordList StatusList[] = {{"not-installed",pkgCache::State::NotInstalled},
443 {"config-files",pkgCache::State::ConfigFiles},
444 {"half-installed",pkgCache::State::HalfInstalled},
445 {"unpacked",pkgCache::State::UnPacked},
446 {"half-configured",pkgCache::State::HalfConfigured},
447 {"triggers-awaited",pkgCache::State::TriggersAwaited},
448 {"triggers-pending",pkgCache::State::TriggersPending},
449 {"installed",pkgCache::State::Installed},
450 {"", 0}};
451 if (GrabWord(StringView(Start,I-Start),StatusList,Pkg->CurrentState) == false)
452 return _error->Error("Malformed 3rd word in the Status line");
453
454 /* A Status line marks the package as indicating the current
455 version as well. Only if it is actually installed.. Otherwise
456 the interesting dpkg handling of the status file creates bogus
457 entries. */
458 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
459 Pkg->CurrentState == pkgCache::State::ConfigFiles))
460 {
461 if (Ver.end() == true)
462 _error->Warning("Encountered status field in a non-version description");
463 else
464 Pkg->CurrentVer = Ver.Index();
465 }
466
467 return true;
468 }
469
470 const char *debListParser::ConvertRelation(const char *I,unsigned int &Op)
471 {
472 // Determine the operator
473 switch (*I)
474 {
475 case '<':
476 I++;
477 if (*I == '=')
478 {
479 I++;
480 Op = pkgCache::Dep::LessEq;
481 break;
482 }
483
484 if (*I == '<')
485 {
486 I++;
487 Op = pkgCache::Dep::Less;
488 break;
489 }
490
491 // < is the same as <= and << is really Cs < for some reason
492 Op = pkgCache::Dep::LessEq;
493 break;
494
495 case '>':
496 I++;
497 if (*I == '=')
498 {
499 I++;
500 Op = pkgCache::Dep::GreaterEq;
501 break;
502 }
503
504 if (*I == '>')
505 {
506 I++;
507 Op = pkgCache::Dep::Greater;
508 break;
509 }
510
511 // > is the same as >= and >> is really Cs > for some reason
512 Op = pkgCache::Dep::GreaterEq;
513 break;
514
515 case '=':
516 Op = pkgCache::Dep::Equals;
517 I++;
518 break;
519
520 // HACK around bad package definitions
521 default:
522 Op = pkgCache::Dep::Equals;
523 break;
524 }
525 return I;
526 }
527 /*}}}*/
528 // ListParser::ParseDepends - Parse a dependency element /*{{{*/
529 // ---------------------------------------------------------------------
530 /* This parses the dependency elements out of a standard string in place,
531 bit by bit. */
532 const char *debListParser::ParseDepends(const char *Start,const char *Stop,
533 std::string &Package,std::string &Ver,unsigned int &Op)
534 { return ParseDepends(Start, Stop, Package, Ver, Op, false, true, false); }
535 const char *debListParser::ParseDepends(const char *Start,const char *Stop,
536 std::string &Package,std::string &Ver,unsigned int &Op,
537 bool const &ParseArchFlags)
538 { return ParseDepends(Start, Stop, Package, Ver, Op, ParseArchFlags, true, false); }
539 const char *debListParser::ParseDepends(const char *Start,const char *Stop,
540 std::string &Package,std::string &Ver,unsigned int &Op,
541 bool const &ParseArchFlags, bool const &StripMultiArch)
542 { return ParseDepends(Start, Stop, Package, Ver, Op, ParseArchFlags, StripMultiArch, false); }
543 const char *debListParser::ParseDepends(const char *Start,const char *Stop,
544 string &Package,string &Ver,
545 unsigned int &Op, bool const &ParseArchFlags,
546 bool const &StripMultiArch,
547 bool const &ParseRestrictionsList)
548 {
549 StringView PackageView;
550 StringView VerView;
551
552 auto res = ParseDepends(Start, Stop, PackageView, VerView, Op, (bool)ParseArchFlags,
553 (bool) StripMultiArch, (bool) ParseRestrictionsList);
554 Package = PackageView.to_string();
555 Ver = VerView.to_string();
556
557 return res;
558 }
559 const char *debListParser::ParseDepends(const char *Start,const char *Stop,
560 StringView &Package,StringView &Ver,
561 unsigned int &Op, bool ParseArchFlags,
562 bool StripMultiArch,
563 bool ParseRestrictionsList)
564 {
565 // Strip off leading space
566 for (;Start != Stop && isspace_ascii(*Start) != 0; ++Start);
567
568 // Parse off the package name
569 const char *I = Start;
570 for (;I != Stop && isspace_ascii(*I) == 0 && *I != '(' && *I != ')' &&
571 *I != ',' && *I != '|' && *I != '[' && *I != ']' &&
572 *I != '<' && *I != '>'; ++I);
573
574 // Malformed, no '('
575 if (I != Stop && *I == ')')
576 return 0;
577
578 if (I == Start)
579 return 0;
580
581 // Stash the package name
582 Package = StringView(Start, I - Start);
583
584 // We don't want to confuse library users which can't handle MultiArch
585 if (StripMultiArch == true) {
586 string const arch = _config->Find("APT::Architecture");
587 size_t const found = Package.rfind(':');
588 if (found != StringView::npos &&
589 (Package.substr(found) == ":any" ||
590 Package.substr(found) == ":native" ||
591 Package.substr(found +1) == arch))
592 Package = Package.substr(0,found);
593 }
594
595 // Skip white space to the '('
596 for (;I != Stop && isspace_ascii(*I) != 0 ; I++);
597
598 // Parse a version
599 if (I != Stop && *I == '(')
600 {
601 // Skip the '('
602 for (I++; I != Stop && isspace_ascii(*I) != 0 ; I++);
603 if (I + 3 >= Stop)
604 return 0;
605 I = ConvertRelation(I,Op);
606
607 // Skip whitespace
608 for (;I != Stop && isspace_ascii(*I) != 0; I++);
609 Start = I;
610 I = (const char*) memchr(I, ')', Stop - I);
611 if (I == NULL || Start == I)
612 return 0;
613
614 // Skip trailing whitespace
615 const char *End = I;
616 for (; End > Start && isspace_ascii(End[-1]); End--);
617
618 Ver = StringView(Start,End-Start);
619 I++;
620 }
621 else
622 {
623 Ver = StringView();
624 Op = pkgCache::Dep::NoOp;
625 }
626
627 // Skip whitespace
628 for (;I != Stop && isspace_ascii(*I) != 0; I++);
629
630 if (unlikely(ParseArchFlags == true))
631 {
632 string const arch = _config->Find("APT::Architecture");
633 APT::CacheFilter::PackageArchitectureMatchesSpecification matchesArch(arch, false);
634
635 // Parse an architecture
636 if (I != Stop && *I == '[')
637 {
638 ++I;
639 // malformed
640 if (unlikely(I == Stop))
641 return 0;
642
643 const char *End = I;
644 bool Found = false;
645 bool NegArch = false;
646 while (I != Stop)
647 {
648 // look for whitespace or ending ']'
649 for (;End != Stop && !isspace_ascii(*End) && *End != ']'; ++End);
650
651 if (unlikely(End == Stop))
652 return 0;
653
654 if (*I == '!')
655 {
656 NegArch = true;
657 ++I;
658 }
659
660 std::string const arch(I, End);
661 if (arch.empty() == false && matchesArch(arch.c_str()) == true)
662 {
663 Found = true;
664 if (I[-1] != '!')
665 NegArch = false;
666 // we found a match, so fast-forward to the end of the wildcards
667 for (; End != Stop && *End != ']'; ++End);
668 }
669
670 if (*End++ == ']') {
671 I = End;
672 break;
673 }
674
675 I = End;
676 for (;I != Stop && isspace_ascii(*I) != 0; I++);
677 }
678
679 if (NegArch == true)
680 Found = !Found;
681
682 if (Found == false)
683 Package = ""; /* not for this arch */
684 }
685
686 // Skip whitespace
687 for (;I != Stop && isspace_ascii(*I) != 0; I++);
688 }
689
690 if (unlikely(ParseRestrictionsList == true))
691 {
692 // Parse a restrictions formula which is in disjunctive normal form:
693 // (foo AND bar) OR (blub AND bla)
694
695 std::vector<string> const profiles = APT::Configuration::getBuildProfiles();
696
697 // if the next character is a restriction list, then by default the
698 // dependency does not apply and the conditions have to be checked
699 // if the next character is not a restriction list, then by default the
700 // dependency applies
701 bool applies1 = (*I != '<');
702 while (I != Stop)
703 {
704 if (*I != '<')
705 break;
706
707 ++I;
708 // malformed
709 if (unlikely(I == Stop))
710 return 0;
711
712 const char *End = I;
713
714 // if of the prior restriction list is already fulfilled, then
715 // we can just skip to the end of the current list
716 if (applies1) {
717 for (;End != Stop && *End != '>'; ++End);
718 I = ++End;
719 // skip whitespace
720 for (;I != Stop && isspace_ascii(*I) != 0; I++);
721 } else {
722 bool applies2 = true;
723 // all the conditions inside a restriction list have to be
724 // met so once we find one that is not met, we can skip to
725 // the end of this list
726 while (I != Stop)
727 {
728 // look for whitespace or ending '>'
729 // End now points to the character after the current term
730 for (;End != Stop && !isspace_ascii(*End) && *End != '>'; ++End);
731
732 if (unlikely(End == Stop))
733 return 0;
734
735 bool NegRestriction = false;
736 if (*I == '!')
737 {
738 NegRestriction = true;
739 ++I;
740 }
741
742 std::string const restriction(I, End);
743 if (restriction.empty() == false && profiles.empty() == false &&
744 std::find(profiles.begin(), profiles.end(), restriction) != profiles.end())
745 {
746 if (NegRestriction) {
747 applies2 = false;
748 // since one of the terms does not apply we don't have to check the others
749 for (; End != Stop && *End != '>'; ++End);
750 }
751 } else {
752 if (!NegRestriction) {
753 applies2 = false;
754 // since one of the terms does not apply we don't have to check the others
755 for (; End != Stop && *End != '>'; ++End);
756 }
757 }
758
759 if (*End++ == '>') {
760 I = End;
761 // skip whitespace
762 for (;I != Stop && isspace_ascii(*I) != 0; I++);
763 break;
764 }
765
766 I = End;
767 // skip whitespace
768 for (;I != Stop && isspace_ascii(*I) != 0; I++);
769 }
770 if (applies2) {
771 applies1 = true;
772 }
773 }
774 }
775
776 if (applies1 == false) {
777 Package = ""; //not for this restriction
778 }
779 }
780
781 if (I != Stop && *I == '|')
782 Op |= pkgCache::Dep::Or;
783
784 if (I == Stop || *I == ',' || *I == '|')
785 {
786 if (I != Stop)
787 for (I++; I != Stop && isspace_ascii(*I) != 0; I++);
788 return I;
789 }
790
791 return 0;
792 }
793 /*}}}*/
794 // ListParser::ParseDepends - Parse a dependency list /*{{{*/
795 // ---------------------------------------------------------------------
796 /* This is the higher level depends parser. It takes a tag and generates
797 a complete depends tree for the given version. */
798 bool debListParser::ParseDepends(pkgCache::VerIterator &Ver,
799 StringView Tag,unsigned int Type)
800 {
801 const char *Start;
802 const char *Stop;
803 if (Section.Find(Tag,Start,Stop) == false || Start == Stop)
804 return true;
805
806 string const pkgArch = Ver.Arch();
807
808 while (1)
809 {
810 StringView Package;
811 StringView Version;
812 unsigned int Op;
813
814 Start = ParseDepends(Start, Stop, Package, Version, Op, false, false, false);
815 if (Start == 0)
816 return _error->Error("Problem parsing dependency %.*s",(int)Tag.length(), Tag.data());
817 size_t const found = Package.rfind(':');
818
819 if (found == string::npos)
820 {
821 if (NewDepends(Ver,Package,pkgArch,Version,Op,Type) == false)
822 return false;
823 }
824 else if (Package.substr(found) == ":any")
825 {
826 if (NewDepends(Ver,Package,"any",Version,Op,Type) == false)
827 return false;
828 }
829 else
830 {
831 // Such dependencies are not supposed to be accepted …
832 // … but this is probably the best thing to do anyway
833 if (Package.substr(found + 1) == "native")
834 {
835 std::string const Pkg = Package.substr(0, found).to_string() + ':' + Ver.Cache()->NativeArch();
836 if (NewDepends(Ver, Pkg, "any", Version, Op | pkgCache::Dep::ArchSpecific, Type) == false)
837 return false;
838 }
839 else if (NewDepends(Ver, Package, "any", Version, Op | pkgCache::Dep::ArchSpecific, Type) == false)
840 return false;
841 }
842
843 if (Start == Stop)
844 break;
845 }
846 return true;
847 }
848 /*}}}*/
849 // ListParser::ParseProvides - Parse the provides list /*{{{*/
850 // ---------------------------------------------------------------------
851 /* */
852 bool debListParser::ParseProvides(pkgCache::VerIterator &Ver)
853 {
854 /* it is unlikely, but while parsing dependencies, we might have already
855 picked up multi-arch implicit provides which we do not want to duplicate here */
856 bool hasProvidesAlready = false;
857 std::string const spzName = Ver.ParentPkg().FullName(false);
858 {
859 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
860 {
861 if (Prv.IsMultiArchImplicit() == false || (Prv->Flags & pkgCache::Flag::ArchSpecific) == 0)
862 continue;
863 if (spzName != Prv.OwnerPkg().FullName(false))
864 continue;
865 hasProvidesAlready = true;
866 break;
867 }
868 }
869
870 string const Arch = Ver.Arch();
871 const char *Start;
872 const char *Stop;
873 if (Section.Find("Provides",Start,Stop) == true)
874 {
875 StringView Package;
876 StringView Version;
877 unsigned int Op;
878
879 do
880 {
881 Start = ParseDepends(Start,Stop,Package,Version,Op, false, false, false);
882 const size_t archfound = Package.rfind(':');
883 if (Start == 0)
884 return _error->Error("Problem parsing Provides line");
885 if (unlikely(Op != pkgCache::Dep::NoOp && Op != pkgCache::Dep::Equals)) {
886 _error->Warning("Ignoring Provides line with non-equal DepCompareOp for package %s", Package.to_string().c_str());
887 } else if (archfound != string::npos) {
888 StringView spzArch = Package.substr(archfound + 1);
889 if (spzArch != "any")
890 {
891 if (NewProvides(Ver, Package.substr(0, archfound), spzArch, Version, pkgCache::Flag::MultiArchImplicit | pkgCache::Flag::ArchSpecific) == false)
892 return false;
893 }
894 if (NewProvides(Ver, Package, "any", Version, pkgCache::Flag::ArchSpecific) == false)
895 return false;
896 } else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign) {
897 if (APT::Configuration::checkArchitecture(Arch))
898 {
899 if (NewProvidesAllArch(Ver, Package, Version, 0) == false)
900 return false;
901 }
902 else if (NewProvides(Ver, Package, Arch, Version, 0) == false)
903 return false;
904 } else {
905 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
906 {
907 if (NewProvides(Ver, Package.to_string().append(":any"), "any", Version, pkgCache::Flag::MultiArchImplicit) == false)
908 return false;
909 }
910 if (NewProvides(Ver, Package, Arch, Version, 0) == false)
911 return false;
912 }
913 if (archfound == std::string::npos)
914 {
915 string spzName = Package.to_string();
916 spzName.push_back(':');
917 spzName.append(Ver.ParentPkg().Arch());
918 pkgCache::PkgIterator const spzPkg = Ver.Cache()->FindPkg(spzName, "any");
919 if (spzPkg.end() == false)
920 {
921 if (NewProvides(Ver, spzName, "any", Version, pkgCache::Flag::MultiArchImplicit | pkgCache::Flag::ArchSpecific) == false)
922 return false;
923 }
924 }
925 } while (Start != Stop);
926 }
927
928 if (APT::Configuration::checkArchitecture(Arch))
929 {
930 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
931 {
932 string const Package = string(Ver.ParentPkg().Name()).append(":").append("any");
933 if (NewProvides(Ver, Package, "any", Ver.VerStr(), pkgCache::Flag::MultiArchImplicit) == false)
934 return false;
935 }
936 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
937 {
938 if (NewProvidesAllArch(Ver, Ver.ParentPkg().Name(), Ver.VerStr(), pkgCache::Flag::MultiArchImplicit) == false)
939 return false;
940 }
941 }
942
943 if (hasProvidesAlready == false)
944 {
945 pkgCache::PkgIterator const spzPkg = Ver.Cache()->FindPkg(spzName, "any");
946 if (spzPkg.end() == false)
947 {
948 if (NewProvides(Ver, spzName, "any", Ver.VerStr(), pkgCache::Flag::MultiArchImplicit | pkgCache::Flag::ArchSpecific) == false)
949 return false;
950 }
951 }
952 return true;
953 }
954 /*}}}*/
955 // ListParser::GrabWord - Matches a word and returns /*{{{*/
956 // ---------------------------------------------------------------------
957 /* Looks for a word in a list of words - for ParseStatus */
958 bool debListParser::GrabWord(StringView Word, WordList const *List, unsigned char &Out)
959 {
960 for (unsigned int C = 0; List[C].Str.empty() == false; C++)
961 {
962 if (Word.length() == List[C].Str.length() &&
963 strncasecmp(Word.data(), List[C].Str.data(), Word.length()) == 0)
964 {
965 Out = List[C].Val;
966 return true;
967 }
968 }
969 return false;
970 }
971 /*}}}*/
972 // ListParser::Step - Move to the next section in the file /*{{{*/
973 // ---------------------------------------------------------------------
974 /* This has to be careful to only process the correct architecture */
975 bool debListParser::Step()
976 {
977 iOffset = Tags.Offset();
978 return Tags.Step(Section);
979 }
980 /*}}}*/
981 // ListParser::GetPrio - Convert the priority from a string /*{{{*/
982 // ---------------------------------------------------------------------
983 /* */
984 unsigned char debListParser::GetPrio(string Str)
985 {
986 unsigned char Out;
987 if (GrabWord(Str,PrioList,Out) == false)
988 Out = pkgCache::State::Extra;
989
990 return Out;
991 }
992 /*}}}*/
993 bool debListParser::SameVersion(unsigned short const Hash, /*{{{*/
994 pkgCache::VerIterator const &Ver)
995 {
996 if (pkgCacheListParser::SameVersion(Hash, Ver) == false)
997 return false;
998 // status file has no (Download)Size, but all others are fair game
999 // status file is parsed last, so the first version we encounter is
1000 // probably also the version we have downloaded
1001 unsigned long long const Size = Section.FindULL("Size");
1002 if (Size != 0 && Size != Ver->Size)
1003 return false;
1004 // available everywhere, but easier to check here than to include in VersionHash
1005 unsigned char MultiArch = ParseMultiArch(false);
1006 if (MultiArch != Ver->MultiArch)
1007 return false;
1008 // for all practical proposes (we can check): same version
1009 return true;
1010 }
1011 /*}}}*/
1012
1013 debDebFileParser::debDebFileParser(FileFd *File, std::string const &DebFile)
1014 : debListParser(File), DebFile(DebFile)
1015 {
1016 }
1017
1018 bool debDebFileParser::UsePackage(pkgCache::PkgIterator &Pkg,
1019 pkgCache::VerIterator &Ver)
1020 {
1021 bool res = debListParser::UsePackage(Pkg, Ver);
1022 // we use the full file path as a provides so that the file is found
1023 // by its name
1024 if(NewProvides(Ver, DebFile, Pkg.Cache()->NativeArch(), Ver.VerStr(), 0) == false)
1025 return false;
1026 return res;
1027 }
1028
1029 debListParser::~debListParser() {}