# Copyright 2026 SigmaSRC, Inc. All Rights Reserved.

"""Gather available updates."""

import os
import platform
import re
import logging
if os.name == "nt":
    from win32com.client import Dispatch

logger = logging.getLogger("gather.gather_availableupdate")
basicos = platform.uname()[0]
if basicos in [ "Microsoft", "Microsoft Windows" ]:
    basicos = "Windows"
if basicos != "Windows" and basicos != "Darwin":
    # Unsupported OS (e.g. Linux). Import cleanly and let Gather() return an empty
    # result rather than raising at import: this gather is deployed to the broad
    # "unix" agent category (which the server's osx->unix umbrella mapping shares
    # with Linux), so a Linux agent inevitably receives it. Skipping quietly avoids
    # a spurious rule error for a gather it simply can't perform here.
    logger.debug("available updates not supported on %r; gathering nothing", basicos)
_namelength = 250
_desclength = 1000

def get_win_updates(table):
    try:
        session = Dispatch("Microsoft.Update.Session")
        searcher = session.CreateUpdateSearcher()
        searcher.online = 0
        results = searcher.Search("IsInstalled=0")
        updates = results.Updates
        for update in updates:
            name = update.Title
            if not isinstance(name, str):
                name = str(name)
            desc = update.Description
            if not isinstance(desc, str):
                desc = str(desc)
            if len(name) > _namelength:
                name = name[:_namelength]
            if len(desc) > _desclength:
                desc = desc[:_desclength]
            #print name, desc
            table.add((name, desc))
    except:
        logger.exception("availableupdate gather failed")


def get_osx_updates(table):
    command = "softwareupdate -l 2>&1"
    pat1 = re.compile(r"^\s+\S\s+(.+)$")
    pat2 = re.compile(r"^\s+(.+)$")
    try:
        aline = ""
        f = os.popen(command, "r")
        try:
            aline = f.readline()
            while aline:
                name, desc = "", ""
                m = pat1.match(aline)
                if m:
                    name = m.group(1)
                    nextline = f.readline()
                    if nextline:
                        n = pat2.match(nextline)
                        if n:
                            desc = n.group(1)
                    table.add((name, desc))
                aline = f.readline()
        except:
            logger.exception(aline)
    finally:
        f.close()

def Gather(collector):
    # write header to output
    table = collector.table("availableupdate", ["update_name", "update_description"])
    if os.name == "nt":
        get_win_updates(table)
    else:
        ostype = os.uname()[0]
        if ostype == "Darwin":
            get_osx_updates(table)

def _test():
    from sigma.gather import gathering
    gathering.test(Gather)

if __name__ == "__main__":
    _test()
