1"""
2Dot NET functions
3
4.. versionadded:: 3001
5"""
6
7import salt.utils.platform
8import salt.utils.win_reg as win_reg
9from salt.utils.versions import LooseVersion
10
11__virtualname__ = "dotnet"
12
13
14# Although utils are often directly imported, it is also possible to use the
15# loader.
16def __virtual__():
17    """
18    Only load if platform is Windows
19    """
20    if not salt.utils.platform.is_windows():
21        return False, "This utility only works on Windows"
22
23    return __virtualname__
24
25
26def versions():
27    """
28    Figure out what versions of .NET are installed on the system
29
30    Returns:
31        dict: A dictionary containing two keys:
32            - versions: A list of versions installed on the system
33            - details: A dictionary with details about the versions installed on
34              the system
35    """
36    hive = "HKLM"
37    key = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP"
38    ver_keys = win_reg.list_keys(hive=hive, key=key)
39
40    def dotnet_45_plus_versions(release):
41        if release >= 528040:
42            return "4.8"
43        if release >= 461808:
44            return "4.7.2"
45        if release >= 461308:
46            return "4.7.1"
47        if release >= 460798:
48            return "4.7"
49        if release >= 394802:
50            return "4.6.2"
51        if release >= 394254:
52            return "4.6.1"
53        if release >= 393295:
54            return "4.6"
55        if release >= 379893:
56            return "4.5.2"
57        if release >= 378675:
58            return "4.5.1"
59        if release >= 378389:
60            return "4.5"
61
62    return_dict = {"versions": [], "details": {}}
63    for ver_key in ver_keys:
64
65        if ver_key.startswith("v"):
66            if win_reg.value_exists(
67                hive=hive, key="\\".join([key, ver_key]), vname="Version"
68            ):
69                # https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed#find-net-framework-versions-1-4-with-codep
70                install = win_reg.read_value(
71                    hive=hive, key="\\".join([key, ver_key]), vname="Install"
72                )["vdata"]
73                if not install:
74                    continue
75                version = win_reg.read_value(
76                    hive=hive, key="\\".join([key, ver_key]), vname="Version"
77                )["vdata"]
78                sp = win_reg.read_value(
79                    hive=hive, key="\\".join([key, ver_key]), vname="SP"
80                )["vdata"]
81            elif win_reg.value_exists(
82                hive=hive, key="\\".join([key, ver_key, "Full"]), vname="Release"
83            ):
84                # https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed#find-net-framework-versions-45-and-later-with-code
85                install = win_reg.read_value(
86                    hive=hive, key="\\".join([key, ver_key, "Full"]), vname="Install"
87                )["vdata"]
88                if not install:
89                    continue
90                version = dotnet_45_plus_versions(
91                    win_reg.read_value(
92                        hive=hive,
93                        key="\\".join([key, ver_key, "Full"]),
94                        vname="Release",
95                    )["vdata"]
96                )
97                sp = "N/A"
98            else:
99                continue
100
101            service_pack = " SP{}".format(sp) if sp != "N/A" else ""
102            return_dict["versions"].append(version)
103            return_dict["details"][ver_key] = {
104                "version": version,
105                "service_pack": sp,
106                "full": "{}{}".format(version, service_pack),
107            }
108
109    return return_dict
110
111
112def versions_list():
113    """
114    Get a sorted list of .NET versions installed on the system
115
116    Returns:
117        list: A sorted list of versions installed on the system
118    """
119    return sorted(versions()["versions"])
120
121
122def versions_details():
123    """
124    Get the details for all versions of .NET installed on a system
125
126    Returns:
127        dict: A dictionary of details for each version on the system. Contains
128        the following keys:
129            - version: The version installed
130            - service_pack: The service pack for the version installed
131            - full: The full version name including the service pack
132    """
133    return versions()["details"]
134
135
136def version_at_least(version):
137    """
138    Check that the system contains a version of .NET that is at least the
139    passed version.
140
141    Args:
142
143        version (str): The version to check for
144
145    Returns:
146        bool: ``True`` if the system contains a version of .NET that is at least
147        the passed version, otherwise ``False``
148    """
149    for dotnet_version in versions_list():
150        if LooseVersion(dotnet_version) >= LooseVersion(str(version)):
151            return True
152    return False
153