|
5 | 5 | # Visit https://aboutcode.org and https://github.com/nexB/univers for support and download. |
6 | 6 |
|
7 | 7 | from functools import total_ordering |
| 8 | +import re |
8 | 9 |
|
9 | 10 | import attr |
10 | 11 | import semantic_version |
@@ -343,3 +344,172 @@ def __gt__(self, other): |
343 | 344 | if not isinstance(other, self.__class__): |
344 | 345 | return NotImplemented |
345 | 346 | return gentoo.vercmp(self.value, other.value) > 0 |
| 347 | + |
| 348 | + |
| 349 | +@attr.s(frozen=True, order=False, eq=False, hash=True) |
| 350 | +class LegacyOpensslVersion(Version): |
| 351 | + """ |
| 352 | + Represent an Legacy Openssl Version . |
| 353 | +
|
| 354 | + For example:: |
| 355 | +
|
| 356 | + # 1.0.1f|0.9.7d|1.0.2ac |
| 357 | + """ |
| 358 | + |
| 359 | + @classmethod |
| 360 | + def is_valid(cls, string): |
| 361 | + return bool(cls.parse(string)) |
| 362 | + |
| 363 | + @classmethod |
| 364 | + def parse(cls, string): |
| 365 | + |
| 366 | + """ |
| 367 | + Returns the tuple containig the 4 segments (i.e major, minor, build, patch) of Legacy Version, |
| 368 | + False if not valid Legacy Openssl Version. |
| 369 | +
|
| 370 | + For example:: |
| 371 | + >>> LegacyOpensslVersion.parse("1.0.1f") |
| 372 | + (1, 0, 1, 'f') |
| 373 | + >>> LegacyOpensslVersion.parse("1.0.2ac") |
| 374 | + (1, 0, 2, 'ac') |
| 375 | + >>> LegacyOpensslVersion.parse("2.0.2az") |
| 376 | + False |
| 377 | + """ |
| 378 | + |
| 379 | + # All legacy base version of openssl that ever exited/exists. |
| 380 | + all_legacy_base = ( |
| 381 | + "0.9.1", |
| 382 | + "0.9.2", |
| 383 | + "0.9.3", |
| 384 | + "0.9.4", |
| 385 | + "0.9.5", |
| 386 | + "0.9.6", |
| 387 | + "0.9.7", |
| 388 | + "0.9.8", |
| 389 | + "1.0.0", |
| 390 | + "1.0.1", |
| 391 | + "1.0.2", |
| 392 | + "1.1.0", |
| 393 | + "1.1.1", |
| 394 | + ) |
| 395 | + # check if the starting with valid base |
| 396 | + if not string.startswith(all_legacy_base): |
| 397 | + return False |
| 398 | + |
| 399 | + segments = string.split(".") |
| 400 | + if not len(segments) == 3: |
| 401 | + return False |
| 402 | + major, minor, build = segments |
| 403 | + major = int(major) |
| 404 | + minor = int(minor) |
| 405 | + if build.isdigit(): |
| 406 | + build = int(build) |
| 407 | + patch = "" |
| 408 | + else: |
| 409 | + patch = build[1:] |
| 410 | + build = int(build[0]) |
| 411 | + if patch and patch[0].isdigit(): |
| 412 | + return False |
| 413 | + return major, minor, build, patch |
| 414 | + |
| 415 | + @classmethod |
| 416 | + def build_value(cls, string): |
| 417 | + return cls.parse(string) |
| 418 | + |
| 419 | + def __str__(self): |
| 420 | + return self.normalized_string |
| 421 | + |
| 422 | + |
| 423 | +@attr.s(frozen=True, order=False, eq=False, hash=True) |
| 424 | +class OpensslVersion(Version): |
| 425 | + |
| 426 | + """ |
| 427 | + Openssl intenally tracks two types of openssl versions |
| 428 | + - Legacy versions: Implemented in LegacyOpensslVersion |
| 429 | + - New versions: Semver |
| 430 | + For example:: |
| 431 | + >>> old = OpensslVersion("1.1.0f") |
| 432 | + >>> new = OpensslVersion("3.0.1") |
| 433 | + >>> assert old == OpensslVersion(string="1.1.0f") |
| 434 | + >>> assert new == OpensslVersion(string="3.0.1") |
| 435 | + >>> assert old.value == LegacyOpensslVersion(string="1.1.0f") |
| 436 | + >>> assert new.value == SemverVersion(string="3.0.1") |
| 437 | + >>> OpensslVersion("1.2.4fg") |
| 438 | + Traceback (most recent call last): |
| 439 | + ... |
| 440 | + univers.versions.InvalidVersion: '1.2.4fg' is not a valid <class 'univers.versions.OpensslVersion'> |
| 441 | + """ |
| 442 | + |
| 443 | + @classmethod |
| 444 | + def is_valid(cls, string): |
| 445 | + return cls.is_valid_new(string) or cls.is_valid_legacy(string) |
| 446 | + |
| 447 | + @classmethod |
| 448 | + def build_value(cls, string): |
| 449 | + """ |
| 450 | + Return a wrapped version "value" object depending on |
| 451 | + whether version is legacy or semver. |
| 452 | + """ |
| 453 | + if cls.is_valid_legacy(string): |
| 454 | + return LegacyOpensslVersion(string) |
| 455 | + if cls.is_valid_new(string): |
| 456 | + return SemverVersion(string) |
| 457 | + |
| 458 | + @classmethod |
| 459 | + def is_valid_new(cls, string): |
| 460 | + """ |
| 461 | + Checks the validity of new Openssl Version. |
| 462 | +
|
| 463 | + For example:: |
| 464 | + >>> OpensslVersion.is_valid_new("1.0.1f") |
| 465 | + False |
| 466 | + >>> OpensslVersion.is_valid_new("3.0.0") |
| 467 | + True |
| 468 | + >>> OpensslVersion.is_valid_new("3.0.2") |
| 469 | + True |
| 470 | + """ |
| 471 | + if SemverVersion.is_valid(string): |
| 472 | + sem = semantic_version.Version.coerce(string) |
| 473 | + return sem.major >= 3 |
| 474 | + |
| 475 | + @classmethod |
| 476 | + def is_valid_legacy(cls, string): |
| 477 | + return LegacyOpensslVersion.is_valid(string) |
| 478 | + |
| 479 | + def __eq__(self, other): |
| 480 | + if not isinstance(other, self.__class__): |
| 481 | + return NotImplemented |
| 482 | + if not isinstance(other.value, self.value.__class__): |
| 483 | + return NotImplemented |
| 484 | + return self.value.__eq__(other.value) |
| 485 | + |
| 486 | + def __lt__(self, other): |
| 487 | + if not isinstance(other, self.__class__): |
| 488 | + return NotImplemented |
| 489 | + if isinstance(other.value, self.value.__class__): |
| 490 | + return self.value.__lt__(other.value) |
| 491 | + # By construction legacy version is always behind Semver |
| 492 | + return isinstance(self.value, LegacyOpensslVersion) |
| 493 | + |
| 494 | + def __gt__(self, other): |
| 495 | + if not isinstance(other, self.__class__): |
| 496 | + return NotImplemented |
| 497 | + if isinstance(other.value, self.value.__class__): |
| 498 | + return self.value.__gt__(other.value) |
| 499 | + # By construction semver version is always ahead of legacy |
| 500 | + return isinstance(self.value, SemverVersion) |
| 501 | + |
| 502 | + def __le__(self, other): |
| 503 | + if not isinstance(other, self.__class__): |
| 504 | + return NotImplemented |
| 505 | + if isinstance(other.value, self.value.__class__): |
| 506 | + return self.value.__le__(other.value) |
| 507 | + # if both the are dif version, then legacy one is always behind semver |
| 508 | + return isinstance(self.value, LegacyOpensslVersion) |
| 509 | + |
| 510 | + def __ge__(self, other): |
| 511 | + if not isinstance(other, self.__class__): |
| 512 | + return NotImplemented |
| 513 | + if isinstance(other.value, self.value.__class__): |
| 514 | + return self.value.__ge__(other.value) |
| 515 | + return isinstance(self.value, SemverVersion) |
0 commit comments