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