glasswall.libraries.editor.editor

   1import ctypes as ct
   2import functools
   3import io
   4import os
   5from contextlib import contextmanager
   6from typing import Optional, Union
   7
   8import glasswall
   9from glasswall import determine_file_type as dft
  10from glasswall import utils
  11from glasswall.config.logging import format_object, log
  12from glasswall.libraries.editor import errors, successes
  13from glasswall.libraries.library import Library
  14
  15
  16class Editor(Library):
  17    """ A high level Python wrapper for Glasswall Editor / Core2. """
  18
  19    def __init__(self, library_path: str, licence: Union[str, bytes, bytearray, io.BytesIO] = None):
  20        """ Initialise the Editor instance.
  21
  22        Args:
  23            library_path (str): The file or directory path to the Editor library.
  24            licence (str, bytes, bytearray, or io.BytesIO, optional): The licence file content or path. This can be:
  25                - A string representing the file path to the licence.
  26                - A `bytes` or `bytearray` object containing the licence data.
  27                - An `io.BytesIO` object for in-memory licence data.
  28                If not specified, it is assumed that the licence file is located in the same directory as the `library_path`.
  29        """
  30        super().__init__(library_path)
  31        self.library = self.load_library(os.path.abspath(library_path))
  32        self.licence = licence
  33
  34        # Validate killswitch has not activated
  35        self.validate_licence()
  36
  37        log.info(f"Loaded Glasswall {self.__class__.__name__} version {self.version()} from {self.library_path}")
  38
  39    def validate_licence(self):
  40        """ Validates the licence of the library by checking the licence details.
  41
  42        Raises:
  43            LicenceExpired: If the licence has expired or could not be validated.
  44        """
  45        licence_details = self.licence_details()
  46
  47        bad_details = [
  48            "Unable to Read Licence Key File",
  49            "Licence File Missing Required Contents",
  50            "Licence Expired",
  51        ]
  52
  53        if any(bad_detail.lower() in licence_details.lower() for bad_detail in bad_details):
  54            # bad_details found in licence_details
  55            log.error(f"{self.__class__.__name__} licence validation failed. Licence details:\n{licence_details}")
  56            raise errors.LicenceExpired(licence_details)
  57        else:
  58            log.debug(f"{self.__class__.__name__} licence validated successfully. Licence details:\n{licence_details}")
  59
  60    def version(self):
  61        """ Returns the Glasswall library version.
  62
  63        Returns:
  64            version (str): The Glasswall library version.
  65        """
  66        # API function declaration
  67        self.library.GW2LibVersion.restype = ct.c_char_p
  68
  69        # API call
  70        version = self.library.GW2LibVersion()
  71
  72        # Convert to Python string
  73        version = ct.string_at(version).decode()
  74
  75        return version
  76
  77    def open_session(self):
  78        """ Open a new Glasswall session.
  79
  80        Returns:
  81            session (int): An incrementing integer repsenting the current session.
  82        """
  83        # API call
  84        session = self.library.GW2OpenSession()
  85
  86        log.debug(f"\n\tsession: {session}")
  87
  88        if self.licence:
  89            # Must register licence on each GW2OpenSession if the licence is not in the same directory as the library
  90            self.register_licence(session, self.licence)
  91
  92        return session
  93
  94    def close_session(self, session: int) -> int:
  95        """ Close the Glasswall session. All resources allocated by the session will be destroyed.
  96
  97        Args:
  98            session (int): The session to close.
  99
 100        Returns:
 101            status (int): The status code of the function call.
 102        """
 103        if not isinstance(session, int):
 104            raise TypeError(session)
 105
 106        # API function declaration
 107        self.library.GW2CloseSession.argtypes = [ct.c_size_t]
 108
 109        # Variable initialisation
 110        ct_session = ct.c_size_t(session)
 111
 112        # API call
 113        status = self.library.GW2CloseSession(ct_session)
 114
 115        if status not in successes.success_codes:
 116            log.error(f"\n\tsession: {session}\n\tstatus: {status}")
 117        else:
 118            log.debug(f"\n\tsession: {session}\n\tstatus: {status}")
 119
 120        return status
 121
 122    @contextmanager
 123    def new_session(self):
 124        """ Context manager. Opens a new session on entry and closes the session on exit. """
 125        try:
 126            session = self.open_session()
 127            yield session
 128        finally:
 129            self.close_session(session)
 130
 131    def run_session(self, session):
 132        """ Runs the Glasswall session and begins processing of a file.
 133
 134        Args:
 135            session (int): The session to run.
 136
 137        Returns:
 138            status (int): The status code of the function call.
 139        """
 140        # API function declaration
 141        self.library.GW2RunSession.argtypes = [ct.c_size_t]
 142
 143        # Variable initialisation
 144        ct_session = ct.c_size_t(session)
 145
 146        # API call
 147        status = self.library.GW2RunSession(ct_session)
 148
 149        if status not in successes.success_codes:
 150            log.error(f"\n\tsession: {session}\n\tstatus: {status}\n\tGW2FileErrorMsg: {self.file_error_message(session)}")
 151        else:
 152            log.debug(f"\n\tsession: {session}\n\tstatus: {status}\n\tGW2FileErrorMsg: {self.file_error_message(session)}")
 153
 154        return status
 155
 156    def determine_file_type(self, input_file: Union[str, bytes, bytearray, io.BytesIO], as_string: bool = False, raise_unsupported: bool = True) -> Union[int, str]:
 157        """ Determine the file type of a given input file, either as an integer identifier or a string.
 158
 159        Args:
 160            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file to analyse. It can be provided as a file path (str), bytes, bytearray, or a BytesIO object.
 161            as_string (bool, optional): Return file type as string, eg: "bmp" instead of: 29. Defaults to False.
 162            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
 163
 164        Returns:
 165            file_type (Union[int, str]): The file type.
 166        """
 167        if isinstance(input_file, str):
 168            if not os.path.isfile(input_file):
 169                raise FileNotFoundError(input_file)
 170
 171            # convert to ct.c_char_p of bytes
 172            ct_input_file = ct.c_char_p(input_file.encode("utf-8"))
 173
 174            # API call
 175            file_type = self.library.GW2DetermineFileTypeFromFile(ct_input_file)
 176
 177        elif isinstance(input_file, (bytes, bytearray, io.BytesIO)):
 178            # convert to bytes
 179            bytes_input_file = utils.as_bytes(input_file)
 180
 181            # ctypes conversion
 182            ct_buffer = ct.c_char_p(bytes_input_file)
 183            ct_buffer_length = ct.c_size_t(len(bytes_input_file))
 184
 185            # API call
 186            file_type = self.library.GW2DetermineFileTypeFromMemory(
 187                ct_buffer,
 188                ct_buffer_length
 189            )
 190
 191        else:
 192            raise TypeError(input_file)
 193
 194        file_type_as_string = dft.file_type_int_to_str(file_type)
 195        input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
 196
 197        if not dft.is_success(file_type):
 198            log.warning(f"\n\tfile_type: {file_type}\n\tfile_type_as_string: {file_type_as_string}\n\tinput_file: {input_file_repr}")
 199            if raise_unsupported:
 200                raise dft.int_class_map.get(file_type, dft.errors.UnknownErrorCode)(file_type)
 201        else:
 202            log.debug(f"\n\tfile_type: {file_type}\n\tfile_type_as_string: {file_type_as_string}\n\tinput_file: {input_file_repr}")
 203
 204        if as_string:
 205            return file_type_as_string
 206
 207        return file_type
 208
 209    def _GW2GetPolicySettings(self, session: int, policy_format: int = 0):
 210        """ Get current policy settings for the given session.
 211
 212        Args:
 213            session (int): The session integer.
 214            policy_format (int): The format of the content management policy. 0=XML.
 215
 216        Returns:
 217            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'policy_format', 'status', 'policy'.
 218        """
 219        # API function declaration
 220        self.library.GW2GetPolicySettings.argtypes = [
 221            ct.c_size_t,  # Session_Handle session
 222            ct.POINTER(ct.c_void_p),  # char ** policiesBuffer
 223            ct.POINTER(ct.c_size_t),  # size_t * policiesLength
 224            ct.c_int,  # Policy_Format format
 225        ]
 226
 227        # Variable initialisation
 228        gw_return_object = glasswall.GwReturnObj()
 229        gw_return_object.session = ct.c_size_t(session)
 230        gw_return_object.buffer = ct.c_void_p()
 231        gw_return_object.buffer_length = ct.c_size_t()
 232        gw_return_object.policy_format = ct.c_int(policy_format)
 233
 234        # API Call
 235        gw_return_object.status = self.library.GW2GetPolicySettings(
 236            gw_return_object.session,
 237            ct.byref(gw_return_object.buffer),
 238            ct.byref(gw_return_object.buffer_length),
 239            gw_return_object.policy_format
 240        )
 241
 242        # Editor wrote to a buffer, convert it to bytes
 243        policy_bytes = utils.buffer_to_bytes(
 244            gw_return_object.buffer,
 245            gw_return_object.buffer_length
 246        )
 247
 248        gw_return_object.policy = policy_bytes.decode()
 249
 250        return gw_return_object
 251
 252    def get_content_management_policy(self, session: int):
 253        """ Returns the content management configuration for a given session.
 254
 255        Args:
 256            session (int): The session integer.
 257
 258        Returns:
 259            xml_string (str): The XML string of the current content management configuration.
 260        """
 261        # NOTE GW2GetPolicySettings is current not implemented in editor
 262
 263        # set xml_string as loaded default config
 264        xml_string = glasswall.content_management.policies.Editor(default="sanitise").text,
 265
 266        # log.debug(f"xml_string:\n{xml_string}")
 267
 268        return xml_string
 269
 270        # # API function declaration
 271        # self.library.GW2GetPolicySettings.argtypes = [
 272        #     ct.c_size_t,
 273        #     ct.c_void_p,
 274        # ]
 275
 276        # # Variable initialisation
 277        # ct_session = ct.c_size_t(session)
 278        # ct_buffer = ct.c_void_p()
 279        # ct_buffer_length = ct.c_size_t()
 280        # # ct_file_format = ct.c_int(file_format)
 281
 282        # # API Call
 283        # status = self.library.GW2GetPolicySettings(
 284        #     ct_session,
 285        #     ct.byref(ct_buffer),
 286        #     ct.byref(ct_buffer_length)
 287        # )
 288
 289        # print("GW2GetPolicySettings status:", status)
 290
 291        # file_bytes = utils.buffer_to_bytes(
 292        #     ct_buffer,
 293        #     ct_buffer_length,
 294        # )
 295
 296        # return file_bytes
 297
 298    def _GW2RegisterPoliciesFile(self, session: int, input_file: str, policy_format: int = 0):
 299        """ Registers the policies to be used by Glasswall when processing files.
 300
 301        Args:
 302            session (int): The session integer.
 303            input_file (str): The content management policy input file path.
 304            policy_format (int): The format of the content management policy. 0=XML.
 305
 306        Returns:
 307            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'policy_format', 'status'.
 308        """
 309        # API function declaration
 310        self.library.GW2RegisterPoliciesFile.argtypes = [
 311            ct.c_size_t,  # Session_Handle session
 312            ct.c_char_p,  # const char *filename
 313            ct.c_int,  # Policy_Format format
 314        ]
 315
 316        # Variable initialisation
 317        gw_return_object = glasswall.GwReturnObj()
 318        gw_return_object.session = ct.c_size_t(session)
 319        gw_return_object.input_file = ct.c_char_p(input_file.encode("utf-8"))
 320        gw_return_object.policy_format = ct.c_int(policy_format)
 321
 322        gw_return_object.status = self.library.GW2RegisterPoliciesFile(
 323            gw_return_object.session,
 324            gw_return_object.input_file,
 325            gw_return_object.policy_format
 326        )
 327
 328        return gw_return_object
 329
 330    def _GW2RegisterPoliciesMemory(self, session: int, input_file: bytes, policy_format: int = 0):
 331        """ Registers the policies in memory to be used by Glasswall when processing files.
 332
 333        Args:
 334            session (int): The session integer.
 335            input_file (str): The content management policy input file bytes.
 336            policy_format (int): The format of the content management policy. 0=XML.
 337
 338        Returns:
 339            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'policy_format', 'status'.
 340        """
 341        # API function declaration
 342        self.library.GW2RegisterPoliciesMemory.argtype = [
 343            ct.c_size_t,  # Session_Handle session
 344            ct.c_char_p,  # const char *policies
 345            ct.c_size_t,  # size_t policiesLength
 346            ct.c_int  # Policy_Format format
 347        ]
 348
 349        # Variable initialisation
 350        gw_return_object = glasswall.GwReturnObj()
 351        gw_return_object.session = ct.c_size_t(session)
 352        gw_return_object.buffer = ct.c_char_p(input_file)
 353        gw_return_object.buffer_length = ct.c_size_t(len(input_file))
 354        gw_return_object.policy_format = ct.c_int(policy_format)
 355
 356        # API Call
 357        gw_return_object.status = self.library.GW2RegisterPoliciesMemory(
 358            gw_return_object.session,
 359            gw_return_object.buffer,
 360            gw_return_object.buffer_length,
 361            gw_return_object.policy_format
 362        )
 363
 364        return gw_return_object
 365
 366    def set_content_management_policy(self, session: int, input_file: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None, policy_format=0):
 367        """ Sets the content management policy configuration. If input_file is None then default settings (sanitise) are applied.
 368
 369        Args:
 370            session (int): The session integer.
 371            input_file (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
 372            policy_format (int): The format of the content management policy. 0=XML.
 373
 374        Returns:
 375            - result (glasswall.GwReturnObj): Depending on the input 'input_file':
 376                - If input_file is a str file path:
 377                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'policy_format', 'status'.
 378
 379                - If input_file is a file in memory:
 380                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'policy_format', 'status'.
 381        """
 382        # Validate type
 383        if not isinstance(session, int):
 384            raise TypeError(session)
 385        if not isinstance(input_file, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
 386            raise TypeError(input_file)
 387        if not isinstance(policy_format, int):
 388            raise TypeError(policy_format)
 389
 390        # Set input_file to default if input_file is None
 391        if input_file is None:
 392            input_file = glasswall.content_management.policies.Editor(default="sanitise")
 393
 394        # Validate xml content is parsable
 395        utils.validate_xml(input_file)
 396
 397        # From file
 398        if isinstance(input_file, str) and os.path.isfile(input_file):
 399            input_file = os.path.abspath(input_file)
 400
 401            result = self._GW2RegisterPoliciesFile(session, input_file)
 402
 403        # From memory
 404        elif isinstance(input_file, (str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
 405            # Convert bytearray, io.BytesIO to bytes
 406            if isinstance(input_file, (bytearray, io.BytesIO)):
 407                input_file = utils.as_bytes(input_file)
 408            # Convert string xml or Policy to bytes
 409            if isinstance(input_file, (str, glasswall.content_management.policies.policy.Policy)):
 410                input_file = input_file.encode("utf-8")
 411
 412            result = self._GW2RegisterPoliciesMemory(session, input_file)
 413
 414        if result.status not in successes.success_codes:
 415            log.error(format_object(result))
 416            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
 417        else:
 418            log.debug(format_object(result))
 419
 420        return result
 421
 422    def _GW2RegisterInputFile(self, session: int, input_file: str):
 423        """ Register an input file for the given session.
 424
 425        Args:
 426            session (int): The session integer.
 427            input_file (str): The input file path.
 428
 429        Returns:
 430            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'status'.
 431        """
 432        # API function declaration
 433        self.library.GW2RegisterInputFile.argtypes = [
 434            ct.c_size_t,  # Session_Handle session
 435            ct.c_char_p  # const char * inputFilePath
 436        ]
 437
 438        # Variable initialisation
 439        gw_return_object = glasswall.GwReturnObj()
 440        gw_return_object.session = ct.c_size_t(session)
 441        gw_return_object.input_file = ct.c_char_p(input_file.encode("utf-8"))
 442        gw_return_object.input_file_path = input_file
 443
 444        # API call
 445        gw_return_object.status = self.library.GW2RegisterInputFile(
 446            gw_return_object.session,
 447            gw_return_object.input_file
 448        )
 449
 450        return gw_return_object
 451
 452    def _GW2RegisterInputMemory(self, session: int, input_file: bytes):
 453        """ Register an input file in memory for the given session.
 454
 455        Args:
 456            session (int): The session integer.
 457            input_file (bytes): The input file in memory.
 458
 459        Returns:
 460            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
 461        """
 462        # API function declaration
 463        self.library.GW2RegisterInputMemory.argtypes = [
 464            ct.c_size_t,  # Session_Handle session
 465            ct.c_char_p,  # const char * inputFileBuffer
 466            ct.c_size_t,  # size_t inputLength
 467        ]
 468
 469        # Variable initialisation
 470        gw_return_object = glasswall.GwReturnObj()
 471        gw_return_object.session = ct.c_size_t(session)
 472        gw_return_object.buffer = ct.c_char_p(input_file)
 473        gw_return_object.buffer_length = ct.c_size_t(len(input_file))
 474
 475        # API call
 476        gw_return_object.status = self.library.GW2RegisterInputMemory(
 477            gw_return_object.session,
 478            gw_return_object.buffer,
 479            gw_return_object.buffer_length
 480        )
 481
 482        return gw_return_object
 483
 484    def register_input(self, session: int, input_file: Union[str, bytes, bytearray, io.BytesIO]):
 485        """ Register an input file or bytes for the given session.
 486
 487        Args:
 488            session (int): The session integer.
 489            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
 490
 491        Returns:
 492            - result (glasswall.GwReturnObj): Depending on the input 'input_file':
 493                - If input_file is a str file path:
 494                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'status'.
 495
 496                - If input_file is a file in memory:
 497                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
 498        """
 499        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO,)):
 500            raise TypeError(input_file)
 501
 502        if isinstance(input_file, str):
 503            if not os.path.isfile(input_file):
 504                raise FileNotFoundError(input_file)
 505
 506            input_file = os.path.abspath(input_file)
 507
 508            result = self._GW2RegisterInputFile(session, input_file)
 509
 510        elif isinstance(input_file, (bytes, bytearray, io.BytesIO,)):
 511            # Convert bytearray and io.BytesIO to bytes
 512            input_file = utils.as_bytes(input_file)
 513
 514            result = self._GW2RegisterInputMemory(session, input_file)
 515
 516        if result.status not in successes.success_codes:
 517            log.error(format_object(result))
 518            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
 519        else:
 520            log.debug(format_object(result))
 521
 522        return result
 523
 524    def _GW2RegisterOutFile(self, session: int, output_file: str):
 525        """ Register an output file for the given session.
 526
 527        Args:
 528            session (int): The session integer.
 529            output_file (str): The output file path.
 530
 531        Returns:
 532            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.
 533        """
 534        # API function declaration
 535        self.library.GW2RegisterOutFile.argtypes = [
 536            ct.c_size_t,  # Session_Handle session
 537            ct.c_char_p  # const char * outputFilePath
 538        ]
 539
 540        # Variable initialisation
 541        gw_return_object = glasswall.GwReturnObj()
 542        gw_return_object.session = ct.c_size_t(session)
 543        gw_return_object.output_file = ct.c_char_p(output_file.encode("utf-8"))
 544
 545        # API call
 546        gw_return_object.status = self.library.GW2RegisterOutFile(
 547            gw_return_object.session,
 548            gw_return_object.output_file
 549        )
 550
 551        return gw_return_object
 552
 553    def _GW2RegisterOutputMemory(self, session: int):
 554        """ Register an output file in memory for the given session.
 555
 556        Args:
 557            session (int): The session integer.
 558
 559        Returns:
 560            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
 561        """
 562        # API function declaration
 563        self.library.GW2RegisterOutputMemory.argtypes = [
 564            ct.c_size_t,  # Session_Handle session
 565            ct.POINTER(ct.c_void_p),  # char ** outputBuffer
 566            ct.POINTER(ct.c_size_t)  # size_t * outputLength
 567        ]
 568
 569        # Variable initialisation
 570        gw_return_object = glasswall.GwReturnObj()
 571        gw_return_object.session = ct.c_size_t(session)
 572        gw_return_object.buffer = ct.c_void_p()
 573        gw_return_object.buffer_length = ct.c_size_t()
 574
 575        # API call
 576        gw_return_object.status = self.library.GW2RegisterOutputMemory(
 577            gw_return_object.session,
 578            ct.byref(gw_return_object.buffer),
 579            ct.byref(gw_return_object.buffer_length)
 580        )
 581
 582        return gw_return_object
 583
 584    def register_output(self, session, output_file: Optional[str] = None):
 585        """ Register an output file for the given session. If output_file is None the file will be returned as 'buffer' and 'buffer_length' attributes.
 586
 587        Args:
 588            session (int): The session integer.
 589            output_file (Optional[str]): If specified, during run session the file will be written to output_file, otherwise the file will be written to the glasswall.GwReturnObj 'buffer' and 'buffer_length' attributes.
 590
 591        Returns:
 592            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attribute 'status' indicating the result of the function call. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size.
 593        """
 594        if not isinstance(output_file, (type(None), str)):
 595            raise TypeError(output_file)
 596
 597        if isinstance(output_file, str):
 598            output_file = os.path.abspath(output_file)
 599
 600            result = self._GW2RegisterOutFile(session, output_file)
 601
 602        elif isinstance(output_file, type(None)):
 603            result = self._GW2RegisterOutputMemory(session)
 604
 605        if result.status not in successes.success_codes:
 606            log.error(format_object(result))
 607            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
 608        else:
 609            log.debug(format_object(result))
 610
 611        return result
 612
 613    def _GW2RegisterAnalysisFile(self, session: int, output_file: str, analysis_format: int = 0):
 614        """ Register an analysis output file for the given session.
 615
 616        Args:
 617            session (int): The session integer.
 618            output_file (str): The analysis output file path.
 619            analysis_format (int): The format of the analysis report. 0=XML, 1=XMLExtended
 620
 621        Returns:
 622            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'analysis_format', 'status'.
 623        """
 624        # API function declaration
 625        self.library.GW2RegisterAnalysisFile.argtypes = [
 626            ct.c_size_t,  # Session_Handle session
 627            ct.c_char_p,  # const char * analysisFilePathName
 628            ct.c_int,  # Analysis_Format format
 629        ]
 630
 631        # Variable initialisation
 632        gw_return_object = glasswall.GwReturnObj()
 633        gw_return_object.session = ct.c_size_t(session)
 634        gw_return_object.output_file = ct.c_char_p(output_file.encode("utf-8"))
 635        gw_return_object.analysis_format = ct.c_int()
 636
 637        # API call
 638        gw_return_object.status = self.library.GW2RegisterAnalysisFile(
 639            gw_return_object.session,
 640            gw_return_object.output_file,
 641            gw_return_object.analysis_format
 642        )
 643
 644        return gw_return_object
 645
 646    def _GW2RegisterAnalysisMemory(self, session: int, analysis_format: int = 0):
 647        """ Register an analysis output file in memory for the given session.
 648
 649        Args:
 650            session (int): The session integer.
 651            analysis_format (int): The format of the analysis report. 0=XML, 1=XMLExtended
 652
 653        Returns:
 654            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'analysis_format', 'status'.
 655        """
 656        # API function declaration
 657        self.library.GW2RegisterAnalysisMemory.argtypes = [
 658            ct.c_size_t,  # Session_Handle session
 659            ct.POINTER(ct.c_void_p),  # char ** analysisFileBuffer
 660            ct.POINTER(ct.c_size_t),  # size_t * analysisoutputLength
 661            ct.c_int  # Analysis_Format format
 662        ]
 663
 664        # Variable initialisation
 665        gw_return_object = glasswall.GwReturnObj()
 666        gw_return_object.session = ct.c_size_t(session)
 667        gw_return_object.buffer = ct.c_void_p()
 668        gw_return_object.buffer_length = ct.c_size_t()
 669        gw_return_object.analysis_format = ct.c_int()
 670
 671        # API call
 672        gw_return_object.status = self.library.GW2RegisterAnalysisMemory(
 673            gw_return_object.session,
 674            ct.byref(gw_return_object.buffer),
 675            ct.byref(gw_return_object.buffer_length),
 676            gw_return_object.analysis_format
 677        )
 678
 679        return gw_return_object
 680
 681    def register_analysis(self, session: int, output_file: Optional[str] = None):
 682        """ Registers an analysis file for the given session. The analysis file will be created during the session's run_session call.
 683
 684        Args:
 685            session (int): The session integer.
 686            output_file (Optional[str]): Default None. The file path where the analysis will be written. None returns the analysis as bytes.
 687
 688        Returns:
 689            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'status', 'session', 'analysis_format'. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size. If output_file is not None (file mode) 'output_file' is included.
 690        """
 691        if not isinstance(output_file, (type(None), str)):
 692            raise TypeError(output_file)
 693
 694        if isinstance(output_file, str):
 695            output_file = os.path.abspath(output_file)
 696
 697            result = self._GW2RegisterAnalysisFile(session, output_file)
 698
 699        elif isinstance(output_file, type(None)):
 700            result = self._GW2RegisterAnalysisMemory(session)
 701
 702        if result.status not in successes.success_codes:
 703            log.error(format_object(result))
 704            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
 705        else:
 706            log.debug(format_object(result))
 707
 708        return result
 709
 710    def protect_file(
 711        self,
 712        input_file: Union[str, bytes, bytearray, io.BytesIO],
 713        output_file: Optional[str] = None,
 714        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
 715        raise_unsupported: bool = True,
 716    ):
 717        """ Protects a file using the current content management configuration, returning the file bytes. The protected file is written to output_file if it is provided.
 718
 719        Args:
 720            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
 721            output_file (Optional[str]): The output file path where the protected file will be written.
 722            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
 723            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
 724
 725        Returns:
 726            file_bytes (bytes): The protected file bytes.
 727        """
 728        # Validate arg types
 729        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
 730            raise TypeError(input_file)
 731        if not isinstance(output_file, (type(None), str)):
 732            raise TypeError(output_file)
 733        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
 734            raise TypeError(content_management_policy)
 735        if not isinstance(raise_unsupported, bool):
 736            raise TypeError(raise_unsupported)
 737
 738        # Convert string path arguments to absolute paths
 739        if isinstance(input_file, str):
 740            if not os.path.isfile(input_file):
 741                raise FileNotFoundError(input_file)
 742            input_file = os.path.abspath(input_file)
 743        if isinstance(output_file, str):
 744            output_file = os.path.abspath(output_file)
 745            # make directories that do not exist
 746            os.makedirs(os.path.dirname(output_file), exist_ok=True)
 747        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
 748            content_management_policy = os.path.abspath(content_management_policy)
 749
 750        # Convert memory inputs to bytes
 751        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
 752            input_file = utils.as_bytes(input_file)
 753
 754        with utils.CwdHandler(self.library_path):
 755            with self.new_session() as session:
 756                content_management_policy = self.set_content_management_policy(session, content_management_policy)
 757                register_input = self.register_input(session, input_file)
 758                register_output = self.register_output(session, output_file=output_file)
 759                status = self.run_session(session)
 760
 761                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
 762                if status not in successes.success_codes:
 763                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
 764                    if raise_unsupported:
 765                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
 766                    else:
 767                        file_bytes = None
 768                else:
 769                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
 770                    # Get file bytes
 771                    if isinstance(output_file, str):
 772                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
 773                        if not os.path.isfile(output_file):
 774                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
 775                            file_bytes = None
 776                        else:
 777                            with open(output_file, "rb") as f:
 778                                file_bytes = f.read()
 779                    else:
 780                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
 781                        file_bytes = utils.buffer_to_bytes(
 782                            register_output.buffer,
 783                            register_output.buffer_length
 784                        )
 785
 786                # Ensure memory allocated is not garbage collected
 787                content_management_policy, register_input, register_output
 788
 789                return file_bytes
 790
 791    def protect_directory(
 792        self,
 793        input_directory: str,
 794        output_directory: Optional[str] = None,
 795        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
 796        raise_unsupported: bool = True,
 797    ):
 798        """ Recursively processes all files in a directory in protect mode using the given content management policy.
 799        The protected files are written to output_directory maintaining the same directory structure as input_directory.
 800
 801        Args:
 802            input_directory (str): The input directory containing files to protect.
 803            output_directory (Optional[str]): The output directory where the protected file will be written, or None to not write files.
 804            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
 805            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
 806
 807        Returns:
 808            protected_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.
 809        """
 810        protected_files_dict = {}
 811        # Call protect_file on each file in input_directory to output_directory
 812        for input_file in utils.list_file_paths(input_directory):
 813            relative_path = os.path.relpath(input_file, input_directory)
 814            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
 815
 816            protected_bytes = self.protect_file(
 817                input_file=input_file,
 818                output_file=output_file,
 819                raise_unsupported=raise_unsupported,
 820                content_management_policy=content_management_policy,
 821            )
 822
 823            protected_files_dict[relative_path] = protected_bytes
 824
 825        return protected_files_dict
 826
 827    def analyse_file(
 828        self,
 829        input_file: Union[str, bytes, bytearray, io.BytesIO],
 830        output_file: Optional[str] = None,
 831        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
 832        raise_unsupported: bool = True,
 833    ):
 834        """ Analyses a file, returning the analysis bytes. The analysis is written to output_file if it is provided.
 835
 836        Args:
 837            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
 838            output_file (Optional[str]): The output file path where the analysis file will be written.
 839            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
 840            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
 841
 842        Returns:
 843            file_bytes (bytes): The analysis file bytes.
 844        """
 845        # Validate arg types
 846        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
 847            raise TypeError(input_file)
 848        if not isinstance(output_file, (type(None), str)):
 849            raise TypeError(output_file)
 850        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
 851            raise TypeError(content_management_policy)
 852        if not isinstance(raise_unsupported, bool):
 853            raise TypeError(raise_unsupported)
 854
 855        # Convert string path arguments to absolute paths
 856        if isinstance(input_file, str):
 857            if not os.path.isfile(input_file):
 858                raise FileNotFoundError(input_file)
 859            input_file = os.path.abspath(input_file)
 860        if isinstance(output_file, str):
 861            output_file = os.path.abspath(output_file)
 862            # make directories that do not exist
 863            os.makedirs(os.path.dirname(output_file), exist_ok=True)
 864        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
 865            content_management_policy = os.path.abspath(content_management_policy)
 866
 867        # Convert memory inputs to bytes
 868        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
 869            input_file = utils.as_bytes(input_file)
 870
 871        with utils.CwdHandler(self.library_path):
 872            with self.new_session() as session:
 873                content_management_policy = self.set_content_management_policy(session, content_management_policy)
 874                register_input = self.register_input(session, input_file)
 875                register_analysis = self.register_analysis(session, output_file)
 876                status = self.run_session(session)
 877
 878                file_bytes = None
 879                if isinstance(output_file, str):
 880                    # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
 881                    if os.path.isfile(output_file):
 882                        with open(output_file, "rb") as f:
 883                            file_bytes = f.read()
 884                else:
 885                    # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
 886                    if register_analysis.buffer and register_analysis.buffer_length:
 887                        file_bytes = utils.buffer_to_bytes(
 888                            register_analysis.buffer,
 889                            register_analysis.buffer_length
 890                        )
 891
 892                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
 893                if status not in successes.success_codes:
 894                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
 895                    if raise_unsupported:
 896                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
 897                else:
 898                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
 899
 900                # Ensure memory allocated is not garbage collected
 901                content_management_policy, register_input, register_analysis
 902
 903                return file_bytes
 904
 905    def analyse_directory(
 906        self,
 907        input_directory: str,
 908        output_directory: Optional[str] = None,
 909        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
 910        raise_unsupported: bool = True,
 911    ):
 912        """ Analyses all files in a directory and its subdirectories. The analysis files are written to output_directory maintaining the same directory structure as input_directory.
 913
 914        Args:
 915            input_directory (str): The input directory containing files to analyse.
 916            output_directory (Optional[str]): The output directory where the analysis files will be written, or None to not write files.
 917            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
 918            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
 919
 920        Returns:
 921            analysis_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.
 922        """
 923        analysis_files_dict = {}
 924        # Call analyse_file on each file in input_directory to output_directory
 925        for input_file in utils.list_file_paths(input_directory):
 926            relative_path = os.path.relpath(input_file, input_directory) + ".xml"
 927            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
 928
 929            analysis_bytes = self.analyse_file(
 930                input_file=input_file,
 931                output_file=output_file,
 932                raise_unsupported=raise_unsupported,
 933                content_management_policy=content_management_policy,
 934            )
 935
 936            analysis_files_dict[relative_path] = analysis_bytes
 937
 938        return analysis_files_dict
 939
 940    def protect_and_analyse_file(
 941        self,
 942        input_file: Union[str, bytes, bytearray, io.BytesIO],
 943        output_file: Optional[str] = None,
 944        output_analysis_report: Optional[str] = None,
 945        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
 946        raise_unsupported: bool = True,
 947    ):
 948        """ Protects and analyses a file in a single session, returning both protected file bytes and analysis report bytes.
 949
 950        Args:
 951            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
 952            output_file (Optional[str]): The output file path where the protected file will be written.
 953            output_analysis_report (Optional[str]): The output file path where the XML analysis report will be written.
 954            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
 955            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
 956
 957        Returns:
 958            Tuple[Optional[bytes], Optional[bytes]]: A tuple of (protected_file_bytes, analysis_report_bytes).
 959        """
 960        # Validate arg types
 961        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
 962            raise TypeError(f"input_file expected to be of type: str, bytes, bytearray, io.BytesIO. Got: {type(input_file).__name__}")
 963        if not isinstance(output_file, (type(None), str)):
 964            raise TypeError(f"output_file expected to be of type: None, str. Got: {type(output_file).__name__}")
 965        if not isinstance(output_analysis_report, (type(None), str)):
 966            raise TypeError(f"output_analysis_report expected to be of type: None, str. Got: {type(output_analysis_report).__name__}")
 967        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
 968            raise TypeError(f"content_management_policy expected to be of type: None, str, bytes, bytearray, io.BytesIO, Policy. Got: {type(content_management_policy).__name__}")
 969        if not isinstance(raise_unsupported, bool):
 970            raise TypeError(f"raise_unsupported expected to be of type: bool. Got: {type(raise_unsupported).__name__}")
 971
 972        # Convert string path arguments to absolute paths
 973        if isinstance(input_file, str):
 974            if not os.path.isfile(input_file):
 975                raise FileNotFoundError(input_file)
 976            input_file = os.path.abspath(input_file)
 977        if isinstance(output_file, str):
 978            output_file = os.path.abspath(output_file)
 979            os.makedirs(os.path.dirname(output_file), exist_ok=True)
 980        if isinstance(output_analysis_report, str):
 981            output_analysis_report = os.path.abspath(output_analysis_report)
 982            os.makedirs(os.path.dirname(output_analysis_report), exist_ok=True)
 983        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
 984            content_management_policy = os.path.abspath(content_management_policy)
 985
 986        # Convert memory inputs to bytes
 987        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
 988            input_file = utils.as_bytes(input_file)
 989
 990        with utils.CwdHandler(self.library_path):
 991            with self.new_session() as session:
 992                content_management_policy = self.set_content_management_policy(session, content_management_policy)
 993                register_input = self.register_input(session, input_file)
 994                register_output = self.register_output(session, output_file)
 995                register_analysis = self.register_analysis(session, output_analysis_report)
 996
 997                status = self.run_session(session)
 998
 999                protected_file_bytes = None
1000                analysis_report_bytes = None
1001
1002                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1003                if status not in successes.success_codes:
1004                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1005                    if raise_unsupported:
1006                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1007
1008                # Get analysis report file bytes, even on processing failure
1009                if isinstance(output_analysis_report, str):
1010                    # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1011                    if not os.path.isfile(output_analysis_report):
1012                        log.error(f"Editor returned success code: {status} but no output file was found: {output_analysis_report}")
1013                    else:
1014                        with open(output_analysis_report, "rb") as f:
1015                            analysis_report_bytes = f.read()
1016                else:
1017                    # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1018                    if register_analysis.buffer and register_analysis.buffer_length:
1019                        analysis_report_bytes = utils.buffer_to_bytes(
1020                            register_analysis.buffer,
1021                            register_analysis.buffer_length
1022                        )
1023
1024                # On success, get protected file bytes
1025                if status in successes.success_codes:
1026                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1027                    # Get file bytes
1028                    if isinstance(output_file, str):
1029                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1030                        if not os.path.isfile(output_file):
1031                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1032                        else:
1033                            with open(output_file, "rb") as f:
1034                                protected_file_bytes = f.read()
1035                    else:
1036                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1037                        protected_file_bytes = utils.buffer_to_bytes(
1038                            register_output.buffer,
1039                            register_output.buffer_length
1040                        )
1041
1042                # Ensure memory allocated is not garbage collected
1043                content_management_policy, register_input, register_output, register_analysis
1044
1045                return protected_file_bytes, analysis_report_bytes
1046
1047    def protect_and_analyse_directory(
1048        self,
1049        input_directory: str,
1050        output_directory: Optional[str] = None,
1051        analysis_directory: Optional[str] = None,
1052        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1053        raise_unsupported: bool = True,
1054    ):
1055        """ Recursively processes all files in a directory using protect and analyse mode with the given content management policy.
1056        Outputs are written to output_directory and analysis_directory maintaining the same structure as input_directory.
1057
1058        Args:
1059            input_directory (str): The input directory containing files to process.
1060            output_directory (Optional[str]): The output directory for protected files.
1061            analysis_directory (Optional[str]): The output directory for XML analysis reports.
1062            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply.
1063            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1064
1065        Returns:
1066            result_dict (dict): A dictionary mapping relative file paths to tuples of (protected_file_bytes, analysis_report_bytes).
1067        """
1068        result_dict = {}
1069        for input_file in utils.list_file_paths(input_directory):
1070            relative_path = os.path.relpath(input_file, input_directory)
1071            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
1072            output_analysis_report = None if analysis_directory is None else os.path.join(os.path.abspath(analysis_directory), relative_path + ".xml")
1073
1074            protected_file_bytes, analysis_report_bytes = self.protect_and_analyse_file(
1075                input_file=input_file,
1076                output_file=output_file,
1077                output_analysis_report=output_analysis_report,
1078                content_management_policy=content_management_policy,
1079                raise_unsupported=raise_unsupported,
1080            )
1081
1082            result_dict[relative_path] = (protected_file_bytes, analysis_report_bytes)
1083
1084        return result_dict
1085
1086    def _GW2RegisterExportFile(self, session: int, output_file: str):
1087        """ Register an export output file for the given session.
1088
1089        Args:
1090            session (int): The session integer.
1091            output_file (str): The export output file path.
1092
1093        Returns:
1094            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.
1095        """
1096        # API function declaration
1097        self.library.GW2RegisterExportFile.argtypes = [
1098            ct.c_size_t,  # Session_Handle session
1099            ct.c_char_p  # const char * exportFilePath
1100        ]
1101
1102        # Variable initialisation
1103        gw_return_object = glasswall.GwReturnObj()
1104        gw_return_object.session = ct.c_size_t(session)
1105        gw_return_object.output_file = ct.c_char_p(output_file.encode("utf-8"))
1106
1107        # API Call
1108        gw_return_object.status = self.library.GW2RegisterExportFile(
1109            gw_return_object.session,
1110            gw_return_object.output_file
1111        )
1112
1113        return gw_return_object
1114
1115    def _GW2RegisterExportMemory(self, session: int):
1116        """ Register an export output file in memory for the given session.
1117
1118        Args:
1119            session (int): The session integer.
1120
1121        Returns:
1122            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
1123        """
1124        # API function declaration
1125        self.library.GW2RegisterExportMemory.argtypes = [
1126            ct.c_size_t,  # Session_Handle session
1127            ct.POINTER(ct.c_void_p),  # char ** exportFileBuffer
1128            ct.POINTER(ct.c_size_t)  # size_t * exportLength
1129        ]
1130
1131        # Variable initialisation
1132        gw_return_object = glasswall.GwReturnObj()
1133        gw_return_object.session = ct.c_size_t(session)
1134        gw_return_object.buffer = ct.c_void_p()
1135        gw_return_object.buffer_length = ct.c_size_t()
1136
1137        # API call
1138        gw_return_object.status = self.library.GW2RegisterExportMemory(
1139            gw_return_object.session,
1140            ct.byref(gw_return_object.buffer),
1141            ct.byref(gw_return_object.buffer_length)
1142        )
1143
1144        return gw_return_object
1145
1146    def register_export(self, session: int, output_file: Optional[str] = None):
1147        """ Registers a file to be exported for the given session. The export file will be created during the session's run_session call.
1148
1149        Args:
1150            session (int): The session integer.
1151            output_file (Optional[str]): Default None. The file path where the export will be written. None exports the file in memory.
1152
1153        Returns:
1154            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attribute 'status' indicating the result of the function call and 'session', the session integer. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size.
1155        """
1156        if not isinstance(output_file, (type(None), str)):
1157            raise TypeError(output_file)
1158
1159        if isinstance(output_file, str):
1160            output_file = os.path.abspath(output_file)
1161
1162            result = self._GW2RegisterExportFile(session, output_file)
1163
1164        elif isinstance(output_file, type(None)):
1165            result = self._GW2RegisterExportMemory(session)
1166
1167        if result.status not in successes.success_codes:
1168            log.error(format_object(result))
1169            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
1170        else:
1171            log.debug(format_object(result))
1172
1173        return result
1174
1175    def export_file(
1176        self,
1177        input_file: Union[str, bytes, bytearray, io.BytesIO],
1178        output_file: Optional[str] = None,
1179        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1180        raise_unsupported: bool = True,
1181    ):
1182        """ Export a file, returning the .zip file bytes. The .zip file is written to output_file if it is provided.
1183
1184        Args:
1185            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
1186            output_file (Optional[str]): The output file path where the .zip file will be written.
1187            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
1188            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1189
1190        Returns:
1191            file_bytes (bytes): The exported .zip file.
1192        """
1193        # Validate arg types
1194        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
1195            raise TypeError(input_file)
1196        if not isinstance(output_file, (type(None), str)):
1197            raise TypeError(output_file)
1198        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
1199            raise TypeError(content_management_policy)
1200        if not isinstance(raise_unsupported, bool):
1201            raise TypeError(raise_unsupported)
1202
1203        # Convert string path arguments to absolute paths
1204        if isinstance(input_file, str):
1205            if not os.path.isfile(input_file):
1206                raise FileNotFoundError(input_file)
1207            input_file = os.path.abspath(input_file)
1208        if isinstance(output_file, str):
1209            output_file = os.path.abspath(output_file)
1210            # make directories that do not exist
1211            os.makedirs(os.path.dirname(output_file), exist_ok=True)
1212        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
1213            content_management_policy = os.path.abspath(content_management_policy)
1214
1215        # Convert memory inputs to bytes
1216        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
1217            input_file = utils.as_bytes(input_file)
1218
1219        with utils.CwdHandler(self.library_path):
1220            with self.new_session() as session:
1221                content_management_policy = self.set_content_management_policy(session, content_management_policy)
1222                register_input = self.register_input(session, input_file)
1223                register_export = self.register_export(session, output_file)
1224                status = self.run_session(session)
1225
1226                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1227                if status not in successes.success_codes:
1228                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
1229                    if raise_unsupported:
1230                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1231                    else:
1232                        file_bytes = None
1233                else:
1234                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
1235                    # Get file bytes
1236                    if isinstance(output_file, str):
1237                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1238                        if not os.path.isfile(output_file):
1239                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1240                            file_bytes = None
1241                        else:
1242                            with open(output_file, "rb") as f:
1243                                file_bytes = f.read()
1244                    else:
1245                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1246                        file_bytes = utils.buffer_to_bytes(
1247                            register_export.buffer,
1248                            register_export.buffer_length
1249                        )
1250
1251                # Ensure memory allocated is not garbage collected
1252                content_management_policy, register_input, register_export
1253
1254                return file_bytes
1255
1256    def export_directory(
1257        self,
1258        input_directory: str,
1259        output_directory: Optional[str] = None,
1260        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1261        raise_unsupported: bool = True
1262    ):
1263        """ Exports all files in a directory and its subdirectories. The export files are written to output_directory maintaining the same directory structure as input_directory.
1264
1265        Args:
1266            input_directory (str): The input directory containing files to export.
1267            output_directory (Optional[str]): The output directory where the export files will be written, or None to not write files.
1268            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
1269            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1270
1271        Returns:
1272            export_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.
1273        """
1274        export_files_dict = {}
1275        # Call export_file on each file in input_directory to output_directory
1276        for input_file in utils.list_file_paths(input_directory):
1277            relative_path = os.path.relpath(input_file, input_directory) + ".zip"
1278            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
1279
1280            export_bytes = self.export_file(
1281                input_file=input_file,
1282                output_file=output_file,
1283                raise_unsupported=raise_unsupported,
1284                content_management_policy=content_management_policy,
1285            )
1286
1287            export_files_dict[relative_path] = export_bytes
1288
1289        return export_files_dict
1290
1291    def export_and_analyse_file(
1292        self,
1293        input_file: Union[str, bytes, bytearray, io.BytesIO],
1294        output_file: Optional[str] = None,
1295        output_analysis_report: Optional[str] = None,
1296        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1297        raise_unsupported: bool = True,
1298    ):
1299        """ Exports and analyses a file in a single session, returning both exported .zip bytes and analysis report bytes.
1300
1301        Args:
1302            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
1303            output_file (Optional[str]): The output file path where the .zip export will be written.
1304            output_analysis_report (Optional[str]): The output file path where the XML analysis report will be written.
1305            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
1306            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1307
1308        Returns:
1309            Tuple[Optional[bytes], Optional[bytes]]: A tuple of (export_file_bytes, analysis_report_bytes).
1310        """
1311        # Validate arg types
1312        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
1313            raise TypeError(f"input_file expected to be of type: str, bytes, bytearray, io.BytesIO. Got: {type(input_file).__name__}")
1314        if not isinstance(output_file, (type(None), str)):
1315            raise TypeError(f"output_file expected to be of type: None, str. Got: {type(output_file).__name__}")
1316        if not isinstance(output_analysis_report, (type(None), str)):
1317            raise TypeError(f"output_analysis_report expected to be of type: None, str. Got: {type(output_analysis_report).__name__}")
1318        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
1319            raise TypeError(f"content_management_policy expected to be of type: None, str, bytes, bytearray, io.BytesIO, Policy. Got: {type(content_management_policy).__name__}")
1320        if not isinstance(raise_unsupported, bool):
1321            raise TypeError(f"raise_unsupported expected to be of type: bool. Got: {type(raise_unsupported).__name__}")
1322
1323        # Convert string path arguments to absolute paths
1324        if isinstance(input_file, str):
1325            if not os.path.isfile(input_file):
1326                raise FileNotFoundError(input_file)
1327            input_file = os.path.abspath(input_file)
1328        if isinstance(output_file, str):
1329            output_file = os.path.abspath(output_file)
1330            os.makedirs(os.path.dirname(output_file), exist_ok=True)
1331        if isinstance(output_analysis_report, str):
1332            output_analysis_report = os.path.abspath(output_analysis_report)
1333            os.makedirs(os.path.dirname(output_analysis_report), exist_ok=True)
1334        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
1335            content_management_policy = os.path.abspath(content_management_policy)
1336
1337        # Convert memory inputs to bytes
1338        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
1339            input_file = utils.as_bytes(input_file)
1340
1341        with utils.CwdHandler(self.library_path):
1342            with self.new_session() as session:
1343                content_management_policy = self.set_content_management_policy(session, content_management_policy)
1344                register_input = self.register_input(session, input_file)
1345                register_export = self.register_export(session, output_file)
1346                register_analysis = self.register_analysis(session, output_analysis_report)
1347
1348                status = self.run_session(session)
1349
1350                export_file_bytes = None
1351                analysis_report_bytes = None
1352
1353                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1354                if status not in successes.success_codes:
1355                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1356                    if raise_unsupported:
1357                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1358
1359                # Get analysis report file bytes, even on processing failure
1360                if isinstance(output_analysis_report, str):
1361                    # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1362                    if not os.path.isfile(output_analysis_report):
1363                        log.error(f"Editor returned success code: {status} but no output file was found: {output_analysis_report}")
1364                    else:
1365                        with open(output_analysis_report, "rb") as f:
1366                            analysis_report_bytes = f.read()
1367                else:
1368                    # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1369                    if register_analysis.buffer and register_analysis.buffer_length:
1370                        analysis_report_bytes = utils.buffer_to_bytes(
1371                            register_analysis.buffer,
1372                            register_analysis.buffer_length
1373                        )
1374
1375                # On success, get export file bytes
1376                if status in successes.success_codes:
1377                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1378                    # Get export file bytes
1379                    if isinstance(output_file, str):
1380                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1381                        if not os.path.isfile(output_file):
1382                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1383                        else:
1384                            with open(output_file, "rb") as f:
1385                                export_file_bytes = f.read()
1386                    else:
1387                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1388                        export_file_bytes = utils.buffer_to_bytes(
1389                            register_export.buffer,
1390                            register_export.buffer_length
1391                        )
1392
1393                # Ensure memory allocated is not garbage collected
1394                content_management_policy, register_input, register_export, register_analysis
1395
1396                return export_file_bytes, analysis_report_bytes
1397
1398    def export_and_analyse_directory(
1399        self,
1400        input_directory: str,
1401        output_directory: Optional[str] = None,
1402        analysis_directory: Optional[str] = None,
1403        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1404        raise_unsupported: bool = True,
1405    ):
1406        """ Recursively processes all files in a directory using export and analyse mode with the given content management policy.
1407        Outputs are written to output_directory and analysis_directory maintaining the same structure as input_directory.
1408
1409        Args:
1410            input_directory (str): The input directory containing files to process.
1411            output_directory (Optional[str]): The output directory for exported .zip files.
1412            analysis_directory (Optional[str]): The output directory for XML analysis reports.
1413            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply.
1414            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1415
1416        Returns:
1417            result_dict (dict): A dictionary mapping relative file paths to tuples of (export_file_bytes, analysis_report_bytes).
1418        """
1419        result_dict = {}
1420
1421        for input_file in utils.list_file_paths(input_directory):
1422            relative_path = os.path.relpath(input_file, input_directory)
1423            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path + ".zip")
1424            output_analysis_report = None if analysis_directory is None else os.path.join(os.path.abspath(analysis_directory), relative_path + ".xml")
1425
1426            export_file_bytes, analysis_report_bytes = self.export_and_analyse_file(
1427                input_file=input_file,
1428                output_file=output_file,
1429                output_analysis_report=output_analysis_report,
1430                content_management_policy=content_management_policy,
1431                raise_unsupported=raise_unsupported,
1432            )
1433
1434            result_dict[relative_path] = (export_file_bytes, analysis_report_bytes)
1435
1436        return result_dict
1437
1438    def _GW2RegisterImportFile(self, session: int, input_file: str):
1439        """ Register an import input file for the given session.
1440
1441        Args:
1442            session (int): The session integer.
1443            input_file (str): The input import file path.
1444
1445        Returns:
1446            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.
1447        """
1448        # API function declaration
1449        self.library.GW2RegisterImportFile.argtypes = [
1450            ct.c_size_t,  # Session_Handle session
1451            ct.c_char_p  # const char * importFilePath
1452        ]
1453
1454        # Variable initialisation
1455        gw_return_object = glasswall.GwReturnObj()
1456        gw_return_object.session = ct.c_size_t(session)
1457        gw_return_object.input_file = ct.c_char_p(input_file.encode("utf-8"))
1458
1459        # API Call
1460        gw_return_object.status = self.library.GW2RegisterImportFile(
1461            gw_return_object.session,
1462            gw_return_object.input_file
1463        )
1464
1465        return gw_return_object
1466
1467    def _GW2RegisterImportMemory(self, session: int, input_file: bytes):
1468        """ Register an import input file in memory for the given session.
1469
1470        Args:
1471            session (int): The session integer.
1472            input_file (str): The input import file in memory.
1473
1474        Returns:
1475            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
1476        """
1477        # API function declaration
1478        self.library.GW2RegisterImportMemory.argtypes = [
1479            ct.c_size_t,  # Session_Handle session
1480            ct.c_void_p,  # char * importFileBuffer
1481            ct.c_size_t  # size_t importLength
1482        ]
1483
1484        # Variable initialisation
1485        gw_return_object = glasswall.GwReturnObj()
1486        gw_return_object.session = ct.c_size_t(session)
1487        gw_return_object.buffer = ct.c_char_p(input_file)
1488        gw_return_object.buffer_length = ct.c_size_t(len(input_file))
1489
1490        # API call
1491        gw_return_object.status = self.library.GW2RegisterImportMemory(
1492            gw_return_object.session,
1493            gw_return_object.buffer,
1494            gw_return_object.buffer_length
1495        )
1496
1497        return gw_return_object
1498
1499    def register_import(self, session: int, input_file: Union[str, bytes, bytearray, io.BytesIO]):
1500        """ Registers a .zip file to be imported for the given session. The constructed file will be created during the session's run_session call.
1501
1502        Args:
1503            session (int): The session integer.
1504            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input import file path or bytes.
1505
1506        Returns:
1507            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attribute 'status' indicating the result of the function call. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size.
1508        """
1509        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO,)):
1510            raise TypeError(input_file)
1511
1512        if isinstance(input_file, str):
1513            if not os.path.isfile(input_file):
1514                raise FileNotFoundError(input_file)
1515
1516            input_file = os.path.abspath(input_file)
1517
1518            result = self._GW2RegisterImportFile(session, input_file)
1519
1520        elif isinstance(input_file, (bytes, bytearray, io.BytesIO,)):
1521            # Convert bytearray and io.BytesIO to bytes
1522            input_file = utils.as_bytes(input_file)
1523
1524            result = self._GW2RegisterImportMemory(session, input_file)
1525
1526        if result.status not in successes.success_codes:
1527            log.error(format_object(result))
1528            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
1529        else:
1530            log.debug(format_object(result))
1531
1532        return result
1533
1534    def import_file(
1535        self,
1536        input_file: Union[str, bytes, bytearray, io.BytesIO],
1537        output_file: Optional[str] = None,
1538        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1539        raise_unsupported: bool = True,
1540    ):
1541        """ Import a .zip file, constructs a file from the .zip file and returns the file bytes. The file is written to output_file if it is provided.
1542
1543        Args:
1544            input_file (Union[str, bytes, bytearray, io.BytesIO]): The .zip input file path or bytes.
1545            output_file (Optional[str]): The output file path where the constructed file will be written.
1546            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
1547            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1548
1549        Returns:
1550            file_bytes (bytes): The imported file bytes.
1551        """
1552        # Validate arg types
1553        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
1554            raise TypeError(input_file)
1555        if not isinstance(output_file, (type(None), str)):
1556            raise TypeError(output_file)
1557        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
1558            raise TypeError(content_management_policy)
1559        if not isinstance(raise_unsupported, bool):
1560            raise TypeError(raise_unsupported)
1561
1562        # Convert string path arguments to absolute paths
1563        if isinstance(input_file, str):
1564            if not os.path.isfile(input_file):
1565                raise FileNotFoundError(input_file)
1566            input_file = os.path.abspath(input_file)
1567        if isinstance(output_file, str):
1568            output_file = os.path.abspath(output_file)
1569            # make directories that do not exist
1570            os.makedirs(os.path.dirname(output_file), exist_ok=True)
1571        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
1572            content_management_policy = os.path.abspath(content_management_policy)
1573
1574        # Convert memory inputs to bytes
1575        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
1576            input_file = utils.as_bytes(input_file)
1577
1578        with utils.CwdHandler(self.library_path):
1579            with self.new_session() as session:
1580                content_management_policy = self.set_content_management_policy(session, content_management_policy)
1581                register_import = self.register_import(session, input_file)
1582                register_output = self.register_output(session, output_file)
1583                status = self.run_session(session)
1584
1585                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1586                if status not in successes.success_codes:
1587                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
1588                    if raise_unsupported:
1589                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1590                    else:
1591                        file_bytes = None
1592                else:
1593                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
1594                    # Get file bytes
1595                    if isinstance(output_file, str):
1596                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1597                        if not os.path.isfile(output_file):
1598                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1599                            file_bytes = None
1600                        else:
1601                            with open(output_file, "rb") as f:
1602                                file_bytes = f.read()
1603                    else:
1604                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1605                        file_bytes = utils.buffer_to_bytes(
1606                            register_output.buffer,
1607                            register_output.buffer_length
1608                        )
1609
1610                # Ensure memory allocated is not garbage collected
1611                content_management_policy, register_import, register_output
1612
1613                return file_bytes
1614
1615    def import_directory(
1616        self,
1617        input_directory: str,
1618        output_directory: Optional[str] = None,
1619        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1620        raise_unsupported: bool = True,
1621    ):
1622        """ Imports all files in a directory and its subdirectories. Files are expected as .zip but this is not forced.
1623        The constructed files are written to output_directory maintaining the same directory structure as input_directory.
1624
1625        Args:
1626            input_directory (str): The input directory containing files to import.
1627            output_directory (Optional[str]): The output directory where the constructed files will be written, or None to not write files.
1628            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
1629            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1630
1631        Returns:
1632            import_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.
1633        """
1634        import_files_dict = {}
1635        # Call import_file on each file in input_directory to output_directory
1636        for input_file in utils.list_file_paths(input_directory):
1637            relative_path = os.path.relpath(input_file, input_directory)
1638            # Remove .zip extension from relative_path
1639            if relative_path.endswith(".zip"):
1640                relative_path = os.path.splitext(relative_path)[0]
1641            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
1642
1643            import_bytes = self.import_file(
1644                input_file=input_file,
1645                output_file=output_file,
1646                raise_unsupported=raise_unsupported,
1647                content_management_policy=content_management_policy,
1648            )
1649
1650            import_files_dict[relative_path] = import_bytes
1651
1652        return import_files_dict
1653
1654    def import_and_analyse_file(
1655        self,
1656        input_file: Union[str, bytes, bytearray, io.BytesIO],
1657        output_file: Optional[str] = None,
1658        output_analysis_report: Optional[str] = None,
1659        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1660        raise_unsupported: bool = True,
1661    ):
1662        """ Imports and analyses a file in a single session, returning both imported file bytes and analysis report bytes.
1663
1664        Args:
1665            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
1666            output_file (Optional[str]): The output file path where the imported file will be written.
1667            output_analysis_report (Optional[str]): The output file path where the XML analysis report will be written.
1668            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
1669            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1670
1671        Returns:
1672            Tuple[Optional[bytes], Optional[bytes]]: A tuple of (import_file_bytes, analysis_report_bytes).
1673        """
1674        # Validate arg types
1675        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
1676            raise TypeError(f"input_file expected to be of type: str, bytes, bytearray, io.BytesIO. Got: {type(input_file).__name__}")
1677        if not isinstance(output_file, (type(None), str)):
1678            raise TypeError(f"output_file expected to be of type: None, str. Got: {type(output_file).__name__}")
1679        if not isinstance(output_analysis_report, (type(None), str)):
1680            raise TypeError(f"output_analysis_report expected to be of type: None, str. Got: {type(output_analysis_report).__name__}")
1681        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
1682            raise TypeError(f"content_management_policy expected to be of type: None, str, bytes, bytearray, io.BytesIO, Policy. Got: {type(content_management_policy).__name__}")
1683        if not isinstance(raise_unsupported, bool):
1684            raise TypeError(f"raise_unsupported expected to be of type: bool. Got: {type(raise_unsupported).__name__}")
1685
1686        # Convert string path arguments to absolute paths
1687        if isinstance(input_file, str):
1688            if not os.path.isfile(input_file):
1689                raise FileNotFoundError(input_file)
1690            input_file = os.path.abspath(input_file)
1691        if isinstance(output_file, str):
1692            output_file = os.path.abspath(output_file)
1693            os.makedirs(os.path.dirname(output_file), exist_ok=True)
1694        if isinstance(output_analysis_report, str):
1695            output_analysis_report = os.path.abspath(output_analysis_report)
1696            os.makedirs(os.path.dirname(output_analysis_report), exist_ok=True)
1697        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
1698            content_management_policy = os.path.abspath(content_management_policy)
1699
1700        # Convert memory inputs to bytes
1701        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
1702            input_file = utils.as_bytes(input_file)
1703
1704        with utils.CwdHandler(self.library_path):
1705            with self.new_session() as session:
1706                content_management_policy = self.set_content_management_policy(session, content_management_policy)
1707                register_import = self.register_import(session, input_file)
1708                register_output = self.register_output(session, output_file)
1709                register_analysis = self.register_analysis(session, output_analysis_report)
1710
1711                status = self.run_session(session)
1712
1713                import_file_bytes = None
1714                analysis_report_bytes = None
1715
1716                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1717                if status not in successes.success_codes:
1718                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1719                    if raise_unsupported:
1720                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1721
1722                # Get analysis report file bytes, even on processing failure
1723                if isinstance(output_analysis_report, str):
1724                    # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1725                    if not os.path.isfile(output_analysis_report):
1726                        log.error(f"Editor returned success code: {status} but no output file was found: {output_analysis_report}")
1727                    else:
1728                        with open(output_analysis_report, "rb") as f:
1729                            analysis_report_bytes = f.read()
1730                else:
1731                    # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1732                    if register_analysis.buffer and register_analysis.buffer_length:
1733                        analysis_report_bytes = utils.buffer_to_bytes(
1734                            register_analysis.buffer,
1735                            register_analysis.buffer_length
1736                        )
1737
1738                # On success, get import file bytes
1739                if status in successes.success_codes:
1740                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1741                    # Get import file bytes
1742                    if isinstance(output_file, str):
1743                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1744                        if not os.path.isfile(output_file):
1745                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1746                        else:
1747                            with open(output_file, "rb") as f:
1748                                import_file_bytes = f.read()
1749                    else:
1750                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1751                        import_file_bytes = utils.buffer_to_bytes(
1752                            register_import.buffer,
1753                            register_import.buffer_length
1754                        )
1755
1756                # Ensure memory allocated is not garbage collected
1757                content_management_policy, register_import, register_output, register_analysis
1758
1759                return import_file_bytes, analysis_report_bytes
1760
1761    def import_and_analyse_directory(
1762        self,
1763        input_directory: str,
1764        output_directory: Optional[str] = None,
1765        analysis_directory: Optional[str] = None,
1766        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1767        raise_unsupported: bool = True,
1768    ):
1769        """ Recursively processes all files in a directory using import and analyse mode with the given content management policy.
1770        Outputs are written to output_directory and analysis_directory maintaining the same structure as input_directory.
1771
1772        Args:
1773            input_directory (str): The input directory containing export .zip files to process.
1774            output_directory (Optional[str]): The output directory for imported files.
1775            analysis_directory (Optional[str]): The output directory for XML analysis reports.
1776            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply.
1777            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1778
1779        Returns:
1780            result_dict (dict): A dictionary mapping relative file paths to tuples of (import_file_bytes, analysis_report_bytes).
1781        """
1782        result_dict = {}
1783
1784        for input_file in utils.list_file_paths(input_directory):
1785            relative_path = os.path.relpath(input_file, input_directory)
1786            # Remove .zip extension from relative_path
1787            if relative_path.endswith(".zip"):
1788                relative_path = os.path.splitext(relative_path)[0]
1789            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
1790            output_analysis_report = None if analysis_directory is None else os.path.join(os.path.abspath(analysis_directory), relative_path + ".xml")
1791
1792            import_file_bytes, analysis_report_bytes = self.import_and_analyse_file(
1793                input_file=input_file,
1794                output_file=output_file,
1795                output_analysis_report=output_analysis_report,
1796                content_management_policy=content_management_policy,
1797                raise_unsupported=raise_unsupported,
1798            )
1799
1800            result_dict[relative_path] = (import_file_bytes, analysis_report_bytes)
1801
1802        return result_dict
1803
1804    def _GW2FileErrorMsg(self, session: int):
1805        """ Retrieve the Glasswall Session Process error message.
1806
1807        Args:
1808            session (int): The session integer.
1809
1810        Returns:
1811            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status', 'error_message'.
1812        """
1813        # API function declaration
1814        self.library.GW2FileErrorMsg.argtypes = [
1815            ct.c_size_t,  # Session_Handle session
1816            ct.POINTER(ct.c_void_p),  # char **errorMsgBuffer
1817            ct.POINTER(ct.c_size_t)  # size_t *errorMsgBufferLength
1818        ]
1819
1820        # Variable initialisation
1821        gw_return_object = glasswall.GwReturnObj()
1822        gw_return_object.session = ct.c_size_t(session)
1823        gw_return_object.buffer = ct.c_void_p()
1824        gw_return_object.buffer_length = ct.c_size_t()
1825
1826        # API call
1827        gw_return_object.status = self.library.GW2FileErrorMsg(
1828            gw_return_object.session,
1829            ct.byref(gw_return_object.buffer),
1830            ct.byref(gw_return_object.buffer_length)
1831        )
1832
1833        # Editor wrote to a buffer, convert it to bytes
1834        error_bytes = utils.buffer_to_bytes(
1835            gw_return_object.buffer,
1836            gw_return_object.buffer_length
1837        )
1838
1839        gw_return_object.error_message = error_bytes.decode()
1840
1841        return gw_return_object
1842
1843    @functools.lru_cache()
1844    def file_error_message(self, session: int) -> str:
1845        """ Retrieve the Glasswall Session Process error message.
1846
1847        Args:
1848            session (int): The session integer.
1849
1850        Returns:
1851            error_message (str): The Glasswall Session Process error message.
1852        """
1853        # Validate arg types
1854        if not isinstance(session, int):
1855            raise TypeError(session)
1856
1857        result = self._GW2FileErrorMsg(session)
1858
1859        if result.status not in successes.success_codes:
1860            log.error(format_object(result))
1861            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
1862        else:
1863            log.debug(format_object(result))
1864
1865        return result.error_message
1866
1867    def GW2GetFileType(self, session: int, file_type_id):
1868        """ Retrieve the file type as a string.
1869
1870        Args:
1871            session (int): The session integer.
1872            file_type_id (int): The file type id.
1873
1874        Returns:
1875            file_type (str): The formal file name for the corresponding file id.
1876        """
1877        # Validate arg types
1878        if not isinstance(session, int):
1879            raise TypeError(session)
1880
1881        # API function declaration
1882        self.library.GW2GetFileType.argtypes = [
1883            ct.c_size_t,
1884            ct.c_size_t,
1885            ct.POINTER(ct.c_size_t),
1886            ct.POINTER(ct.c_void_p)
1887        ]
1888
1889        # Variable initialisation
1890        ct_session = ct.c_size_t(session)
1891        ct_file_type = ct.c_size_t(file_type_id)
1892        ct_buffer_length = ct.c_size_t()
1893        ct_buffer = ct.c_void_p()
1894
1895        # API call
1896        status = self.library.GW2GetFileType(
1897            ct_session,
1898            ct_file_type,
1899            ct.byref(ct_buffer_length),
1900            ct.byref(ct_buffer)
1901        )
1902
1903        if status not in successes.success_codes:
1904            log.error(f"\n\tsession: {session}\n\tstatus: {status}")
1905            raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1906        else:
1907            log.debug(f"\n\tsession: {session}\n\tstatus: {status}")
1908
1909        # Editor wrote to a buffer, convert it to bytes
1910        file_type_bytes = utils.buffer_to_bytes(
1911            ct_buffer,
1912            ct_buffer_length
1913        )
1914
1915        file_type = file_type_bytes.decode()
1916
1917        return file_type
1918
1919    def GW2GetFileTypeID(self, session: int, file_type_str):
1920        """ Retrieve the Glasswall file type id given a file type string.
1921
1922        Args:
1923            session (int): The session integer.
1924            file_type_str (str): The file type as a string.
1925
1926        Returns:
1927            file_type_id (str): The Glasswall file type id for the specified file type.
1928        """
1929        # Validate arg types
1930        if not isinstance(session, int):
1931            raise TypeError(session)
1932
1933        # API function declaration
1934        self.library.GW2GetFileTypeID.argtypes = [
1935            ct.c_size_t,
1936            ct.c_char_p,
1937            ct.POINTER(ct.c_size_t),
1938            ct.POINTER(ct.c_void_p)
1939        ]
1940
1941        # Variable initialisation
1942        ct_session = ct.c_size_t(session)
1943        ct_file_type = ct.c_char_p(file_type_str.encode('utf-8'))
1944        ct_buffer_length = ct.c_size_t()
1945        ct_buffer = ct.c_void_p()
1946
1947        # API call
1948        status = self.library.GW2GetFileTypeID(
1949            ct_session,
1950            ct_file_type,
1951            ct.byref(ct_buffer_length),
1952            ct.byref(ct_buffer)
1953        )
1954
1955        if status not in successes.success_codes:
1956            log.error(f"\n\tsession: {session}\n\tstatus: {status}")
1957            raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1958        else:
1959            log.debug(f"\n\tsession: {session}\n\tstatus: {status}")
1960
1961        # Editor wrote to a buffer, convert it to bytes
1962        file_type_bytes = utils.buffer_to_bytes(
1963            ct_buffer,
1964            ct_buffer_length
1965        )
1966
1967        file_type_id = file_type_bytes.decode()
1968
1969        return file_type_id
1970
1971    def get_file_type_info(self, file_type: Union[str, int]):
1972        """ Retrieve information about a file type based on its identifier.
1973
1974        Args:
1975            file_type (Union[str, int]): The file type identifier. This can be either a string representing a file
1976            extension (e.g. 'bmp') or an integer corresponding to a file type (e.g. 29).
1977
1978        Returns:
1979            - file_type_info (Union[int, str]): Depending on the input 'file_type':
1980                - If `file_type` is a string (e.g. 'bmp'):
1981                    - If the file type is recognised, returns an integer corresponding to that file type.
1982                    - If the file type is not recognised, returns 0.
1983                - If `file_type` is an integer (e.g. 29):
1984                    - If the integer corresponds to a recognised file type, returns a more detailed string description
1985                        of the file type (e.g. 'BMP Image').
1986                    - If the integer does not match any recognised file type, returns an empty string.
1987        """
1988        # Validate arg types
1989        if not isinstance(file_type, (str, int)):
1990            raise TypeError(file_type)
1991
1992        with utils.CwdHandler(self.library_path):
1993            with self.new_session() as session:
1994
1995                if isinstance(file_type, int):
1996                    file_type_info = self.GW2GetFileType(session, file_type)
1997                if isinstance(file_type, str):
1998                    file_type_info = self.GW2GetFileTypeID(session, file_type)
1999
2000                return file_type_info
2001
2002    @utils.deprecated_function(replacement_function=get_file_type_info)
2003    def get_file_info(self, *args, **kwargs):
2004        """ Deprecated in 1.0.6. Use get_file_type_info. """
2005        pass
2006
2007    def _GW2RegisterReportFile(self, session: int, output_file: str):
2008        """ Register an output report file path for the given session.
2009
2010        Args:
2011            session (int): The session integer.
2012            output_file (str): The file path of the output report file.
2013
2014        Returns:
2015            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.
2016        """
2017        # API function declaration
2018        self.library.GW2RegisterReportFile.argtypes = [
2019            ct.c_size_t,  # Session_Handle session
2020            ct.c_char_p,  # const char * reportFilePathName
2021        ]
2022
2023        # Variable initialisation
2024        gw_return_object = glasswall.GwReturnObj()
2025        gw_return_object.session = ct.c_size_t(session)
2026        gw_return_object.output_file = ct.c_char_p(output_file.encode("utf-8"))
2027
2028        # API call
2029        gw_return_object.status = self.library.GW2RegisterReportFile(
2030            gw_return_object.session,
2031            gw_return_object.output_file
2032        )
2033
2034        return gw_return_object
2035
2036    def register_report_file(self, session: int, output_file: str):
2037        """ Register the report file path for the given session.
2038
2039        Args:
2040            session (int): The session integer.
2041            output_file (str): The file path of the report file.
2042
2043        Returns:
2044            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.
2045        """
2046        # Validate arg types
2047        if not isinstance(session, int):
2048            raise TypeError(session)
2049        if not isinstance(output_file, (type(None), str)):
2050            raise TypeError(output_file)
2051
2052        result = self._GW2RegisterReportFile(session, output_file)
2053
2054        if result.status not in successes.success_codes:
2055            log.error(format_object(result))
2056            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2057        else:
2058            log.debug(format_object(result))
2059
2060        return result
2061
2062    def _GW2GetIdInfo(self, session: int, issue_id: int):
2063        """ Retrieves the group description for the given Issue ID. e.g. issue_id 96 returns "Document Processing Instances"
2064
2065        Args:
2066            session (int): The session integer.
2067            issue_id (int): The issue id.
2068            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
2069
2070        Returns:
2071            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'issue_id', 'buffer_length', 'buffer', 'status', 'id_info'.
2072        """
2073        # API function declaration
2074        self.library.GW2GetIdInfo.argtypes = [
2075            ct.c_size_t,  # Session_Handle session
2076            ct.c_size_t,  # size_t issueId
2077            ct.POINTER(ct.c_size_t),  # size_t * bufferLength
2078            ct.POINTER(ct.c_void_p)  # char ** outputBuffer
2079        ]
2080
2081        # Variable initialisation
2082        gw_return_object = glasswall.GwReturnObj()
2083        gw_return_object.session = ct.c_size_t(session)
2084        gw_return_object.issue_id = ct.c_size_t(issue_id)
2085        gw_return_object.buffer_length = ct.c_size_t()
2086        gw_return_object.buffer = ct.c_void_p()
2087
2088        # API call
2089        gw_return_object.status = self.library.GW2GetIdInfo(
2090            gw_return_object.session,
2091            gw_return_object.issue_id,
2092            ct.byref(gw_return_object.buffer_length),
2093            ct.byref(gw_return_object.buffer)
2094        )
2095
2096        # Editor wrote to a buffer, convert it to bytes
2097        id_info_bytes = utils.buffer_to_bytes(
2098            gw_return_object.buffer,
2099            gw_return_object.buffer_length
2100        )
2101
2102        gw_return_object.id_info = id_info_bytes.decode()
2103
2104        return gw_return_object
2105
2106    def get_id_info(self, issue_id: int, raise_unsupported: bool = True):
2107        """ Retrieves the group description for the given Issue ID. e.g. issue_id 96 returns "Document Processing Instances"
2108
2109        Args:
2110            issue_id (int): The issue id.
2111            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
2112
2113        Returns:
2114            id_info (str): The group description for the given Issue ID.
2115        """
2116        # Validate arg types
2117        if not isinstance(issue_id, int):
2118            raise TypeError(issue_id)
2119
2120        with utils.CwdHandler(self.library_path):
2121            with self.new_session() as session:
2122                result = self._GW2GetIdInfo(session, issue_id)
2123
2124                if result.status not in successes.success_codes:
2125                    log.error(format_object(result))
2126                    if raise_unsupported:
2127                        raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2128                else:
2129                    log.debug(format_object(result))
2130
2131                return result.id_info
2132
2133    def _GW2GetAllIdInfo(self, session: int):
2134        """ Retrieves the XML containing all the Issue ID ranges with their group descriptions
2135
2136        Args:
2137            session (int): The session integer.
2138
2139        Returns:
2140            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'analysis_format', 'status', 'all_id_info'.
2141        """
2142
2143        # API function declaration
2144        self.library.GW2GetAllIdInfo.argtypes = [
2145            ct.c_size_t,  # Session_Handle session
2146            ct.POINTER(ct.c_size_t),  # size_t * bufferLength
2147            ct.POINTER(ct.c_void_p)  # char ** outputBuffer
2148        ]
2149
2150        # Variable initialisation
2151        # The extracted issue Id information is stored in the analysis report, register an analysis session.
2152        gw_return_object = self._GW2RegisterAnalysisMemory(session)
2153
2154        # API call
2155        gw_return_object.status = self.library.GW2GetAllIdInfo(
2156            gw_return_object.session,
2157            ct.byref(gw_return_object.buffer_length),
2158            ct.byref(gw_return_object.buffer)
2159        )
2160
2161        # Editor wrote to a buffer, convert it to bytes
2162        all_id_info_bytes = utils.buffer_to_bytes(
2163            gw_return_object.buffer,
2164            gw_return_object.buffer_length
2165        )
2166
2167        gw_return_object.all_id_info = all_id_info_bytes.decode()
2168
2169        return gw_return_object
2170
2171    def get_all_id_info(self, output_file: Optional[str] = None, raise_unsupported: bool = True) -> str:
2172        """ Retrieves the XML containing all the Issue ID ranges with their group descriptions
2173
2174        Args:
2175            output_file (Optional[str]): The output file path where the analysis file will be written.
2176            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
2177
2178        Returns:
2179            all_id_info (str): A string XML analysis report containing all id info.
2180        """
2181        # Validate arg types
2182        if not isinstance(output_file, (type(None), str)):
2183            raise TypeError(output_file)
2184        if isinstance(output_file, str):
2185            output_file = os.path.abspath(output_file)
2186
2187        with utils.CwdHandler(self.library_path):
2188            with self.new_session() as session:
2189                result = self._GW2GetAllIdInfo(session)
2190
2191                if result.status not in successes.success_codes:
2192                    log.error(format_object(result))
2193                    if raise_unsupported:
2194                        raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2195                else:
2196                    log.debug(format_object(result))
2197
2198                if isinstance(output_file, str):
2199                    # GW2GetAllIdInfo is memory only, write to file
2200                    # make directories that do not exist
2201                    os.makedirs(os.path.dirname(output_file), exist_ok=True)
2202                    with open(output_file, "w") as f:
2203                        f.write(result.all_id_info)
2204
2205                return result.all_id_info
2206
2207    def _GW2FileSessionStatus(self, session: int):
2208        """ Retrieves the Glasswall Session Status. Also gives a high level indication of the processing that was carried out on the last document processed by the library
2209
2210        Args:
2211            session (int): The session integer.
2212            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
2213
2214        Returns:
2215            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'session_status', 'buffer', 'buffer_length', 'status', 'message'.
2216        """
2217        # API function declaration
2218        self.library.GW2FileSessionStatus.argtypes = [
2219            ct.c_size_t,  # Session_Handle session
2220            ct.POINTER(ct.c_int),  # int *glasswallSessionStatus
2221            ct.POINTER(ct.c_void_p),  # char **statusMsgBuffer
2222            ct.POINTER(ct.c_size_t)  # size_t *statusbufferLength
2223        ]
2224
2225        # Variable initialisation
2226        gw_return_object = glasswall.GwReturnObj()
2227        gw_return_object.session = ct.c_size_t(session)
2228        gw_return_object.session_status = ct.c_int()
2229        gw_return_object.buffer = ct.c_void_p()
2230        gw_return_object.buffer_length = ct.c_size_t()
2231
2232        # API call
2233        gw_return_object.status = self.library.GW2FileSessionStatus(
2234            gw_return_object.session,
2235            ct.byref(gw_return_object.session_status),
2236            ct.byref(gw_return_object.buffer),
2237            ct.byref(gw_return_object.buffer_length)
2238        )
2239
2240        # Convert session_status to int
2241        gw_return_object.session_status = gw_return_object.session_status.value
2242
2243        # Editor wrote to a buffer, convert it to bytes
2244        message_bytes = utils.buffer_to_bytes(
2245            gw_return_object.buffer,
2246            gw_return_object.buffer_length
2247        )
2248        gw_return_object.message = message_bytes.decode()
2249
2250        return gw_return_object
2251
2252    def file_session_status_message(self, session: int, raise_unsupported: bool = True) -> str:
2253        """ Retrieves the Glasswall session status message. Gives a high level indication of the processing that was carried out.
2254
2255        Args:
2256            session (int): The session integer.
2257            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
2258
2259        Returns:
2260            result.message (str):The file session status message.
2261        """
2262        # Validate arg types
2263        if not isinstance(session, int):
2264            raise TypeError(session)
2265
2266        result = self._GW2FileSessionStatus(session)
2267
2268        if result.status not in successes.success_codes:
2269            log.error(format_object(result))
2270            if raise_unsupported:
2271                raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2272        else:
2273            log.debug(format_object(result))
2274
2275        return result.message
2276
2277    def _GW2LicenceDetails(self, session: int):
2278        """ Returns a human readable string containing licence details.
2279
2280        Args:
2281            session (int): The session integer.
2282
2283        Returns:
2284            licence_details (str): A human readable string representing the relevant information contained in the licence.
2285        """
2286        # API function declaration
2287        self.library.GW2LicenceDetails.argtypes = [ct.c_size_t]
2288        self.library.GW2LicenceDetails.restype = ct.c_char_p
2289
2290        # Variable initialisation
2291        ct_session = ct.c_size_t(session)
2292
2293        # API call
2294        licence_details = self.library.GW2LicenceDetails(ct_session)
2295
2296        # Convert to Python string
2297        licence_details = ct.string_at(licence_details).decode()
2298
2299        return licence_details
2300
2301    def licence_details(self):
2302        """ Returns a string containing details of the licence.
2303
2304        Returns:
2305            result (str): A string containing details of the licence.
2306        """
2307        with self.new_session() as session:
2308            result = self._GW2LicenceDetails(session)
2309
2310            log.debug(f"\n\tsession: {session}\n\tGW2LicenceDetails: {result}")
2311
2312        return result
2313
2314    def _GW2RegisterExportTextDumpMemory(self, session: int):
2315        """ Registers an export text dump to be written in memory.
2316
2317        Args:
2318            session (int): The session integer.
2319
2320        Returns:
2321            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
2322        """
2323        # API function declaration
2324        self.library.GW2RegisterExportTextDumpMemory.argtypes = [
2325            ct.c_size_t,  # Session_Handle session
2326            ct.POINTER(ct.c_void_p),  # char ** exportTextDumpFileBuffer
2327            ct.POINTER(ct.c_size_t)  # size_t * exportTextDumpLength
2328        ]
2329
2330        # Variable initialisation
2331        gw_return_object = glasswall.GwReturnObj()
2332        gw_return_object.session = ct.c_size_t(session)
2333        gw_return_object.buffer = ct.c_void_p()
2334        gw_return_object.buffer_length = ct.c_size_t()
2335
2336        # API call
2337        gw_return_object.status = self.library.GW2RegisterExportTextDumpMemory(
2338            gw_return_object.session,
2339            ct.byref(gw_return_object.buffer),
2340            ct.byref(gw_return_object.buffer_length)
2341        )
2342
2343        return gw_return_object
2344
2345    def _GW2RegisterExportTextDumpFile(self, session: int, output_file: str):
2346        """ Registers an export text dump to be written to file.
2347
2348        Args:
2349            session (int): The session integer.
2350            output_file (str): The file path of the text dump file.
2351
2352        Returns:
2353            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.
2354        """
2355        # API function declaration
2356        self.library.GW2RegisterExportTextDumpFile.argtypes = [
2357            ct.c_size_t,  # Session_Handle session
2358            ct.c_char_p  # const char * textDumpFilePathName
2359        ]
2360
2361        # Variable initialisation
2362        gw_return_object = glasswall.GwReturnObj()
2363        gw_return_object.session = ct.c_size_t(session)
2364        gw_return_object.output_file = ct.c_char_p(output_file.encode("utf-8"))
2365
2366        # API call
2367        gw_return_object.status = self.library.GW2RegisterExportTextDumpFile(
2368            gw_return_object.session,
2369            gw_return_object.output_file
2370        )
2371
2372        return gw_return_object
2373
2374    def _GW2RegisterLicenceFile(self, session: int, input_file: str):
2375        """ Registers a "gwkey.lic" licence from file path.
2376
2377        Args:
2378            session (int): The session integer.
2379            input_file (str): The "gwkey.lic" licence input file path.
2380
2381        Returns:
2382            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'status'.
2383        """
2384        # API function declaration
2385        self.library.GW2RegisterLicenceFile.argtypes = [
2386            ct.c_size_t,  # Session_Handle session
2387            ct.c_char_p,  # const char *filename
2388        ]
2389
2390        # Variable initialisation
2391        gw_return_object = glasswall.GwReturnObj()
2392        gw_return_object.session = ct.c_size_t(session)
2393        gw_return_object.input_file = ct.c_char_p(input_file.encode("utf-8"))
2394
2395        # API call
2396        gw_return_object.status = self.library.GW2RegisterLicenceFile(
2397            gw_return_object.session,
2398            gw_return_object.input_file,
2399        )
2400
2401        return gw_return_object
2402
2403    def _GW2RegisterLicenceMemory(self, session: int, input_file: bytes):
2404        """ Registers a "gwkey.lic" licence from memory.
2405
2406        Args:
2407            session (int): The session integer.
2408            input_file (bytes): The "gwkey.lic" licence input file.
2409
2410        Returns:
2411            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
2412        """
2413        # API function declaration
2414        self.library.GW2RegisterLicenceMemory.argtypes = [
2415            ct.c_size_t,  # Session_Handle session
2416            ct.c_char_p,  # const char *filename
2417            ct.c_size_t,  # size_t licenceLength
2418        ]
2419
2420        # Variable initialisation
2421        gw_return_object = glasswall.GwReturnObj()
2422        gw_return_object.session = ct.c_size_t(session)
2423        gw_return_object.buffer = ct.c_char_p(input_file)
2424        gw_return_object.buffer_length = ct.c_size_t(len(input_file))
2425
2426        # API call
2427        gw_return_object.status = self.library.GW2RegisterLicenceMemory(
2428            gw_return_object.session,
2429            gw_return_object.buffer,
2430            gw_return_object.buffer_length
2431        )
2432
2433        return gw_return_object
2434
2435    def register_licence(self, session: int, input_file: Union[str, bytes, bytearray, io.BytesIO]):
2436        """ Registers a "gwkey.lic" licence from file path or memory.
2437
2438        Args:
2439            session (int): The session integer.
2440            input_file (Union[str, bytes, bytearray, io.BytesIO]): The "gwkey.lic" licence. It can be provided as a file path (str), bytes, bytearray, or a BytesIO object.
2441
2442        Returns:
2443            - result (glasswall.GwReturnObj): Depending on the input 'input_file':
2444                - If input_file is a str file path:
2445                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'status'.
2446
2447                - If input_file is a file in memory:
2448                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
2449        """
2450        if isinstance(input_file, str):
2451            if not os.path.isfile(input_file):
2452                raise FileNotFoundError(input_file)
2453
2454            input_file = os.path.abspath(input_file)
2455
2456            result = self._GW2RegisterLicenceFile(session, input_file)
2457
2458        elif isinstance(input_file, (bytes, bytearray, io.BytesIO,)):
2459            # Convert bytearray and io.BytesIO to bytes
2460            input_file = utils.as_bytes(input_file)
2461
2462            result = self._GW2RegisterLicenceMemory(session, input_file)
2463
2464        else:
2465            raise TypeError(input_file)
2466
2467        if result.status not in successes.success_codes:
2468            log.error(format_object(result))
2469            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2470        else:
2471            log.debug(format_object(result))
2472
2473        return result
class Editor(glasswall.libraries.library.Library):
  19class Editor(Library):
  20    """ A high level Python wrapper for Glasswall Editor / Core2. """
  21
  22    def __init__(self, library_path: str, licence: Union[str, bytes, bytearray, io.BytesIO] = None):
  23        """ Initialise the Editor instance.
  24
  25        Args:
  26            library_path (str): The file or directory path to the Editor library.
  27            licence (str, bytes, bytearray, or io.BytesIO, optional): The licence file content or path. This can be:
  28                - A string representing the file path to the licence.
  29                - A `bytes` or `bytearray` object containing the licence data.
  30                - An `io.BytesIO` object for in-memory licence data.
  31                If not specified, it is assumed that the licence file is located in the same directory as the `library_path`.
  32        """
  33        super().__init__(library_path)
  34        self.library = self.load_library(os.path.abspath(library_path))
  35        self.licence = licence
  36
  37        # Validate killswitch has not activated
  38        self.validate_licence()
  39
  40        log.info(f"Loaded Glasswall {self.__class__.__name__} version {self.version()} from {self.library_path}")
  41
  42    def validate_licence(self):
  43        """ Validates the licence of the library by checking the licence details.
  44
  45        Raises:
  46            LicenceExpired: If the licence has expired or could not be validated.
  47        """
  48        licence_details = self.licence_details()
  49
  50        bad_details = [
  51            "Unable to Read Licence Key File",
  52            "Licence File Missing Required Contents",
  53            "Licence Expired",
  54        ]
  55
  56        if any(bad_detail.lower() in licence_details.lower() for bad_detail in bad_details):
  57            # bad_details found in licence_details
  58            log.error(f"{self.__class__.__name__} licence validation failed. Licence details:\n{licence_details}")
  59            raise errors.LicenceExpired(licence_details)
  60        else:
  61            log.debug(f"{self.__class__.__name__} licence validated successfully. Licence details:\n{licence_details}")
  62
  63    def version(self):
  64        """ Returns the Glasswall library version.
  65
  66        Returns:
  67            version (str): The Glasswall library version.
  68        """
  69        # API function declaration
  70        self.library.GW2LibVersion.restype = ct.c_char_p
  71
  72        # API call
  73        version = self.library.GW2LibVersion()
  74
  75        # Convert to Python string
  76        version = ct.string_at(version).decode()
  77
  78        return version
  79
  80    def open_session(self):
  81        """ Open a new Glasswall session.
  82
  83        Returns:
  84            session (int): An incrementing integer repsenting the current session.
  85        """
  86        # API call
  87        session = self.library.GW2OpenSession()
  88
  89        log.debug(f"\n\tsession: {session}")
  90
  91        if self.licence:
  92            # Must register licence on each GW2OpenSession if the licence is not in the same directory as the library
  93            self.register_licence(session, self.licence)
  94
  95        return session
  96
  97    def close_session(self, session: int) -> int:
  98        """ Close the Glasswall session. All resources allocated by the session will be destroyed.
  99
 100        Args:
 101            session (int): The session to close.
 102
 103        Returns:
 104            status (int): The status code of the function call.
 105        """
 106        if not isinstance(session, int):
 107            raise TypeError(session)
 108
 109        # API function declaration
 110        self.library.GW2CloseSession.argtypes = [ct.c_size_t]
 111
 112        # Variable initialisation
 113        ct_session = ct.c_size_t(session)
 114
 115        # API call
 116        status = self.library.GW2CloseSession(ct_session)
 117
 118        if status not in successes.success_codes:
 119            log.error(f"\n\tsession: {session}\n\tstatus: {status}")
 120        else:
 121            log.debug(f"\n\tsession: {session}\n\tstatus: {status}")
 122
 123        return status
 124
 125    @contextmanager
 126    def new_session(self):
 127        """ Context manager. Opens a new session on entry and closes the session on exit. """
 128        try:
 129            session = self.open_session()
 130            yield session
 131        finally:
 132            self.close_session(session)
 133
 134    def run_session(self, session):
 135        """ Runs the Glasswall session and begins processing of a file.
 136
 137        Args:
 138            session (int): The session to run.
 139
 140        Returns:
 141            status (int): The status code of the function call.
 142        """
 143        # API function declaration
 144        self.library.GW2RunSession.argtypes = [ct.c_size_t]
 145
 146        # Variable initialisation
 147        ct_session = ct.c_size_t(session)
 148
 149        # API call
 150        status = self.library.GW2RunSession(ct_session)
 151
 152        if status not in successes.success_codes:
 153            log.error(f"\n\tsession: {session}\n\tstatus: {status}\n\tGW2FileErrorMsg: {self.file_error_message(session)}")
 154        else:
 155            log.debug(f"\n\tsession: {session}\n\tstatus: {status}\n\tGW2FileErrorMsg: {self.file_error_message(session)}")
 156
 157        return status
 158
 159    def determine_file_type(self, input_file: Union[str, bytes, bytearray, io.BytesIO], as_string: bool = False, raise_unsupported: bool = True) -> Union[int, str]:
 160        """ Determine the file type of a given input file, either as an integer identifier or a string.
 161
 162        Args:
 163            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file to analyse. It can be provided as a file path (str), bytes, bytearray, or a BytesIO object.
 164            as_string (bool, optional): Return file type as string, eg: "bmp" instead of: 29. Defaults to False.
 165            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
 166
 167        Returns:
 168            file_type (Union[int, str]): The file type.
 169        """
 170        if isinstance(input_file, str):
 171            if not os.path.isfile(input_file):
 172                raise FileNotFoundError(input_file)
 173
 174            # convert to ct.c_char_p of bytes
 175            ct_input_file = ct.c_char_p(input_file.encode("utf-8"))
 176
 177            # API call
 178            file_type = self.library.GW2DetermineFileTypeFromFile(ct_input_file)
 179
 180        elif isinstance(input_file, (bytes, bytearray, io.BytesIO)):
 181            # convert to bytes
 182            bytes_input_file = utils.as_bytes(input_file)
 183
 184            # ctypes conversion
 185            ct_buffer = ct.c_char_p(bytes_input_file)
 186            ct_buffer_length = ct.c_size_t(len(bytes_input_file))
 187
 188            # API call
 189            file_type = self.library.GW2DetermineFileTypeFromMemory(
 190                ct_buffer,
 191                ct_buffer_length
 192            )
 193
 194        else:
 195            raise TypeError(input_file)
 196
 197        file_type_as_string = dft.file_type_int_to_str(file_type)
 198        input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
 199
 200        if not dft.is_success(file_type):
 201            log.warning(f"\n\tfile_type: {file_type}\n\tfile_type_as_string: {file_type_as_string}\n\tinput_file: {input_file_repr}")
 202            if raise_unsupported:
 203                raise dft.int_class_map.get(file_type, dft.errors.UnknownErrorCode)(file_type)
 204        else:
 205            log.debug(f"\n\tfile_type: {file_type}\n\tfile_type_as_string: {file_type_as_string}\n\tinput_file: {input_file_repr}")
 206
 207        if as_string:
 208            return file_type_as_string
 209
 210        return file_type
 211
 212    def _GW2GetPolicySettings(self, session: int, policy_format: int = 0):
 213        """ Get current policy settings for the given session.
 214
 215        Args:
 216            session (int): The session integer.
 217            policy_format (int): The format of the content management policy. 0=XML.
 218
 219        Returns:
 220            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'policy_format', 'status', 'policy'.
 221        """
 222        # API function declaration
 223        self.library.GW2GetPolicySettings.argtypes = [
 224            ct.c_size_t,  # Session_Handle session
 225            ct.POINTER(ct.c_void_p),  # char ** policiesBuffer
 226            ct.POINTER(ct.c_size_t),  # size_t * policiesLength
 227            ct.c_int,  # Policy_Format format
 228        ]
 229
 230        # Variable initialisation
 231        gw_return_object = glasswall.GwReturnObj()
 232        gw_return_object.session = ct.c_size_t(session)
 233        gw_return_object.buffer = ct.c_void_p()
 234        gw_return_object.buffer_length = ct.c_size_t()
 235        gw_return_object.policy_format = ct.c_int(policy_format)
 236
 237        # API Call
 238        gw_return_object.status = self.library.GW2GetPolicySettings(
 239            gw_return_object.session,
 240            ct.byref(gw_return_object.buffer),
 241            ct.byref(gw_return_object.buffer_length),
 242            gw_return_object.policy_format
 243        )
 244
 245        # Editor wrote to a buffer, convert it to bytes
 246        policy_bytes = utils.buffer_to_bytes(
 247            gw_return_object.buffer,
 248            gw_return_object.buffer_length
 249        )
 250
 251        gw_return_object.policy = policy_bytes.decode()
 252
 253        return gw_return_object
 254
 255    def get_content_management_policy(self, session: int):
 256        """ Returns the content management configuration for a given session.
 257
 258        Args:
 259            session (int): The session integer.
 260
 261        Returns:
 262            xml_string (str): The XML string of the current content management configuration.
 263        """
 264        # NOTE GW2GetPolicySettings is current not implemented in editor
 265
 266        # set xml_string as loaded default config
 267        xml_string = glasswall.content_management.policies.Editor(default="sanitise").text,
 268
 269        # log.debug(f"xml_string:\n{xml_string}")
 270
 271        return xml_string
 272
 273        # # API function declaration
 274        # self.library.GW2GetPolicySettings.argtypes = [
 275        #     ct.c_size_t,
 276        #     ct.c_void_p,
 277        # ]
 278
 279        # # Variable initialisation
 280        # ct_session = ct.c_size_t(session)
 281        # ct_buffer = ct.c_void_p()
 282        # ct_buffer_length = ct.c_size_t()
 283        # # ct_file_format = ct.c_int(file_format)
 284
 285        # # API Call
 286        # status = self.library.GW2GetPolicySettings(
 287        #     ct_session,
 288        #     ct.byref(ct_buffer),
 289        #     ct.byref(ct_buffer_length)
 290        # )
 291
 292        # print("GW2GetPolicySettings status:", status)
 293
 294        # file_bytes = utils.buffer_to_bytes(
 295        #     ct_buffer,
 296        #     ct_buffer_length,
 297        # )
 298
 299        # return file_bytes
 300
 301    def _GW2RegisterPoliciesFile(self, session: int, input_file: str, policy_format: int = 0):
 302        """ Registers the policies to be used by Glasswall when processing files.
 303
 304        Args:
 305            session (int): The session integer.
 306            input_file (str): The content management policy input file path.
 307            policy_format (int): The format of the content management policy. 0=XML.
 308
 309        Returns:
 310            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'policy_format', 'status'.
 311        """
 312        # API function declaration
 313        self.library.GW2RegisterPoliciesFile.argtypes = [
 314            ct.c_size_t,  # Session_Handle session
 315            ct.c_char_p,  # const char *filename
 316            ct.c_int,  # Policy_Format format
 317        ]
 318
 319        # Variable initialisation
 320        gw_return_object = glasswall.GwReturnObj()
 321        gw_return_object.session = ct.c_size_t(session)
 322        gw_return_object.input_file = ct.c_char_p(input_file.encode("utf-8"))
 323        gw_return_object.policy_format = ct.c_int(policy_format)
 324
 325        gw_return_object.status = self.library.GW2RegisterPoliciesFile(
 326            gw_return_object.session,
 327            gw_return_object.input_file,
 328            gw_return_object.policy_format
 329        )
 330
 331        return gw_return_object
 332
 333    def _GW2RegisterPoliciesMemory(self, session: int, input_file: bytes, policy_format: int = 0):
 334        """ Registers the policies in memory to be used by Glasswall when processing files.
 335
 336        Args:
 337            session (int): The session integer.
 338            input_file (str): The content management policy input file bytes.
 339            policy_format (int): The format of the content management policy. 0=XML.
 340
 341        Returns:
 342            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'policy_format', 'status'.
 343        """
 344        # API function declaration
 345        self.library.GW2RegisterPoliciesMemory.argtype = [
 346            ct.c_size_t,  # Session_Handle session
 347            ct.c_char_p,  # const char *policies
 348            ct.c_size_t,  # size_t policiesLength
 349            ct.c_int  # Policy_Format format
 350        ]
 351
 352        # Variable initialisation
 353        gw_return_object = glasswall.GwReturnObj()
 354        gw_return_object.session = ct.c_size_t(session)
 355        gw_return_object.buffer = ct.c_char_p(input_file)
 356        gw_return_object.buffer_length = ct.c_size_t(len(input_file))
 357        gw_return_object.policy_format = ct.c_int(policy_format)
 358
 359        # API Call
 360        gw_return_object.status = self.library.GW2RegisterPoliciesMemory(
 361            gw_return_object.session,
 362            gw_return_object.buffer,
 363            gw_return_object.buffer_length,
 364            gw_return_object.policy_format
 365        )
 366
 367        return gw_return_object
 368
 369    def set_content_management_policy(self, session: int, input_file: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None, policy_format=0):
 370        """ Sets the content management policy configuration. If input_file is None then default settings (sanitise) are applied.
 371
 372        Args:
 373            session (int): The session integer.
 374            input_file (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
 375            policy_format (int): The format of the content management policy. 0=XML.
 376
 377        Returns:
 378            - result (glasswall.GwReturnObj): Depending on the input 'input_file':
 379                - If input_file is a str file path:
 380                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'policy_format', 'status'.
 381
 382                - If input_file is a file in memory:
 383                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'policy_format', 'status'.
 384        """
 385        # Validate type
 386        if not isinstance(session, int):
 387            raise TypeError(session)
 388        if not isinstance(input_file, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
 389            raise TypeError(input_file)
 390        if not isinstance(policy_format, int):
 391            raise TypeError(policy_format)
 392
 393        # Set input_file to default if input_file is None
 394        if input_file is None:
 395            input_file = glasswall.content_management.policies.Editor(default="sanitise")
 396
 397        # Validate xml content is parsable
 398        utils.validate_xml(input_file)
 399
 400        # From file
 401        if isinstance(input_file, str) and os.path.isfile(input_file):
 402            input_file = os.path.abspath(input_file)
 403
 404            result = self._GW2RegisterPoliciesFile(session, input_file)
 405
 406        # From memory
 407        elif isinstance(input_file, (str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
 408            # Convert bytearray, io.BytesIO to bytes
 409            if isinstance(input_file, (bytearray, io.BytesIO)):
 410                input_file = utils.as_bytes(input_file)
 411            # Convert string xml or Policy to bytes
 412            if isinstance(input_file, (str, glasswall.content_management.policies.policy.Policy)):
 413                input_file = input_file.encode("utf-8")
 414
 415            result = self._GW2RegisterPoliciesMemory(session, input_file)
 416
 417        if result.status not in successes.success_codes:
 418            log.error(format_object(result))
 419            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
 420        else:
 421            log.debug(format_object(result))
 422
 423        return result
 424
 425    def _GW2RegisterInputFile(self, session: int, input_file: str):
 426        """ Register an input file for the given session.
 427
 428        Args:
 429            session (int): The session integer.
 430            input_file (str): The input file path.
 431
 432        Returns:
 433            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'status'.
 434        """
 435        # API function declaration
 436        self.library.GW2RegisterInputFile.argtypes = [
 437            ct.c_size_t,  # Session_Handle session
 438            ct.c_char_p  # const char * inputFilePath
 439        ]
 440
 441        # Variable initialisation
 442        gw_return_object = glasswall.GwReturnObj()
 443        gw_return_object.session = ct.c_size_t(session)
 444        gw_return_object.input_file = ct.c_char_p(input_file.encode("utf-8"))
 445        gw_return_object.input_file_path = input_file
 446
 447        # API call
 448        gw_return_object.status = self.library.GW2RegisterInputFile(
 449            gw_return_object.session,
 450            gw_return_object.input_file
 451        )
 452
 453        return gw_return_object
 454
 455    def _GW2RegisterInputMemory(self, session: int, input_file: bytes):
 456        """ Register an input file in memory for the given session.
 457
 458        Args:
 459            session (int): The session integer.
 460            input_file (bytes): The input file in memory.
 461
 462        Returns:
 463            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
 464        """
 465        # API function declaration
 466        self.library.GW2RegisterInputMemory.argtypes = [
 467            ct.c_size_t,  # Session_Handle session
 468            ct.c_char_p,  # const char * inputFileBuffer
 469            ct.c_size_t,  # size_t inputLength
 470        ]
 471
 472        # Variable initialisation
 473        gw_return_object = glasswall.GwReturnObj()
 474        gw_return_object.session = ct.c_size_t(session)
 475        gw_return_object.buffer = ct.c_char_p(input_file)
 476        gw_return_object.buffer_length = ct.c_size_t(len(input_file))
 477
 478        # API call
 479        gw_return_object.status = self.library.GW2RegisterInputMemory(
 480            gw_return_object.session,
 481            gw_return_object.buffer,
 482            gw_return_object.buffer_length
 483        )
 484
 485        return gw_return_object
 486
 487    def register_input(self, session: int, input_file: Union[str, bytes, bytearray, io.BytesIO]):
 488        """ Register an input file or bytes for the given session.
 489
 490        Args:
 491            session (int): The session integer.
 492            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
 493
 494        Returns:
 495            - result (glasswall.GwReturnObj): Depending on the input 'input_file':
 496                - If input_file is a str file path:
 497                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'status'.
 498
 499                - If input_file is a file in memory:
 500                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
 501        """
 502        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO,)):
 503            raise TypeError(input_file)
 504
 505        if isinstance(input_file, str):
 506            if not os.path.isfile(input_file):
 507                raise FileNotFoundError(input_file)
 508
 509            input_file = os.path.abspath(input_file)
 510
 511            result = self._GW2RegisterInputFile(session, input_file)
 512
 513        elif isinstance(input_file, (bytes, bytearray, io.BytesIO,)):
 514            # Convert bytearray and io.BytesIO to bytes
 515            input_file = utils.as_bytes(input_file)
 516
 517            result = self._GW2RegisterInputMemory(session, input_file)
 518
 519        if result.status not in successes.success_codes:
 520            log.error(format_object(result))
 521            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
 522        else:
 523            log.debug(format_object(result))
 524
 525        return result
 526
 527    def _GW2RegisterOutFile(self, session: int, output_file: str):
 528        """ Register an output file for the given session.
 529
 530        Args:
 531            session (int): The session integer.
 532            output_file (str): The output file path.
 533
 534        Returns:
 535            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.
 536        """
 537        # API function declaration
 538        self.library.GW2RegisterOutFile.argtypes = [
 539            ct.c_size_t,  # Session_Handle session
 540            ct.c_char_p  # const char * outputFilePath
 541        ]
 542
 543        # Variable initialisation
 544        gw_return_object = glasswall.GwReturnObj()
 545        gw_return_object.session = ct.c_size_t(session)
 546        gw_return_object.output_file = ct.c_char_p(output_file.encode("utf-8"))
 547
 548        # API call
 549        gw_return_object.status = self.library.GW2RegisterOutFile(
 550            gw_return_object.session,
 551            gw_return_object.output_file
 552        )
 553
 554        return gw_return_object
 555
 556    def _GW2RegisterOutputMemory(self, session: int):
 557        """ Register an output file in memory for the given session.
 558
 559        Args:
 560            session (int): The session integer.
 561
 562        Returns:
 563            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
 564        """
 565        # API function declaration
 566        self.library.GW2RegisterOutputMemory.argtypes = [
 567            ct.c_size_t,  # Session_Handle session
 568            ct.POINTER(ct.c_void_p),  # char ** outputBuffer
 569            ct.POINTER(ct.c_size_t)  # size_t * outputLength
 570        ]
 571
 572        # Variable initialisation
 573        gw_return_object = glasswall.GwReturnObj()
 574        gw_return_object.session = ct.c_size_t(session)
 575        gw_return_object.buffer = ct.c_void_p()
 576        gw_return_object.buffer_length = ct.c_size_t()
 577
 578        # API call
 579        gw_return_object.status = self.library.GW2RegisterOutputMemory(
 580            gw_return_object.session,
 581            ct.byref(gw_return_object.buffer),
 582            ct.byref(gw_return_object.buffer_length)
 583        )
 584
 585        return gw_return_object
 586
 587    def register_output(self, session, output_file: Optional[str] = None):
 588        """ Register an output file for the given session. If output_file is None the file will be returned as 'buffer' and 'buffer_length' attributes.
 589
 590        Args:
 591            session (int): The session integer.
 592            output_file (Optional[str]): If specified, during run session the file will be written to output_file, otherwise the file will be written to the glasswall.GwReturnObj 'buffer' and 'buffer_length' attributes.
 593
 594        Returns:
 595            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attribute 'status' indicating the result of the function call. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size.
 596        """
 597        if not isinstance(output_file, (type(None), str)):
 598            raise TypeError(output_file)
 599
 600        if isinstance(output_file, str):
 601            output_file = os.path.abspath(output_file)
 602
 603            result = self._GW2RegisterOutFile(session, output_file)
 604
 605        elif isinstance(output_file, type(None)):
 606            result = self._GW2RegisterOutputMemory(session)
 607
 608        if result.status not in successes.success_codes:
 609            log.error(format_object(result))
 610            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
 611        else:
 612            log.debug(format_object(result))
 613
 614        return result
 615
 616    def _GW2RegisterAnalysisFile(self, session: int, output_file: str, analysis_format: int = 0):
 617        """ Register an analysis output file for the given session.
 618
 619        Args:
 620            session (int): The session integer.
 621            output_file (str): The analysis output file path.
 622            analysis_format (int): The format of the analysis report. 0=XML, 1=XMLExtended
 623
 624        Returns:
 625            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'analysis_format', 'status'.
 626        """
 627        # API function declaration
 628        self.library.GW2RegisterAnalysisFile.argtypes = [
 629            ct.c_size_t,  # Session_Handle session
 630            ct.c_char_p,  # const char * analysisFilePathName
 631            ct.c_int,  # Analysis_Format format
 632        ]
 633
 634        # Variable initialisation
 635        gw_return_object = glasswall.GwReturnObj()
 636        gw_return_object.session = ct.c_size_t(session)
 637        gw_return_object.output_file = ct.c_char_p(output_file.encode("utf-8"))
 638        gw_return_object.analysis_format = ct.c_int()
 639
 640        # API call
 641        gw_return_object.status = self.library.GW2RegisterAnalysisFile(
 642            gw_return_object.session,
 643            gw_return_object.output_file,
 644            gw_return_object.analysis_format
 645        )
 646
 647        return gw_return_object
 648
 649    def _GW2RegisterAnalysisMemory(self, session: int, analysis_format: int = 0):
 650        """ Register an analysis output file in memory for the given session.
 651
 652        Args:
 653            session (int): The session integer.
 654            analysis_format (int): The format of the analysis report. 0=XML, 1=XMLExtended
 655
 656        Returns:
 657            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'analysis_format', 'status'.
 658        """
 659        # API function declaration
 660        self.library.GW2RegisterAnalysisMemory.argtypes = [
 661            ct.c_size_t,  # Session_Handle session
 662            ct.POINTER(ct.c_void_p),  # char ** analysisFileBuffer
 663            ct.POINTER(ct.c_size_t),  # size_t * analysisoutputLength
 664            ct.c_int  # Analysis_Format format
 665        ]
 666
 667        # Variable initialisation
 668        gw_return_object = glasswall.GwReturnObj()
 669        gw_return_object.session = ct.c_size_t(session)
 670        gw_return_object.buffer = ct.c_void_p()
 671        gw_return_object.buffer_length = ct.c_size_t()
 672        gw_return_object.analysis_format = ct.c_int()
 673
 674        # API call
 675        gw_return_object.status = self.library.GW2RegisterAnalysisMemory(
 676            gw_return_object.session,
 677            ct.byref(gw_return_object.buffer),
 678            ct.byref(gw_return_object.buffer_length),
 679            gw_return_object.analysis_format
 680        )
 681
 682        return gw_return_object
 683
 684    def register_analysis(self, session: int, output_file: Optional[str] = None):
 685        """ Registers an analysis file for the given session. The analysis file will be created during the session's run_session call.
 686
 687        Args:
 688            session (int): The session integer.
 689            output_file (Optional[str]): Default None. The file path where the analysis will be written. None returns the analysis as bytes.
 690
 691        Returns:
 692            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'status', 'session', 'analysis_format'. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size. If output_file is not None (file mode) 'output_file' is included.
 693        """
 694        if not isinstance(output_file, (type(None), str)):
 695            raise TypeError(output_file)
 696
 697        if isinstance(output_file, str):
 698            output_file = os.path.abspath(output_file)
 699
 700            result = self._GW2RegisterAnalysisFile(session, output_file)
 701
 702        elif isinstance(output_file, type(None)):
 703            result = self._GW2RegisterAnalysisMemory(session)
 704
 705        if result.status not in successes.success_codes:
 706            log.error(format_object(result))
 707            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
 708        else:
 709            log.debug(format_object(result))
 710
 711        return result
 712
 713    def protect_file(
 714        self,
 715        input_file: Union[str, bytes, bytearray, io.BytesIO],
 716        output_file: Optional[str] = None,
 717        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
 718        raise_unsupported: bool = True,
 719    ):
 720        """ Protects a file using the current content management configuration, returning the file bytes. The protected file is written to output_file if it is provided.
 721
 722        Args:
 723            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
 724            output_file (Optional[str]): The output file path where the protected file will be written.
 725            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
 726            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
 727
 728        Returns:
 729            file_bytes (bytes): The protected file bytes.
 730        """
 731        # Validate arg types
 732        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
 733            raise TypeError(input_file)
 734        if not isinstance(output_file, (type(None), str)):
 735            raise TypeError(output_file)
 736        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
 737            raise TypeError(content_management_policy)
 738        if not isinstance(raise_unsupported, bool):
 739            raise TypeError(raise_unsupported)
 740
 741        # Convert string path arguments to absolute paths
 742        if isinstance(input_file, str):
 743            if not os.path.isfile(input_file):
 744                raise FileNotFoundError(input_file)
 745            input_file = os.path.abspath(input_file)
 746        if isinstance(output_file, str):
 747            output_file = os.path.abspath(output_file)
 748            # make directories that do not exist
 749            os.makedirs(os.path.dirname(output_file), exist_ok=True)
 750        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
 751            content_management_policy = os.path.abspath(content_management_policy)
 752
 753        # Convert memory inputs to bytes
 754        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
 755            input_file = utils.as_bytes(input_file)
 756
 757        with utils.CwdHandler(self.library_path):
 758            with self.new_session() as session:
 759                content_management_policy = self.set_content_management_policy(session, content_management_policy)
 760                register_input = self.register_input(session, input_file)
 761                register_output = self.register_output(session, output_file=output_file)
 762                status = self.run_session(session)
 763
 764                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
 765                if status not in successes.success_codes:
 766                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
 767                    if raise_unsupported:
 768                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
 769                    else:
 770                        file_bytes = None
 771                else:
 772                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
 773                    # Get file bytes
 774                    if isinstance(output_file, str):
 775                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
 776                        if not os.path.isfile(output_file):
 777                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
 778                            file_bytes = None
 779                        else:
 780                            with open(output_file, "rb") as f:
 781                                file_bytes = f.read()
 782                    else:
 783                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
 784                        file_bytes = utils.buffer_to_bytes(
 785                            register_output.buffer,
 786                            register_output.buffer_length
 787                        )
 788
 789                # Ensure memory allocated is not garbage collected
 790                content_management_policy, register_input, register_output
 791
 792                return file_bytes
 793
 794    def protect_directory(
 795        self,
 796        input_directory: str,
 797        output_directory: Optional[str] = None,
 798        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
 799        raise_unsupported: bool = True,
 800    ):
 801        """ Recursively processes all files in a directory in protect mode using the given content management policy.
 802        The protected files are written to output_directory maintaining the same directory structure as input_directory.
 803
 804        Args:
 805            input_directory (str): The input directory containing files to protect.
 806            output_directory (Optional[str]): The output directory where the protected file will be written, or None to not write files.
 807            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
 808            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
 809
 810        Returns:
 811            protected_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.
 812        """
 813        protected_files_dict = {}
 814        # Call protect_file on each file in input_directory to output_directory
 815        for input_file in utils.list_file_paths(input_directory):
 816            relative_path = os.path.relpath(input_file, input_directory)
 817            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
 818
 819            protected_bytes = self.protect_file(
 820                input_file=input_file,
 821                output_file=output_file,
 822                raise_unsupported=raise_unsupported,
 823                content_management_policy=content_management_policy,
 824            )
 825
 826            protected_files_dict[relative_path] = protected_bytes
 827
 828        return protected_files_dict
 829
 830    def analyse_file(
 831        self,
 832        input_file: Union[str, bytes, bytearray, io.BytesIO],
 833        output_file: Optional[str] = None,
 834        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
 835        raise_unsupported: bool = True,
 836    ):
 837        """ Analyses a file, returning the analysis bytes. The analysis is written to output_file if it is provided.
 838
 839        Args:
 840            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
 841            output_file (Optional[str]): The output file path where the analysis file will be written.
 842            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
 843            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
 844
 845        Returns:
 846            file_bytes (bytes): The analysis file bytes.
 847        """
 848        # Validate arg types
 849        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
 850            raise TypeError(input_file)
 851        if not isinstance(output_file, (type(None), str)):
 852            raise TypeError(output_file)
 853        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
 854            raise TypeError(content_management_policy)
 855        if not isinstance(raise_unsupported, bool):
 856            raise TypeError(raise_unsupported)
 857
 858        # Convert string path arguments to absolute paths
 859        if isinstance(input_file, str):
 860            if not os.path.isfile(input_file):
 861                raise FileNotFoundError(input_file)
 862            input_file = os.path.abspath(input_file)
 863        if isinstance(output_file, str):
 864            output_file = os.path.abspath(output_file)
 865            # make directories that do not exist
 866            os.makedirs(os.path.dirname(output_file), exist_ok=True)
 867        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
 868            content_management_policy = os.path.abspath(content_management_policy)
 869
 870        # Convert memory inputs to bytes
 871        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
 872            input_file = utils.as_bytes(input_file)
 873
 874        with utils.CwdHandler(self.library_path):
 875            with self.new_session() as session:
 876                content_management_policy = self.set_content_management_policy(session, content_management_policy)
 877                register_input = self.register_input(session, input_file)
 878                register_analysis = self.register_analysis(session, output_file)
 879                status = self.run_session(session)
 880
 881                file_bytes = None
 882                if isinstance(output_file, str):
 883                    # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
 884                    if os.path.isfile(output_file):
 885                        with open(output_file, "rb") as f:
 886                            file_bytes = f.read()
 887                else:
 888                    # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
 889                    if register_analysis.buffer and register_analysis.buffer_length:
 890                        file_bytes = utils.buffer_to_bytes(
 891                            register_analysis.buffer,
 892                            register_analysis.buffer_length
 893                        )
 894
 895                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
 896                if status not in successes.success_codes:
 897                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
 898                    if raise_unsupported:
 899                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
 900                else:
 901                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
 902
 903                # Ensure memory allocated is not garbage collected
 904                content_management_policy, register_input, register_analysis
 905
 906                return file_bytes
 907
 908    def analyse_directory(
 909        self,
 910        input_directory: str,
 911        output_directory: Optional[str] = None,
 912        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
 913        raise_unsupported: bool = True,
 914    ):
 915        """ Analyses all files in a directory and its subdirectories. The analysis files are written to output_directory maintaining the same directory structure as input_directory.
 916
 917        Args:
 918            input_directory (str): The input directory containing files to analyse.
 919            output_directory (Optional[str]): The output directory where the analysis files will be written, or None to not write files.
 920            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
 921            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
 922
 923        Returns:
 924            analysis_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.
 925        """
 926        analysis_files_dict = {}
 927        # Call analyse_file on each file in input_directory to output_directory
 928        for input_file in utils.list_file_paths(input_directory):
 929            relative_path = os.path.relpath(input_file, input_directory) + ".xml"
 930            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
 931
 932            analysis_bytes = self.analyse_file(
 933                input_file=input_file,
 934                output_file=output_file,
 935                raise_unsupported=raise_unsupported,
 936                content_management_policy=content_management_policy,
 937            )
 938
 939            analysis_files_dict[relative_path] = analysis_bytes
 940
 941        return analysis_files_dict
 942
 943    def protect_and_analyse_file(
 944        self,
 945        input_file: Union[str, bytes, bytearray, io.BytesIO],
 946        output_file: Optional[str] = None,
 947        output_analysis_report: Optional[str] = None,
 948        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
 949        raise_unsupported: bool = True,
 950    ):
 951        """ Protects and analyses a file in a single session, returning both protected file bytes and analysis report bytes.
 952
 953        Args:
 954            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
 955            output_file (Optional[str]): The output file path where the protected file will be written.
 956            output_analysis_report (Optional[str]): The output file path where the XML analysis report will be written.
 957            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
 958            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
 959
 960        Returns:
 961            Tuple[Optional[bytes], Optional[bytes]]: A tuple of (protected_file_bytes, analysis_report_bytes).
 962        """
 963        # Validate arg types
 964        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
 965            raise TypeError(f"input_file expected to be of type: str, bytes, bytearray, io.BytesIO. Got: {type(input_file).__name__}")
 966        if not isinstance(output_file, (type(None), str)):
 967            raise TypeError(f"output_file expected to be of type: None, str. Got: {type(output_file).__name__}")
 968        if not isinstance(output_analysis_report, (type(None), str)):
 969            raise TypeError(f"output_analysis_report expected to be of type: None, str. Got: {type(output_analysis_report).__name__}")
 970        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
 971            raise TypeError(f"content_management_policy expected to be of type: None, str, bytes, bytearray, io.BytesIO, Policy. Got: {type(content_management_policy).__name__}")
 972        if not isinstance(raise_unsupported, bool):
 973            raise TypeError(f"raise_unsupported expected to be of type: bool. Got: {type(raise_unsupported).__name__}")
 974
 975        # Convert string path arguments to absolute paths
 976        if isinstance(input_file, str):
 977            if not os.path.isfile(input_file):
 978                raise FileNotFoundError(input_file)
 979            input_file = os.path.abspath(input_file)
 980        if isinstance(output_file, str):
 981            output_file = os.path.abspath(output_file)
 982            os.makedirs(os.path.dirname(output_file), exist_ok=True)
 983        if isinstance(output_analysis_report, str):
 984            output_analysis_report = os.path.abspath(output_analysis_report)
 985            os.makedirs(os.path.dirname(output_analysis_report), exist_ok=True)
 986        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
 987            content_management_policy = os.path.abspath(content_management_policy)
 988
 989        # Convert memory inputs to bytes
 990        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
 991            input_file = utils.as_bytes(input_file)
 992
 993        with utils.CwdHandler(self.library_path):
 994            with self.new_session() as session:
 995                content_management_policy = self.set_content_management_policy(session, content_management_policy)
 996                register_input = self.register_input(session, input_file)
 997                register_output = self.register_output(session, output_file)
 998                register_analysis = self.register_analysis(session, output_analysis_report)
 999
1000                status = self.run_session(session)
1001
1002                protected_file_bytes = None
1003                analysis_report_bytes = None
1004
1005                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1006                if status not in successes.success_codes:
1007                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1008                    if raise_unsupported:
1009                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1010
1011                # Get analysis report file bytes, even on processing failure
1012                if isinstance(output_analysis_report, str):
1013                    # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1014                    if not os.path.isfile(output_analysis_report):
1015                        log.error(f"Editor returned success code: {status} but no output file was found: {output_analysis_report}")
1016                    else:
1017                        with open(output_analysis_report, "rb") as f:
1018                            analysis_report_bytes = f.read()
1019                else:
1020                    # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1021                    if register_analysis.buffer and register_analysis.buffer_length:
1022                        analysis_report_bytes = utils.buffer_to_bytes(
1023                            register_analysis.buffer,
1024                            register_analysis.buffer_length
1025                        )
1026
1027                # On success, get protected file bytes
1028                if status in successes.success_codes:
1029                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1030                    # Get file bytes
1031                    if isinstance(output_file, str):
1032                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1033                        if not os.path.isfile(output_file):
1034                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1035                        else:
1036                            with open(output_file, "rb") as f:
1037                                protected_file_bytes = f.read()
1038                    else:
1039                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1040                        protected_file_bytes = utils.buffer_to_bytes(
1041                            register_output.buffer,
1042                            register_output.buffer_length
1043                        )
1044
1045                # Ensure memory allocated is not garbage collected
1046                content_management_policy, register_input, register_output, register_analysis
1047
1048                return protected_file_bytes, analysis_report_bytes
1049
1050    def protect_and_analyse_directory(
1051        self,
1052        input_directory: str,
1053        output_directory: Optional[str] = None,
1054        analysis_directory: Optional[str] = None,
1055        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1056        raise_unsupported: bool = True,
1057    ):
1058        """ Recursively processes all files in a directory using protect and analyse mode with the given content management policy.
1059        Outputs are written to output_directory and analysis_directory maintaining the same structure as input_directory.
1060
1061        Args:
1062            input_directory (str): The input directory containing files to process.
1063            output_directory (Optional[str]): The output directory for protected files.
1064            analysis_directory (Optional[str]): The output directory for XML analysis reports.
1065            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply.
1066            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1067
1068        Returns:
1069            result_dict (dict): A dictionary mapping relative file paths to tuples of (protected_file_bytes, analysis_report_bytes).
1070        """
1071        result_dict = {}
1072        for input_file in utils.list_file_paths(input_directory):
1073            relative_path = os.path.relpath(input_file, input_directory)
1074            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
1075            output_analysis_report = None if analysis_directory is None else os.path.join(os.path.abspath(analysis_directory), relative_path + ".xml")
1076
1077            protected_file_bytes, analysis_report_bytes = self.protect_and_analyse_file(
1078                input_file=input_file,
1079                output_file=output_file,
1080                output_analysis_report=output_analysis_report,
1081                content_management_policy=content_management_policy,
1082                raise_unsupported=raise_unsupported,
1083            )
1084
1085            result_dict[relative_path] = (protected_file_bytes, analysis_report_bytes)
1086
1087        return result_dict
1088
1089    def _GW2RegisterExportFile(self, session: int, output_file: str):
1090        """ Register an export output file for the given session.
1091
1092        Args:
1093            session (int): The session integer.
1094            output_file (str): The export output file path.
1095
1096        Returns:
1097            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.
1098        """
1099        # API function declaration
1100        self.library.GW2RegisterExportFile.argtypes = [
1101            ct.c_size_t,  # Session_Handle session
1102            ct.c_char_p  # const char * exportFilePath
1103        ]
1104
1105        # Variable initialisation
1106        gw_return_object = glasswall.GwReturnObj()
1107        gw_return_object.session = ct.c_size_t(session)
1108        gw_return_object.output_file = ct.c_char_p(output_file.encode("utf-8"))
1109
1110        # API Call
1111        gw_return_object.status = self.library.GW2RegisterExportFile(
1112            gw_return_object.session,
1113            gw_return_object.output_file
1114        )
1115
1116        return gw_return_object
1117
1118    def _GW2RegisterExportMemory(self, session: int):
1119        """ Register an export output file in memory for the given session.
1120
1121        Args:
1122            session (int): The session integer.
1123
1124        Returns:
1125            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
1126        """
1127        # API function declaration
1128        self.library.GW2RegisterExportMemory.argtypes = [
1129            ct.c_size_t,  # Session_Handle session
1130            ct.POINTER(ct.c_void_p),  # char ** exportFileBuffer
1131            ct.POINTER(ct.c_size_t)  # size_t * exportLength
1132        ]
1133
1134        # Variable initialisation
1135        gw_return_object = glasswall.GwReturnObj()
1136        gw_return_object.session = ct.c_size_t(session)
1137        gw_return_object.buffer = ct.c_void_p()
1138        gw_return_object.buffer_length = ct.c_size_t()
1139
1140        # API call
1141        gw_return_object.status = self.library.GW2RegisterExportMemory(
1142            gw_return_object.session,
1143            ct.byref(gw_return_object.buffer),
1144            ct.byref(gw_return_object.buffer_length)
1145        )
1146
1147        return gw_return_object
1148
1149    def register_export(self, session: int, output_file: Optional[str] = None):
1150        """ Registers a file to be exported for the given session. The export file will be created during the session's run_session call.
1151
1152        Args:
1153            session (int): The session integer.
1154            output_file (Optional[str]): Default None. The file path where the export will be written. None exports the file in memory.
1155
1156        Returns:
1157            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attribute 'status' indicating the result of the function call and 'session', the session integer. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size.
1158        """
1159        if not isinstance(output_file, (type(None), str)):
1160            raise TypeError(output_file)
1161
1162        if isinstance(output_file, str):
1163            output_file = os.path.abspath(output_file)
1164
1165            result = self._GW2RegisterExportFile(session, output_file)
1166
1167        elif isinstance(output_file, type(None)):
1168            result = self._GW2RegisterExportMemory(session)
1169
1170        if result.status not in successes.success_codes:
1171            log.error(format_object(result))
1172            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
1173        else:
1174            log.debug(format_object(result))
1175
1176        return result
1177
1178    def export_file(
1179        self,
1180        input_file: Union[str, bytes, bytearray, io.BytesIO],
1181        output_file: Optional[str] = None,
1182        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1183        raise_unsupported: bool = True,
1184    ):
1185        """ Export a file, returning the .zip file bytes. The .zip file is written to output_file if it is provided.
1186
1187        Args:
1188            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
1189            output_file (Optional[str]): The output file path where the .zip file will be written.
1190            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
1191            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1192
1193        Returns:
1194            file_bytes (bytes): The exported .zip file.
1195        """
1196        # Validate arg types
1197        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
1198            raise TypeError(input_file)
1199        if not isinstance(output_file, (type(None), str)):
1200            raise TypeError(output_file)
1201        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
1202            raise TypeError(content_management_policy)
1203        if not isinstance(raise_unsupported, bool):
1204            raise TypeError(raise_unsupported)
1205
1206        # Convert string path arguments to absolute paths
1207        if isinstance(input_file, str):
1208            if not os.path.isfile(input_file):
1209                raise FileNotFoundError(input_file)
1210            input_file = os.path.abspath(input_file)
1211        if isinstance(output_file, str):
1212            output_file = os.path.abspath(output_file)
1213            # make directories that do not exist
1214            os.makedirs(os.path.dirname(output_file), exist_ok=True)
1215        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
1216            content_management_policy = os.path.abspath(content_management_policy)
1217
1218        # Convert memory inputs to bytes
1219        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
1220            input_file = utils.as_bytes(input_file)
1221
1222        with utils.CwdHandler(self.library_path):
1223            with self.new_session() as session:
1224                content_management_policy = self.set_content_management_policy(session, content_management_policy)
1225                register_input = self.register_input(session, input_file)
1226                register_export = self.register_export(session, output_file)
1227                status = self.run_session(session)
1228
1229                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1230                if status not in successes.success_codes:
1231                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
1232                    if raise_unsupported:
1233                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1234                    else:
1235                        file_bytes = None
1236                else:
1237                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
1238                    # Get file bytes
1239                    if isinstance(output_file, str):
1240                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1241                        if not os.path.isfile(output_file):
1242                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1243                            file_bytes = None
1244                        else:
1245                            with open(output_file, "rb") as f:
1246                                file_bytes = f.read()
1247                    else:
1248                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1249                        file_bytes = utils.buffer_to_bytes(
1250                            register_export.buffer,
1251                            register_export.buffer_length
1252                        )
1253
1254                # Ensure memory allocated is not garbage collected
1255                content_management_policy, register_input, register_export
1256
1257                return file_bytes
1258
1259    def export_directory(
1260        self,
1261        input_directory: str,
1262        output_directory: Optional[str] = None,
1263        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1264        raise_unsupported: bool = True
1265    ):
1266        """ Exports all files in a directory and its subdirectories. The export files are written to output_directory maintaining the same directory structure as input_directory.
1267
1268        Args:
1269            input_directory (str): The input directory containing files to export.
1270            output_directory (Optional[str]): The output directory where the export files will be written, or None to not write files.
1271            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
1272            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1273
1274        Returns:
1275            export_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.
1276        """
1277        export_files_dict = {}
1278        # Call export_file on each file in input_directory to output_directory
1279        for input_file in utils.list_file_paths(input_directory):
1280            relative_path = os.path.relpath(input_file, input_directory) + ".zip"
1281            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
1282
1283            export_bytes = self.export_file(
1284                input_file=input_file,
1285                output_file=output_file,
1286                raise_unsupported=raise_unsupported,
1287                content_management_policy=content_management_policy,
1288            )
1289
1290            export_files_dict[relative_path] = export_bytes
1291
1292        return export_files_dict
1293
1294    def export_and_analyse_file(
1295        self,
1296        input_file: Union[str, bytes, bytearray, io.BytesIO],
1297        output_file: Optional[str] = None,
1298        output_analysis_report: Optional[str] = None,
1299        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1300        raise_unsupported: bool = True,
1301    ):
1302        """ Exports and analyses a file in a single session, returning both exported .zip bytes and analysis report bytes.
1303
1304        Args:
1305            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
1306            output_file (Optional[str]): The output file path where the .zip export will be written.
1307            output_analysis_report (Optional[str]): The output file path where the XML analysis report will be written.
1308            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
1309            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1310
1311        Returns:
1312            Tuple[Optional[bytes], Optional[bytes]]: A tuple of (export_file_bytes, analysis_report_bytes).
1313        """
1314        # Validate arg types
1315        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
1316            raise TypeError(f"input_file expected to be of type: str, bytes, bytearray, io.BytesIO. Got: {type(input_file).__name__}")
1317        if not isinstance(output_file, (type(None), str)):
1318            raise TypeError(f"output_file expected to be of type: None, str. Got: {type(output_file).__name__}")
1319        if not isinstance(output_analysis_report, (type(None), str)):
1320            raise TypeError(f"output_analysis_report expected to be of type: None, str. Got: {type(output_analysis_report).__name__}")
1321        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
1322            raise TypeError(f"content_management_policy expected to be of type: None, str, bytes, bytearray, io.BytesIO, Policy. Got: {type(content_management_policy).__name__}")
1323        if not isinstance(raise_unsupported, bool):
1324            raise TypeError(f"raise_unsupported expected to be of type: bool. Got: {type(raise_unsupported).__name__}")
1325
1326        # Convert string path arguments to absolute paths
1327        if isinstance(input_file, str):
1328            if not os.path.isfile(input_file):
1329                raise FileNotFoundError(input_file)
1330            input_file = os.path.abspath(input_file)
1331        if isinstance(output_file, str):
1332            output_file = os.path.abspath(output_file)
1333            os.makedirs(os.path.dirname(output_file), exist_ok=True)
1334        if isinstance(output_analysis_report, str):
1335            output_analysis_report = os.path.abspath(output_analysis_report)
1336            os.makedirs(os.path.dirname(output_analysis_report), exist_ok=True)
1337        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
1338            content_management_policy = os.path.abspath(content_management_policy)
1339
1340        # Convert memory inputs to bytes
1341        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
1342            input_file = utils.as_bytes(input_file)
1343
1344        with utils.CwdHandler(self.library_path):
1345            with self.new_session() as session:
1346                content_management_policy = self.set_content_management_policy(session, content_management_policy)
1347                register_input = self.register_input(session, input_file)
1348                register_export = self.register_export(session, output_file)
1349                register_analysis = self.register_analysis(session, output_analysis_report)
1350
1351                status = self.run_session(session)
1352
1353                export_file_bytes = None
1354                analysis_report_bytes = None
1355
1356                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1357                if status not in successes.success_codes:
1358                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1359                    if raise_unsupported:
1360                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1361
1362                # Get analysis report file bytes, even on processing failure
1363                if isinstance(output_analysis_report, str):
1364                    # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1365                    if not os.path.isfile(output_analysis_report):
1366                        log.error(f"Editor returned success code: {status} but no output file was found: {output_analysis_report}")
1367                    else:
1368                        with open(output_analysis_report, "rb") as f:
1369                            analysis_report_bytes = f.read()
1370                else:
1371                    # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1372                    if register_analysis.buffer and register_analysis.buffer_length:
1373                        analysis_report_bytes = utils.buffer_to_bytes(
1374                            register_analysis.buffer,
1375                            register_analysis.buffer_length
1376                        )
1377
1378                # On success, get export file bytes
1379                if status in successes.success_codes:
1380                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1381                    # Get export file bytes
1382                    if isinstance(output_file, str):
1383                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1384                        if not os.path.isfile(output_file):
1385                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1386                        else:
1387                            with open(output_file, "rb") as f:
1388                                export_file_bytes = f.read()
1389                    else:
1390                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1391                        export_file_bytes = utils.buffer_to_bytes(
1392                            register_export.buffer,
1393                            register_export.buffer_length
1394                        )
1395
1396                # Ensure memory allocated is not garbage collected
1397                content_management_policy, register_input, register_export, register_analysis
1398
1399                return export_file_bytes, analysis_report_bytes
1400
1401    def export_and_analyse_directory(
1402        self,
1403        input_directory: str,
1404        output_directory: Optional[str] = None,
1405        analysis_directory: Optional[str] = None,
1406        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1407        raise_unsupported: bool = True,
1408    ):
1409        """ Recursively processes all files in a directory using export and analyse mode with the given content management policy.
1410        Outputs are written to output_directory and analysis_directory maintaining the same structure as input_directory.
1411
1412        Args:
1413            input_directory (str): The input directory containing files to process.
1414            output_directory (Optional[str]): The output directory for exported .zip files.
1415            analysis_directory (Optional[str]): The output directory for XML analysis reports.
1416            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply.
1417            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1418
1419        Returns:
1420            result_dict (dict): A dictionary mapping relative file paths to tuples of (export_file_bytes, analysis_report_bytes).
1421        """
1422        result_dict = {}
1423
1424        for input_file in utils.list_file_paths(input_directory):
1425            relative_path = os.path.relpath(input_file, input_directory)
1426            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path + ".zip")
1427            output_analysis_report = None if analysis_directory is None else os.path.join(os.path.abspath(analysis_directory), relative_path + ".xml")
1428
1429            export_file_bytes, analysis_report_bytes = self.export_and_analyse_file(
1430                input_file=input_file,
1431                output_file=output_file,
1432                output_analysis_report=output_analysis_report,
1433                content_management_policy=content_management_policy,
1434                raise_unsupported=raise_unsupported,
1435            )
1436
1437            result_dict[relative_path] = (export_file_bytes, analysis_report_bytes)
1438
1439        return result_dict
1440
1441    def _GW2RegisterImportFile(self, session: int, input_file: str):
1442        """ Register an import input file for the given session.
1443
1444        Args:
1445            session (int): The session integer.
1446            input_file (str): The input import file path.
1447
1448        Returns:
1449            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.
1450        """
1451        # API function declaration
1452        self.library.GW2RegisterImportFile.argtypes = [
1453            ct.c_size_t,  # Session_Handle session
1454            ct.c_char_p  # const char * importFilePath
1455        ]
1456
1457        # Variable initialisation
1458        gw_return_object = glasswall.GwReturnObj()
1459        gw_return_object.session = ct.c_size_t(session)
1460        gw_return_object.input_file = ct.c_char_p(input_file.encode("utf-8"))
1461
1462        # API Call
1463        gw_return_object.status = self.library.GW2RegisterImportFile(
1464            gw_return_object.session,
1465            gw_return_object.input_file
1466        )
1467
1468        return gw_return_object
1469
1470    def _GW2RegisterImportMemory(self, session: int, input_file: bytes):
1471        """ Register an import input file in memory for the given session.
1472
1473        Args:
1474            session (int): The session integer.
1475            input_file (str): The input import file in memory.
1476
1477        Returns:
1478            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
1479        """
1480        # API function declaration
1481        self.library.GW2RegisterImportMemory.argtypes = [
1482            ct.c_size_t,  # Session_Handle session
1483            ct.c_void_p,  # char * importFileBuffer
1484            ct.c_size_t  # size_t importLength
1485        ]
1486
1487        # Variable initialisation
1488        gw_return_object = glasswall.GwReturnObj()
1489        gw_return_object.session = ct.c_size_t(session)
1490        gw_return_object.buffer = ct.c_char_p(input_file)
1491        gw_return_object.buffer_length = ct.c_size_t(len(input_file))
1492
1493        # API call
1494        gw_return_object.status = self.library.GW2RegisterImportMemory(
1495            gw_return_object.session,
1496            gw_return_object.buffer,
1497            gw_return_object.buffer_length
1498        )
1499
1500        return gw_return_object
1501
1502    def register_import(self, session: int, input_file: Union[str, bytes, bytearray, io.BytesIO]):
1503        """ Registers a .zip file to be imported for the given session. The constructed file will be created during the session's run_session call.
1504
1505        Args:
1506            session (int): The session integer.
1507            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input import file path or bytes.
1508
1509        Returns:
1510            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attribute 'status' indicating the result of the function call. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size.
1511        """
1512        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO,)):
1513            raise TypeError(input_file)
1514
1515        if isinstance(input_file, str):
1516            if not os.path.isfile(input_file):
1517                raise FileNotFoundError(input_file)
1518
1519            input_file = os.path.abspath(input_file)
1520
1521            result = self._GW2RegisterImportFile(session, input_file)
1522
1523        elif isinstance(input_file, (bytes, bytearray, io.BytesIO,)):
1524            # Convert bytearray and io.BytesIO to bytes
1525            input_file = utils.as_bytes(input_file)
1526
1527            result = self._GW2RegisterImportMemory(session, input_file)
1528
1529        if result.status not in successes.success_codes:
1530            log.error(format_object(result))
1531            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
1532        else:
1533            log.debug(format_object(result))
1534
1535        return result
1536
1537    def import_file(
1538        self,
1539        input_file: Union[str, bytes, bytearray, io.BytesIO],
1540        output_file: Optional[str] = None,
1541        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1542        raise_unsupported: bool = True,
1543    ):
1544        """ Import a .zip file, constructs a file from the .zip file and returns the file bytes. The file is written to output_file if it is provided.
1545
1546        Args:
1547            input_file (Union[str, bytes, bytearray, io.BytesIO]): The .zip input file path or bytes.
1548            output_file (Optional[str]): The output file path where the constructed file will be written.
1549            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
1550            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1551
1552        Returns:
1553            file_bytes (bytes): The imported file bytes.
1554        """
1555        # Validate arg types
1556        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
1557            raise TypeError(input_file)
1558        if not isinstance(output_file, (type(None), str)):
1559            raise TypeError(output_file)
1560        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
1561            raise TypeError(content_management_policy)
1562        if not isinstance(raise_unsupported, bool):
1563            raise TypeError(raise_unsupported)
1564
1565        # Convert string path arguments to absolute paths
1566        if isinstance(input_file, str):
1567            if not os.path.isfile(input_file):
1568                raise FileNotFoundError(input_file)
1569            input_file = os.path.abspath(input_file)
1570        if isinstance(output_file, str):
1571            output_file = os.path.abspath(output_file)
1572            # make directories that do not exist
1573            os.makedirs(os.path.dirname(output_file), exist_ok=True)
1574        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
1575            content_management_policy = os.path.abspath(content_management_policy)
1576
1577        # Convert memory inputs to bytes
1578        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
1579            input_file = utils.as_bytes(input_file)
1580
1581        with utils.CwdHandler(self.library_path):
1582            with self.new_session() as session:
1583                content_management_policy = self.set_content_management_policy(session, content_management_policy)
1584                register_import = self.register_import(session, input_file)
1585                register_output = self.register_output(session, output_file)
1586                status = self.run_session(session)
1587
1588                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1589                if status not in successes.success_codes:
1590                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
1591                    if raise_unsupported:
1592                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1593                    else:
1594                        file_bytes = None
1595                else:
1596                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
1597                    # Get file bytes
1598                    if isinstance(output_file, str):
1599                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1600                        if not os.path.isfile(output_file):
1601                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1602                            file_bytes = None
1603                        else:
1604                            with open(output_file, "rb") as f:
1605                                file_bytes = f.read()
1606                    else:
1607                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1608                        file_bytes = utils.buffer_to_bytes(
1609                            register_output.buffer,
1610                            register_output.buffer_length
1611                        )
1612
1613                # Ensure memory allocated is not garbage collected
1614                content_management_policy, register_import, register_output
1615
1616                return file_bytes
1617
1618    def import_directory(
1619        self,
1620        input_directory: str,
1621        output_directory: Optional[str] = None,
1622        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1623        raise_unsupported: bool = True,
1624    ):
1625        """ Imports all files in a directory and its subdirectories. Files are expected as .zip but this is not forced.
1626        The constructed files are written to output_directory maintaining the same directory structure as input_directory.
1627
1628        Args:
1629            input_directory (str): The input directory containing files to import.
1630            output_directory (Optional[str]): The output directory where the constructed files will be written, or None to not write files.
1631            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
1632            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1633
1634        Returns:
1635            import_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.
1636        """
1637        import_files_dict = {}
1638        # Call import_file on each file in input_directory to output_directory
1639        for input_file in utils.list_file_paths(input_directory):
1640            relative_path = os.path.relpath(input_file, input_directory)
1641            # Remove .zip extension from relative_path
1642            if relative_path.endswith(".zip"):
1643                relative_path = os.path.splitext(relative_path)[0]
1644            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
1645
1646            import_bytes = self.import_file(
1647                input_file=input_file,
1648                output_file=output_file,
1649                raise_unsupported=raise_unsupported,
1650                content_management_policy=content_management_policy,
1651            )
1652
1653            import_files_dict[relative_path] = import_bytes
1654
1655        return import_files_dict
1656
1657    def import_and_analyse_file(
1658        self,
1659        input_file: Union[str, bytes, bytearray, io.BytesIO],
1660        output_file: Optional[str] = None,
1661        output_analysis_report: Optional[str] = None,
1662        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1663        raise_unsupported: bool = True,
1664    ):
1665        """ Imports and analyses a file in a single session, returning both imported file bytes and analysis report bytes.
1666
1667        Args:
1668            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
1669            output_file (Optional[str]): The output file path where the imported file will be written.
1670            output_analysis_report (Optional[str]): The output file path where the XML analysis report will be written.
1671            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
1672            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1673
1674        Returns:
1675            Tuple[Optional[bytes], Optional[bytes]]: A tuple of (import_file_bytes, analysis_report_bytes).
1676        """
1677        # Validate arg types
1678        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
1679            raise TypeError(f"input_file expected to be of type: str, bytes, bytearray, io.BytesIO. Got: {type(input_file).__name__}")
1680        if not isinstance(output_file, (type(None), str)):
1681            raise TypeError(f"output_file expected to be of type: None, str. Got: {type(output_file).__name__}")
1682        if not isinstance(output_analysis_report, (type(None), str)):
1683            raise TypeError(f"output_analysis_report expected to be of type: None, str. Got: {type(output_analysis_report).__name__}")
1684        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
1685            raise TypeError(f"content_management_policy expected to be of type: None, str, bytes, bytearray, io.BytesIO, Policy. Got: {type(content_management_policy).__name__}")
1686        if not isinstance(raise_unsupported, bool):
1687            raise TypeError(f"raise_unsupported expected to be of type: bool. Got: {type(raise_unsupported).__name__}")
1688
1689        # Convert string path arguments to absolute paths
1690        if isinstance(input_file, str):
1691            if not os.path.isfile(input_file):
1692                raise FileNotFoundError(input_file)
1693            input_file = os.path.abspath(input_file)
1694        if isinstance(output_file, str):
1695            output_file = os.path.abspath(output_file)
1696            os.makedirs(os.path.dirname(output_file), exist_ok=True)
1697        if isinstance(output_analysis_report, str):
1698            output_analysis_report = os.path.abspath(output_analysis_report)
1699            os.makedirs(os.path.dirname(output_analysis_report), exist_ok=True)
1700        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
1701            content_management_policy = os.path.abspath(content_management_policy)
1702
1703        # Convert memory inputs to bytes
1704        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
1705            input_file = utils.as_bytes(input_file)
1706
1707        with utils.CwdHandler(self.library_path):
1708            with self.new_session() as session:
1709                content_management_policy = self.set_content_management_policy(session, content_management_policy)
1710                register_import = self.register_import(session, input_file)
1711                register_output = self.register_output(session, output_file)
1712                register_analysis = self.register_analysis(session, output_analysis_report)
1713
1714                status = self.run_session(session)
1715
1716                import_file_bytes = None
1717                analysis_report_bytes = None
1718
1719                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1720                if status not in successes.success_codes:
1721                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1722                    if raise_unsupported:
1723                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1724
1725                # Get analysis report file bytes, even on processing failure
1726                if isinstance(output_analysis_report, str):
1727                    # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1728                    if not os.path.isfile(output_analysis_report):
1729                        log.error(f"Editor returned success code: {status} but no output file was found: {output_analysis_report}")
1730                    else:
1731                        with open(output_analysis_report, "rb") as f:
1732                            analysis_report_bytes = f.read()
1733                else:
1734                    # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1735                    if register_analysis.buffer and register_analysis.buffer_length:
1736                        analysis_report_bytes = utils.buffer_to_bytes(
1737                            register_analysis.buffer,
1738                            register_analysis.buffer_length
1739                        )
1740
1741                # On success, get import file bytes
1742                if status in successes.success_codes:
1743                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1744                    # Get import file bytes
1745                    if isinstance(output_file, str):
1746                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1747                        if not os.path.isfile(output_file):
1748                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1749                        else:
1750                            with open(output_file, "rb") as f:
1751                                import_file_bytes = f.read()
1752                    else:
1753                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1754                        import_file_bytes = utils.buffer_to_bytes(
1755                            register_import.buffer,
1756                            register_import.buffer_length
1757                        )
1758
1759                # Ensure memory allocated is not garbage collected
1760                content_management_policy, register_import, register_output, register_analysis
1761
1762                return import_file_bytes, analysis_report_bytes
1763
1764    def import_and_analyse_directory(
1765        self,
1766        input_directory: str,
1767        output_directory: Optional[str] = None,
1768        analysis_directory: Optional[str] = None,
1769        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1770        raise_unsupported: bool = True,
1771    ):
1772        """ Recursively processes all files in a directory using import and analyse mode with the given content management policy.
1773        Outputs are written to output_directory and analysis_directory maintaining the same structure as input_directory.
1774
1775        Args:
1776            input_directory (str): The input directory containing export .zip files to process.
1777            output_directory (Optional[str]): The output directory for imported files.
1778            analysis_directory (Optional[str]): The output directory for XML analysis reports.
1779            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply.
1780            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1781
1782        Returns:
1783            result_dict (dict): A dictionary mapping relative file paths to tuples of (import_file_bytes, analysis_report_bytes).
1784        """
1785        result_dict = {}
1786
1787        for input_file in utils.list_file_paths(input_directory):
1788            relative_path = os.path.relpath(input_file, input_directory)
1789            # Remove .zip extension from relative_path
1790            if relative_path.endswith(".zip"):
1791                relative_path = os.path.splitext(relative_path)[0]
1792            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
1793            output_analysis_report = None if analysis_directory is None else os.path.join(os.path.abspath(analysis_directory), relative_path + ".xml")
1794
1795            import_file_bytes, analysis_report_bytes = self.import_and_analyse_file(
1796                input_file=input_file,
1797                output_file=output_file,
1798                output_analysis_report=output_analysis_report,
1799                content_management_policy=content_management_policy,
1800                raise_unsupported=raise_unsupported,
1801            )
1802
1803            result_dict[relative_path] = (import_file_bytes, analysis_report_bytes)
1804
1805        return result_dict
1806
1807    def _GW2FileErrorMsg(self, session: int):
1808        """ Retrieve the Glasswall Session Process error message.
1809
1810        Args:
1811            session (int): The session integer.
1812
1813        Returns:
1814            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status', 'error_message'.
1815        """
1816        # API function declaration
1817        self.library.GW2FileErrorMsg.argtypes = [
1818            ct.c_size_t,  # Session_Handle session
1819            ct.POINTER(ct.c_void_p),  # char **errorMsgBuffer
1820            ct.POINTER(ct.c_size_t)  # size_t *errorMsgBufferLength
1821        ]
1822
1823        # Variable initialisation
1824        gw_return_object = glasswall.GwReturnObj()
1825        gw_return_object.session = ct.c_size_t(session)
1826        gw_return_object.buffer = ct.c_void_p()
1827        gw_return_object.buffer_length = ct.c_size_t()
1828
1829        # API call
1830        gw_return_object.status = self.library.GW2FileErrorMsg(
1831            gw_return_object.session,
1832            ct.byref(gw_return_object.buffer),
1833            ct.byref(gw_return_object.buffer_length)
1834        )
1835
1836        # Editor wrote to a buffer, convert it to bytes
1837        error_bytes = utils.buffer_to_bytes(
1838            gw_return_object.buffer,
1839            gw_return_object.buffer_length
1840        )
1841
1842        gw_return_object.error_message = error_bytes.decode()
1843
1844        return gw_return_object
1845
1846    @functools.lru_cache()
1847    def file_error_message(self, session: int) -> str:
1848        """ Retrieve the Glasswall Session Process error message.
1849
1850        Args:
1851            session (int): The session integer.
1852
1853        Returns:
1854            error_message (str): The Glasswall Session Process error message.
1855        """
1856        # Validate arg types
1857        if not isinstance(session, int):
1858            raise TypeError(session)
1859
1860        result = self._GW2FileErrorMsg(session)
1861
1862        if result.status not in successes.success_codes:
1863            log.error(format_object(result))
1864            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
1865        else:
1866            log.debug(format_object(result))
1867
1868        return result.error_message
1869
1870    def GW2GetFileType(self, session: int, file_type_id):
1871        """ Retrieve the file type as a string.
1872
1873        Args:
1874            session (int): The session integer.
1875            file_type_id (int): The file type id.
1876
1877        Returns:
1878            file_type (str): The formal file name for the corresponding file id.
1879        """
1880        # Validate arg types
1881        if not isinstance(session, int):
1882            raise TypeError(session)
1883
1884        # API function declaration
1885        self.library.GW2GetFileType.argtypes = [
1886            ct.c_size_t,
1887            ct.c_size_t,
1888            ct.POINTER(ct.c_size_t),
1889            ct.POINTER(ct.c_void_p)
1890        ]
1891
1892        # Variable initialisation
1893        ct_session = ct.c_size_t(session)
1894        ct_file_type = ct.c_size_t(file_type_id)
1895        ct_buffer_length = ct.c_size_t()
1896        ct_buffer = ct.c_void_p()
1897
1898        # API call
1899        status = self.library.GW2GetFileType(
1900            ct_session,
1901            ct_file_type,
1902            ct.byref(ct_buffer_length),
1903            ct.byref(ct_buffer)
1904        )
1905
1906        if status not in successes.success_codes:
1907            log.error(f"\n\tsession: {session}\n\tstatus: {status}")
1908            raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1909        else:
1910            log.debug(f"\n\tsession: {session}\n\tstatus: {status}")
1911
1912        # Editor wrote to a buffer, convert it to bytes
1913        file_type_bytes = utils.buffer_to_bytes(
1914            ct_buffer,
1915            ct_buffer_length
1916        )
1917
1918        file_type = file_type_bytes.decode()
1919
1920        return file_type
1921
1922    def GW2GetFileTypeID(self, session: int, file_type_str):
1923        """ Retrieve the Glasswall file type id given a file type string.
1924
1925        Args:
1926            session (int): The session integer.
1927            file_type_str (str): The file type as a string.
1928
1929        Returns:
1930            file_type_id (str): The Glasswall file type id for the specified file type.
1931        """
1932        # Validate arg types
1933        if not isinstance(session, int):
1934            raise TypeError(session)
1935
1936        # API function declaration
1937        self.library.GW2GetFileTypeID.argtypes = [
1938            ct.c_size_t,
1939            ct.c_char_p,
1940            ct.POINTER(ct.c_size_t),
1941            ct.POINTER(ct.c_void_p)
1942        ]
1943
1944        # Variable initialisation
1945        ct_session = ct.c_size_t(session)
1946        ct_file_type = ct.c_char_p(file_type_str.encode('utf-8'))
1947        ct_buffer_length = ct.c_size_t()
1948        ct_buffer = ct.c_void_p()
1949
1950        # API call
1951        status = self.library.GW2GetFileTypeID(
1952            ct_session,
1953            ct_file_type,
1954            ct.byref(ct_buffer_length),
1955            ct.byref(ct_buffer)
1956        )
1957
1958        if status not in successes.success_codes:
1959            log.error(f"\n\tsession: {session}\n\tstatus: {status}")
1960            raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1961        else:
1962            log.debug(f"\n\tsession: {session}\n\tstatus: {status}")
1963
1964        # Editor wrote to a buffer, convert it to bytes
1965        file_type_bytes = utils.buffer_to_bytes(
1966            ct_buffer,
1967            ct_buffer_length
1968        )
1969
1970        file_type_id = file_type_bytes.decode()
1971
1972        return file_type_id
1973
1974    def get_file_type_info(self, file_type: Union[str, int]):
1975        """ Retrieve information about a file type based on its identifier.
1976
1977        Args:
1978            file_type (Union[str, int]): The file type identifier. This can be either a string representing a file
1979            extension (e.g. 'bmp') or an integer corresponding to a file type (e.g. 29).
1980
1981        Returns:
1982            - file_type_info (Union[int, str]): Depending on the input 'file_type':
1983                - If `file_type` is a string (e.g. 'bmp'):
1984                    - If the file type is recognised, returns an integer corresponding to that file type.
1985                    - If the file type is not recognised, returns 0.
1986                - If `file_type` is an integer (e.g. 29):
1987                    - If the integer corresponds to a recognised file type, returns a more detailed string description
1988                        of the file type (e.g. 'BMP Image').
1989                    - If the integer does not match any recognised file type, returns an empty string.
1990        """
1991        # Validate arg types
1992        if not isinstance(file_type, (str, int)):
1993            raise TypeError(file_type)
1994
1995        with utils.CwdHandler(self.library_path):
1996            with self.new_session() as session:
1997
1998                if isinstance(file_type, int):
1999                    file_type_info = self.GW2GetFileType(session, file_type)
2000                if isinstance(file_type, str):
2001                    file_type_info = self.GW2GetFileTypeID(session, file_type)
2002
2003                return file_type_info
2004
2005    @utils.deprecated_function(replacement_function=get_file_type_info)
2006    def get_file_info(self, *args, **kwargs):
2007        """ Deprecated in 1.0.6. Use get_file_type_info. """
2008        pass
2009
2010    def _GW2RegisterReportFile(self, session: int, output_file: str):
2011        """ Register an output report file path for the given session.
2012
2013        Args:
2014            session (int): The session integer.
2015            output_file (str): The file path of the output report file.
2016
2017        Returns:
2018            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.
2019        """
2020        # API function declaration
2021        self.library.GW2RegisterReportFile.argtypes = [
2022            ct.c_size_t,  # Session_Handle session
2023            ct.c_char_p,  # const char * reportFilePathName
2024        ]
2025
2026        # Variable initialisation
2027        gw_return_object = glasswall.GwReturnObj()
2028        gw_return_object.session = ct.c_size_t(session)
2029        gw_return_object.output_file = ct.c_char_p(output_file.encode("utf-8"))
2030
2031        # API call
2032        gw_return_object.status = self.library.GW2RegisterReportFile(
2033            gw_return_object.session,
2034            gw_return_object.output_file
2035        )
2036
2037        return gw_return_object
2038
2039    def register_report_file(self, session: int, output_file: str):
2040        """ Register the report file path for the given session.
2041
2042        Args:
2043            session (int): The session integer.
2044            output_file (str): The file path of the report file.
2045
2046        Returns:
2047            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.
2048        """
2049        # Validate arg types
2050        if not isinstance(session, int):
2051            raise TypeError(session)
2052        if not isinstance(output_file, (type(None), str)):
2053            raise TypeError(output_file)
2054
2055        result = self._GW2RegisterReportFile(session, output_file)
2056
2057        if result.status not in successes.success_codes:
2058            log.error(format_object(result))
2059            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2060        else:
2061            log.debug(format_object(result))
2062
2063        return result
2064
2065    def _GW2GetIdInfo(self, session: int, issue_id: int):
2066        """ Retrieves the group description for the given Issue ID. e.g. issue_id 96 returns "Document Processing Instances"
2067
2068        Args:
2069            session (int): The session integer.
2070            issue_id (int): The issue id.
2071            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
2072
2073        Returns:
2074            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'issue_id', 'buffer_length', 'buffer', 'status', 'id_info'.
2075        """
2076        # API function declaration
2077        self.library.GW2GetIdInfo.argtypes = [
2078            ct.c_size_t,  # Session_Handle session
2079            ct.c_size_t,  # size_t issueId
2080            ct.POINTER(ct.c_size_t),  # size_t * bufferLength
2081            ct.POINTER(ct.c_void_p)  # char ** outputBuffer
2082        ]
2083
2084        # Variable initialisation
2085        gw_return_object = glasswall.GwReturnObj()
2086        gw_return_object.session = ct.c_size_t(session)
2087        gw_return_object.issue_id = ct.c_size_t(issue_id)
2088        gw_return_object.buffer_length = ct.c_size_t()
2089        gw_return_object.buffer = ct.c_void_p()
2090
2091        # API call
2092        gw_return_object.status = self.library.GW2GetIdInfo(
2093            gw_return_object.session,
2094            gw_return_object.issue_id,
2095            ct.byref(gw_return_object.buffer_length),
2096            ct.byref(gw_return_object.buffer)
2097        )
2098
2099        # Editor wrote to a buffer, convert it to bytes
2100        id_info_bytes = utils.buffer_to_bytes(
2101            gw_return_object.buffer,
2102            gw_return_object.buffer_length
2103        )
2104
2105        gw_return_object.id_info = id_info_bytes.decode()
2106
2107        return gw_return_object
2108
2109    def get_id_info(self, issue_id: int, raise_unsupported: bool = True):
2110        """ Retrieves the group description for the given Issue ID. e.g. issue_id 96 returns "Document Processing Instances"
2111
2112        Args:
2113            issue_id (int): The issue id.
2114            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
2115
2116        Returns:
2117            id_info (str): The group description for the given Issue ID.
2118        """
2119        # Validate arg types
2120        if not isinstance(issue_id, int):
2121            raise TypeError(issue_id)
2122
2123        with utils.CwdHandler(self.library_path):
2124            with self.new_session() as session:
2125                result = self._GW2GetIdInfo(session, issue_id)
2126
2127                if result.status not in successes.success_codes:
2128                    log.error(format_object(result))
2129                    if raise_unsupported:
2130                        raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2131                else:
2132                    log.debug(format_object(result))
2133
2134                return result.id_info
2135
2136    def _GW2GetAllIdInfo(self, session: int):
2137        """ Retrieves the XML containing all the Issue ID ranges with their group descriptions
2138
2139        Args:
2140            session (int): The session integer.
2141
2142        Returns:
2143            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'analysis_format', 'status', 'all_id_info'.
2144        """
2145
2146        # API function declaration
2147        self.library.GW2GetAllIdInfo.argtypes = [
2148            ct.c_size_t,  # Session_Handle session
2149            ct.POINTER(ct.c_size_t),  # size_t * bufferLength
2150            ct.POINTER(ct.c_void_p)  # char ** outputBuffer
2151        ]
2152
2153        # Variable initialisation
2154        # The extracted issue Id information is stored in the analysis report, register an analysis session.
2155        gw_return_object = self._GW2RegisterAnalysisMemory(session)
2156
2157        # API call
2158        gw_return_object.status = self.library.GW2GetAllIdInfo(
2159            gw_return_object.session,
2160            ct.byref(gw_return_object.buffer_length),
2161            ct.byref(gw_return_object.buffer)
2162        )
2163
2164        # Editor wrote to a buffer, convert it to bytes
2165        all_id_info_bytes = utils.buffer_to_bytes(
2166            gw_return_object.buffer,
2167            gw_return_object.buffer_length
2168        )
2169
2170        gw_return_object.all_id_info = all_id_info_bytes.decode()
2171
2172        return gw_return_object
2173
2174    def get_all_id_info(self, output_file: Optional[str] = None, raise_unsupported: bool = True) -> str:
2175        """ Retrieves the XML containing all the Issue ID ranges with their group descriptions
2176
2177        Args:
2178            output_file (Optional[str]): The output file path where the analysis file will be written.
2179            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
2180
2181        Returns:
2182            all_id_info (str): A string XML analysis report containing all id info.
2183        """
2184        # Validate arg types
2185        if not isinstance(output_file, (type(None), str)):
2186            raise TypeError(output_file)
2187        if isinstance(output_file, str):
2188            output_file = os.path.abspath(output_file)
2189
2190        with utils.CwdHandler(self.library_path):
2191            with self.new_session() as session:
2192                result = self._GW2GetAllIdInfo(session)
2193
2194                if result.status not in successes.success_codes:
2195                    log.error(format_object(result))
2196                    if raise_unsupported:
2197                        raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2198                else:
2199                    log.debug(format_object(result))
2200
2201                if isinstance(output_file, str):
2202                    # GW2GetAllIdInfo is memory only, write to file
2203                    # make directories that do not exist
2204                    os.makedirs(os.path.dirname(output_file), exist_ok=True)
2205                    with open(output_file, "w") as f:
2206                        f.write(result.all_id_info)
2207
2208                return result.all_id_info
2209
2210    def _GW2FileSessionStatus(self, session: int):
2211        """ Retrieves the Glasswall Session Status. Also gives a high level indication of the processing that was carried out on the last document processed by the library
2212
2213        Args:
2214            session (int): The session integer.
2215            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
2216
2217        Returns:
2218            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'session_status', 'buffer', 'buffer_length', 'status', 'message'.
2219        """
2220        # API function declaration
2221        self.library.GW2FileSessionStatus.argtypes = [
2222            ct.c_size_t,  # Session_Handle session
2223            ct.POINTER(ct.c_int),  # int *glasswallSessionStatus
2224            ct.POINTER(ct.c_void_p),  # char **statusMsgBuffer
2225            ct.POINTER(ct.c_size_t)  # size_t *statusbufferLength
2226        ]
2227
2228        # Variable initialisation
2229        gw_return_object = glasswall.GwReturnObj()
2230        gw_return_object.session = ct.c_size_t(session)
2231        gw_return_object.session_status = ct.c_int()
2232        gw_return_object.buffer = ct.c_void_p()
2233        gw_return_object.buffer_length = ct.c_size_t()
2234
2235        # API call
2236        gw_return_object.status = self.library.GW2FileSessionStatus(
2237            gw_return_object.session,
2238            ct.byref(gw_return_object.session_status),
2239            ct.byref(gw_return_object.buffer),
2240            ct.byref(gw_return_object.buffer_length)
2241        )
2242
2243        # Convert session_status to int
2244        gw_return_object.session_status = gw_return_object.session_status.value
2245
2246        # Editor wrote to a buffer, convert it to bytes
2247        message_bytes = utils.buffer_to_bytes(
2248            gw_return_object.buffer,
2249            gw_return_object.buffer_length
2250        )
2251        gw_return_object.message = message_bytes.decode()
2252
2253        return gw_return_object
2254
2255    def file_session_status_message(self, session: int, raise_unsupported: bool = True) -> str:
2256        """ Retrieves the Glasswall session status message. Gives a high level indication of the processing that was carried out.
2257
2258        Args:
2259            session (int): The session integer.
2260            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
2261
2262        Returns:
2263            result.message (str):The file session status message.
2264        """
2265        # Validate arg types
2266        if not isinstance(session, int):
2267            raise TypeError(session)
2268
2269        result = self._GW2FileSessionStatus(session)
2270
2271        if result.status not in successes.success_codes:
2272            log.error(format_object(result))
2273            if raise_unsupported:
2274                raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2275        else:
2276            log.debug(format_object(result))
2277
2278        return result.message
2279
2280    def _GW2LicenceDetails(self, session: int):
2281        """ Returns a human readable string containing licence details.
2282
2283        Args:
2284            session (int): The session integer.
2285
2286        Returns:
2287            licence_details (str): A human readable string representing the relevant information contained in the licence.
2288        """
2289        # API function declaration
2290        self.library.GW2LicenceDetails.argtypes = [ct.c_size_t]
2291        self.library.GW2LicenceDetails.restype = ct.c_char_p
2292
2293        # Variable initialisation
2294        ct_session = ct.c_size_t(session)
2295
2296        # API call
2297        licence_details = self.library.GW2LicenceDetails(ct_session)
2298
2299        # Convert to Python string
2300        licence_details = ct.string_at(licence_details).decode()
2301
2302        return licence_details
2303
2304    def licence_details(self):
2305        """ Returns a string containing details of the licence.
2306
2307        Returns:
2308            result (str): A string containing details of the licence.
2309        """
2310        with self.new_session() as session:
2311            result = self._GW2LicenceDetails(session)
2312
2313            log.debug(f"\n\tsession: {session}\n\tGW2LicenceDetails: {result}")
2314
2315        return result
2316
2317    def _GW2RegisterExportTextDumpMemory(self, session: int):
2318        """ Registers an export text dump to be written in memory.
2319
2320        Args:
2321            session (int): The session integer.
2322
2323        Returns:
2324            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
2325        """
2326        # API function declaration
2327        self.library.GW2RegisterExportTextDumpMemory.argtypes = [
2328            ct.c_size_t,  # Session_Handle session
2329            ct.POINTER(ct.c_void_p),  # char ** exportTextDumpFileBuffer
2330            ct.POINTER(ct.c_size_t)  # size_t * exportTextDumpLength
2331        ]
2332
2333        # Variable initialisation
2334        gw_return_object = glasswall.GwReturnObj()
2335        gw_return_object.session = ct.c_size_t(session)
2336        gw_return_object.buffer = ct.c_void_p()
2337        gw_return_object.buffer_length = ct.c_size_t()
2338
2339        # API call
2340        gw_return_object.status = self.library.GW2RegisterExportTextDumpMemory(
2341            gw_return_object.session,
2342            ct.byref(gw_return_object.buffer),
2343            ct.byref(gw_return_object.buffer_length)
2344        )
2345
2346        return gw_return_object
2347
2348    def _GW2RegisterExportTextDumpFile(self, session: int, output_file: str):
2349        """ Registers an export text dump to be written to file.
2350
2351        Args:
2352            session (int): The session integer.
2353            output_file (str): The file path of the text dump file.
2354
2355        Returns:
2356            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.
2357        """
2358        # API function declaration
2359        self.library.GW2RegisterExportTextDumpFile.argtypes = [
2360            ct.c_size_t,  # Session_Handle session
2361            ct.c_char_p  # const char * textDumpFilePathName
2362        ]
2363
2364        # Variable initialisation
2365        gw_return_object = glasswall.GwReturnObj()
2366        gw_return_object.session = ct.c_size_t(session)
2367        gw_return_object.output_file = ct.c_char_p(output_file.encode("utf-8"))
2368
2369        # API call
2370        gw_return_object.status = self.library.GW2RegisterExportTextDumpFile(
2371            gw_return_object.session,
2372            gw_return_object.output_file
2373        )
2374
2375        return gw_return_object
2376
2377    def _GW2RegisterLicenceFile(self, session: int, input_file: str):
2378        """ Registers a "gwkey.lic" licence from file path.
2379
2380        Args:
2381            session (int): The session integer.
2382            input_file (str): The "gwkey.lic" licence input file path.
2383
2384        Returns:
2385            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'status'.
2386        """
2387        # API function declaration
2388        self.library.GW2RegisterLicenceFile.argtypes = [
2389            ct.c_size_t,  # Session_Handle session
2390            ct.c_char_p,  # const char *filename
2391        ]
2392
2393        # Variable initialisation
2394        gw_return_object = glasswall.GwReturnObj()
2395        gw_return_object.session = ct.c_size_t(session)
2396        gw_return_object.input_file = ct.c_char_p(input_file.encode("utf-8"))
2397
2398        # API call
2399        gw_return_object.status = self.library.GW2RegisterLicenceFile(
2400            gw_return_object.session,
2401            gw_return_object.input_file,
2402        )
2403
2404        return gw_return_object
2405
2406    def _GW2RegisterLicenceMemory(self, session: int, input_file: bytes):
2407        """ Registers a "gwkey.lic" licence from memory.
2408
2409        Args:
2410            session (int): The session integer.
2411            input_file (bytes): The "gwkey.lic" licence input file.
2412
2413        Returns:
2414            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
2415        """
2416        # API function declaration
2417        self.library.GW2RegisterLicenceMemory.argtypes = [
2418            ct.c_size_t,  # Session_Handle session
2419            ct.c_char_p,  # const char *filename
2420            ct.c_size_t,  # size_t licenceLength
2421        ]
2422
2423        # Variable initialisation
2424        gw_return_object = glasswall.GwReturnObj()
2425        gw_return_object.session = ct.c_size_t(session)
2426        gw_return_object.buffer = ct.c_char_p(input_file)
2427        gw_return_object.buffer_length = ct.c_size_t(len(input_file))
2428
2429        # API call
2430        gw_return_object.status = self.library.GW2RegisterLicenceMemory(
2431            gw_return_object.session,
2432            gw_return_object.buffer,
2433            gw_return_object.buffer_length
2434        )
2435
2436        return gw_return_object
2437
2438    def register_licence(self, session: int, input_file: Union[str, bytes, bytearray, io.BytesIO]):
2439        """ Registers a "gwkey.lic" licence from file path or memory.
2440
2441        Args:
2442            session (int): The session integer.
2443            input_file (Union[str, bytes, bytearray, io.BytesIO]): The "gwkey.lic" licence. It can be provided as a file path (str), bytes, bytearray, or a BytesIO object.
2444
2445        Returns:
2446            - result (glasswall.GwReturnObj): Depending on the input 'input_file':
2447                - If input_file is a str file path:
2448                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'status'.
2449
2450                - If input_file is a file in memory:
2451                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
2452        """
2453        if isinstance(input_file, str):
2454            if not os.path.isfile(input_file):
2455                raise FileNotFoundError(input_file)
2456
2457            input_file = os.path.abspath(input_file)
2458
2459            result = self._GW2RegisterLicenceFile(session, input_file)
2460
2461        elif isinstance(input_file, (bytes, bytearray, io.BytesIO,)):
2462            # Convert bytearray and io.BytesIO to bytes
2463            input_file = utils.as_bytes(input_file)
2464
2465            result = self._GW2RegisterLicenceMemory(session, input_file)
2466
2467        else:
2468            raise TypeError(input_file)
2469
2470        if result.status not in successes.success_codes:
2471            log.error(format_object(result))
2472            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2473        else:
2474            log.debug(format_object(result))
2475
2476        return result

A high level Python wrapper for Glasswall Editor / Core2.

Editor( library_path: str, licence: Union[str, bytes, bytearray, _io.BytesIO] = None)
22    def __init__(self, library_path: str, licence: Union[str, bytes, bytearray, io.BytesIO] = None):
23        """ Initialise the Editor instance.
24
25        Args:
26            library_path (str): The file or directory path to the Editor library.
27            licence (str, bytes, bytearray, or io.BytesIO, optional): The licence file content or path. This can be:
28                - A string representing the file path to the licence.
29                - A `bytes` or `bytearray` object containing the licence data.
30                - An `io.BytesIO` object for in-memory licence data.
31                If not specified, it is assumed that the licence file is located in the same directory as the `library_path`.
32        """
33        super().__init__(library_path)
34        self.library = self.load_library(os.path.abspath(library_path))
35        self.licence = licence
36
37        # Validate killswitch has not activated
38        self.validate_licence()
39
40        log.info(f"Loaded Glasswall {self.__class__.__name__} version {self.version()} from {self.library_path}")

Initialise the Editor instance.

Args: library_path (str): The file or directory path to the Editor library. licence (str, bytes, bytearray, or io.BytesIO, optional): The licence file content or path. This can be: - A string representing the file path to the licence. - A bytes or bytearray object containing the licence data. - An io.BytesIO object for in-memory licence data. If not specified, it is assumed that the licence file is located in the same directory as the library_path.

library
licence
def validate_licence(self):
42    def validate_licence(self):
43        """ Validates the licence of the library by checking the licence details.
44
45        Raises:
46            LicenceExpired: If the licence has expired or could not be validated.
47        """
48        licence_details = self.licence_details()
49
50        bad_details = [
51            "Unable to Read Licence Key File",
52            "Licence File Missing Required Contents",
53            "Licence Expired",
54        ]
55
56        if any(bad_detail.lower() in licence_details.lower() for bad_detail in bad_details):
57            # bad_details found in licence_details
58            log.error(f"{self.__class__.__name__} licence validation failed. Licence details:\n{licence_details}")
59            raise errors.LicenceExpired(licence_details)
60        else:
61            log.debug(f"{self.__class__.__name__} licence validated successfully. Licence details:\n{licence_details}")

Validates the licence of the library by checking the licence details.

Raises: LicenceExpired: If the licence has expired or could not be validated.

def version(self):
63    def version(self):
64        """ Returns the Glasswall library version.
65
66        Returns:
67            version (str): The Glasswall library version.
68        """
69        # API function declaration
70        self.library.GW2LibVersion.restype = ct.c_char_p
71
72        # API call
73        version = self.library.GW2LibVersion()
74
75        # Convert to Python string
76        version = ct.string_at(version).decode()
77
78        return version

Returns the Glasswall library version.

Returns: version (str): The Glasswall library version.

def open_session(self):
80    def open_session(self):
81        """ Open a new Glasswall session.
82
83        Returns:
84            session (int): An incrementing integer repsenting the current session.
85        """
86        # API call
87        session = self.library.GW2OpenSession()
88
89        log.debug(f"\n\tsession: {session}")
90
91        if self.licence:
92            # Must register licence on each GW2OpenSession if the licence is not in the same directory as the library
93            self.register_licence(session, self.licence)
94
95        return session

Open a new Glasswall session.

Returns: session (int): An incrementing integer repsenting the current session.

def close_session(self, session: int) -> int:
 97    def close_session(self, session: int) -> int:
 98        """ Close the Glasswall session. All resources allocated by the session will be destroyed.
 99
100        Args:
101            session (int): The session to close.
102
103        Returns:
104            status (int): The status code of the function call.
105        """
106        if not isinstance(session, int):
107            raise TypeError(session)
108
109        # API function declaration
110        self.library.GW2CloseSession.argtypes = [ct.c_size_t]
111
112        # Variable initialisation
113        ct_session = ct.c_size_t(session)
114
115        # API call
116        status = self.library.GW2CloseSession(ct_session)
117
118        if status not in successes.success_codes:
119            log.error(f"\n\tsession: {session}\n\tstatus: {status}")
120        else:
121            log.debug(f"\n\tsession: {session}\n\tstatus: {status}")
122
123        return status

Close the Glasswall session. All resources allocated by the session will be destroyed.

Args: session (int): The session to close.

Returns: status (int): The status code of the function call.

@contextmanager
def new_session(self):
125    @contextmanager
126    def new_session(self):
127        """ Context manager. Opens a new session on entry and closes the session on exit. """
128        try:
129            session = self.open_session()
130            yield session
131        finally:
132            self.close_session(session)

Context manager. Opens a new session on entry and closes the session on exit.

def run_session(self, session):
134    def run_session(self, session):
135        """ Runs the Glasswall session and begins processing of a file.
136
137        Args:
138            session (int): The session to run.
139
140        Returns:
141            status (int): The status code of the function call.
142        """
143        # API function declaration
144        self.library.GW2RunSession.argtypes = [ct.c_size_t]
145
146        # Variable initialisation
147        ct_session = ct.c_size_t(session)
148
149        # API call
150        status = self.library.GW2RunSession(ct_session)
151
152        if status not in successes.success_codes:
153            log.error(f"\n\tsession: {session}\n\tstatus: {status}\n\tGW2FileErrorMsg: {self.file_error_message(session)}")
154        else:
155            log.debug(f"\n\tsession: {session}\n\tstatus: {status}\n\tGW2FileErrorMsg: {self.file_error_message(session)}")
156
157        return status

Runs the Glasswall session and begins processing of a file.

Args: session (int): The session to run.

Returns: status (int): The status code of the function call.

def determine_file_type( self, input_file: Union[str, bytes, bytearray, _io.BytesIO], as_string: bool = False, raise_unsupported: bool = True) -> Union[int, str]:
159    def determine_file_type(self, input_file: Union[str, bytes, bytearray, io.BytesIO], as_string: bool = False, raise_unsupported: bool = True) -> Union[int, str]:
160        """ Determine the file type of a given input file, either as an integer identifier or a string.
161
162        Args:
163            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file to analyse. It can be provided as a file path (str), bytes, bytearray, or a BytesIO object.
164            as_string (bool, optional): Return file type as string, eg: "bmp" instead of: 29. Defaults to False.
165            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
166
167        Returns:
168            file_type (Union[int, str]): The file type.
169        """
170        if isinstance(input_file, str):
171            if not os.path.isfile(input_file):
172                raise FileNotFoundError(input_file)
173
174            # convert to ct.c_char_p of bytes
175            ct_input_file = ct.c_char_p(input_file.encode("utf-8"))
176
177            # API call
178            file_type = self.library.GW2DetermineFileTypeFromFile(ct_input_file)
179
180        elif isinstance(input_file, (bytes, bytearray, io.BytesIO)):
181            # convert to bytes
182            bytes_input_file = utils.as_bytes(input_file)
183
184            # ctypes conversion
185            ct_buffer = ct.c_char_p(bytes_input_file)
186            ct_buffer_length = ct.c_size_t(len(bytes_input_file))
187
188            # API call
189            file_type = self.library.GW2DetermineFileTypeFromMemory(
190                ct_buffer,
191                ct_buffer_length
192            )
193
194        else:
195            raise TypeError(input_file)
196
197        file_type_as_string = dft.file_type_int_to_str(file_type)
198        input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
199
200        if not dft.is_success(file_type):
201            log.warning(f"\n\tfile_type: {file_type}\n\tfile_type_as_string: {file_type_as_string}\n\tinput_file: {input_file_repr}")
202            if raise_unsupported:
203                raise dft.int_class_map.get(file_type, dft.errors.UnknownErrorCode)(file_type)
204        else:
205            log.debug(f"\n\tfile_type: {file_type}\n\tfile_type_as_string: {file_type_as_string}\n\tinput_file: {input_file_repr}")
206
207        if as_string:
208            return file_type_as_string
209
210        return file_type

Determine the file type of a given input file, either as an integer identifier or a string.

Args: input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file to analyse. It can be provided as a file path (str), bytes, bytearray, or a BytesIO object. as_string (bool, optional): Return file type as string, eg: "bmp" instead of: 29. Defaults to False. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: file_type (Union[int, str]): The file type.

def get_content_management_policy(self, session: int):
255    def get_content_management_policy(self, session: int):
256        """ Returns the content management configuration for a given session.
257
258        Args:
259            session (int): The session integer.
260
261        Returns:
262            xml_string (str): The XML string of the current content management configuration.
263        """
264        # NOTE GW2GetPolicySettings is current not implemented in editor
265
266        # set xml_string as loaded default config
267        xml_string = glasswall.content_management.policies.Editor(default="sanitise").text,
268
269        # log.debug(f"xml_string:\n{xml_string}")
270
271        return xml_string
272
273        # # API function declaration
274        # self.library.GW2GetPolicySettings.argtypes = [
275        #     ct.c_size_t,
276        #     ct.c_void_p,
277        # ]
278
279        # # Variable initialisation
280        # ct_session = ct.c_size_t(session)
281        # ct_buffer = ct.c_void_p()
282        # ct_buffer_length = ct.c_size_t()
283        # # ct_file_format = ct.c_int(file_format)
284
285        # # API Call
286        # status = self.library.GW2GetPolicySettings(
287        #     ct_session,
288        #     ct.byref(ct_buffer),
289        #     ct.byref(ct_buffer_length)
290        # )
291
292        # print("GW2GetPolicySettings status:", status)
293
294        # file_bytes = utils.buffer_to_bytes(
295        #     ct_buffer,
296        #     ct_buffer_length,
297        # )
298
299        # return file_bytes

Returns the content management configuration for a given session.

Args: session (int): The session integer.

Returns: xml_string (str): The XML string of the current content management configuration.

def set_content_management_policy( self, session: int, input_file: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, policy_format=0):
369    def set_content_management_policy(self, session: int, input_file: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None, policy_format=0):
370        """ Sets the content management policy configuration. If input_file is None then default settings (sanitise) are applied.
371
372        Args:
373            session (int): The session integer.
374            input_file (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
375            policy_format (int): The format of the content management policy. 0=XML.
376
377        Returns:
378            - result (glasswall.GwReturnObj): Depending on the input 'input_file':
379                - If input_file is a str file path:
380                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'policy_format', 'status'.
381
382                - If input_file is a file in memory:
383                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'policy_format', 'status'.
384        """
385        # Validate type
386        if not isinstance(session, int):
387            raise TypeError(session)
388        if not isinstance(input_file, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
389            raise TypeError(input_file)
390        if not isinstance(policy_format, int):
391            raise TypeError(policy_format)
392
393        # Set input_file to default if input_file is None
394        if input_file is None:
395            input_file = glasswall.content_management.policies.Editor(default="sanitise")
396
397        # Validate xml content is parsable
398        utils.validate_xml(input_file)
399
400        # From file
401        if isinstance(input_file, str) and os.path.isfile(input_file):
402            input_file = os.path.abspath(input_file)
403
404            result = self._GW2RegisterPoliciesFile(session, input_file)
405
406        # From memory
407        elif isinstance(input_file, (str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
408            # Convert bytearray, io.BytesIO to bytes
409            if isinstance(input_file, (bytearray, io.BytesIO)):
410                input_file = utils.as_bytes(input_file)
411            # Convert string xml or Policy to bytes
412            if isinstance(input_file, (str, glasswall.content_management.policies.policy.Policy)):
413                input_file = input_file.encode("utf-8")
414
415            result = self._GW2RegisterPoliciesMemory(session, input_file)
416
417        if result.status not in successes.success_codes:
418            log.error(format_object(result))
419            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
420        else:
421            log.debug(format_object(result))
422
423        return result

Sets the content management policy configuration. If input_file is None then default settings (sanitise) are applied.

Args: session (int): The session integer. input_file (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply. policy_format (int): The format of the content management policy. 0=XML.

Returns: - result (glasswall.GwReturnObj): Depending on the input 'input_file': - If input_file is a str file path: - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'policy_format', 'status'.

    - If input_file is a file in memory:
        - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'policy_format', 'status'.
def register_input( self, session: int, input_file: Union[str, bytes, bytearray, _io.BytesIO]):
487    def register_input(self, session: int, input_file: Union[str, bytes, bytearray, io.BytesIO]):
488        """ Register an input file or bytes for the given session.
489
490        Args:
491            session (int): The session integer.
492            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
493
494        Returns:
495            - result (glasswall.GwReturnObj): Depending on the input 'input_file':
496                - If input_file is a str file path:
497                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'status'.
498
499                - If input_file is a file in memory:
500                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
501        """
502        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO,)):
503            raise TypeError(input_file)
504
505        if isinstance(input_file, str):
506            if not os.path.isfile(input_file):
507                raise FileNotFoundError(input_file)
508
509            input_file = os.path.abspath(input_file)
510
511            result = self._GW2RegisterInputFile(session, input_file)
512
513        elif isinstance(input_file, (bytes, bytearray, io.BytesIO,)):
514            # Convert bytearray and io.BytesIO to bytes
515            input_file = utils.as_bytes(input_file)
516
517            result = self._GW2RegisterInputMemory(session, input_file)
518
519        if result.status not in successes.success_codes:
520            log.error(format_object(result))
521            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
522        else:
523            log.debug(format_object(result))
524
525        return result

Register an input file or bytes for the given session.

Args: session (int): The session integer. input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.

Returns: - result (glasswall.GwReturnObj): Depending on the input 'input_file': - If input_file is a str file path: - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'status'.

    - If input_file is a file in memory:
        - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
def register_output(self, session, output_file: Optional[str] = None):
587    def register_output(self, session, output_file: Optional[str] = None):
588        """ Register an output file for the given session. If output_file is None the file will be returned as 'buffer' and 'buffer_length' attributes.
589
590        Args:
591            session (int): The session integer.
592            output_file (Optional[str]): If specified, during run session the file will be written to output_file, otherwise the file will be written to the glasswall.GwReturnObj 'buffer' and 'buffer_length' attributes.
593
594        Returns:
595            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attribute 'status' indicating the result of the function call. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size.
596        """
597        if not isinstance(output_file, (type(None), str)):
598            raise TypeError(output_file)
599
600        if isinstance(output_file, str):
601            output_file = os.path.abspath(output_file)
602
603            result = self._GW2RegisterOutFile(session, output_file)
604
605        elif isinstance(output_file, type(None)):
606            result = self._GW2RegisterOutputMemory(session)
607
608        if result.status not in successes.success_codes:
609            log.error(format_object(result))
610            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
611        else:
612            log.debug(format_object(result))
613
614        return result

Register an output file for the given session. If output_file is None the file will be returned as 'buffer' and 'buffer_length' attributes.

Args: session (int): The session integer. output_file (Optional[str]): If specified, during run session the file will be written to output_file, otherwise the file will be written to the glasswall.GwReturnObj 'buffer' and 'buffer_length' attributes.

Returns: gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attribute 'status' indicating the result of the function call. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size.

def register_analysis(self, session: int, output_file: Optional[str] = None):
684    def register_analysis(self, session: int, output_file: Optional[str] = None):
685        """ Registers an analysis file for the given session. The analysis file will be created during the session's run_session call.
686
687        Args:
688            session (int): The session integer.
689            output_file (Optional[str]): Default None. The file path where the analysis will be written. None returns the analysis as bytes.
690
691        Returns:
692            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'status', 'session', 'analysis_format'. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size. If output_file is not None (file mode) 'output_file' is included.
693        """
694        if not isinstance(output_file, (type(None), str)):
695            raise TypeError(output_file)
696
697        if isinstance(output_file, str):
698            output_file = os.path.abspath(output_file)
699
700            result = self._GW2RegisterAnalysisFile(session, output_file)
701
702        elif isinstance(output_file, type(None)):
703            result = self._GW2RegisterAnalysisMemory(session)
704
705        if result.status not in successes.success_codes:
706            log.error(format_object(result))
707            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
708        else:
709            log.debug(format_object(result))
710
711        return result

Registers an analysis file for the given session. The analysis file will be created during the session's run_session call.

Args: session (int): The session integer. output_file (Optional[str]): Default None. The file path where the analysis will be written. None returns the analysis as bytes.

Returns: gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'status', 'session', 'analysis_format'. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size. If output_file is not None (file mode) 'output_file' is included.

def protect_file( self, input_file: Union[str, bytes, bytearray, _io.BytesIO], output_file: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
713    def protect_file(
714        self,
715        input_file: Union[str, bytes, bytearray, io.BytesIO],
716        output_file: Optional[str] = None,
717        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
718        raise_unsupported: bool = True,
719    ):
720        """ Protects a file using the current content management configuration, returning the file bytes. The protected file is written to output_file if it is provided.
721
722        Args:
723            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
724            output_file (Optional[str]): The output file path where the protected file will be written.
725            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
726            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
727
728        Returns:
729            file_bytes (bytes): The protected file bytes.
730        """
731        # Validate arg types
732        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
733            raise TypeError(input_file)
734        if not isinstance(output_file, (type(None), str)):
735            raise TypeError(output_file)
736        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
737            raise TypeError(content_management_policy)
738        if not isinstance(raise_unsupported, bool):
739            raise TypeError(raise_unsupported)
740
741        # Convert string path arguments to absolute paths
742        if isinstance(input_file, str):
743            if not os.path.isfile(input_file):
744                raise FileNotFoundError(input_file)
745            input_file = os.path.abspath(input_file)
746        if isinstance(output_file, str):
747            output_file = os.path.abspath(output_file)
748            # make directories that do not exist
749            os.makedirs(os.path.dirname(output_file), exist_ok=True)
750        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
751            content_management_policy = os.path.abspath(content_management_policy)
752
753        # Convert memory inputs to bytes
754        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
755            input_file = utils.as_bytes(input_file)
756
757        with utils.CwdHandler(self.library_path):
758            with self.new_session() as session:
759                content_management_policy = self.set_content_management_policy(session, content_management_policy)
760                register_input = self.register_input(session, input_file)
761                register_output = self.register_output(session, output_file=output_file)
762                status = self.run_session(session)
763
764                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
765                if status not in successes.success_codes:
766                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
767                    if raise_unsupported:
768                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
769                    else:
770                        file_bytes = None
771                else:
772                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
773                    # Get file bytes
774                    if isinstance(output_file, str):
775                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
776                        if not os.path.isfile(output_file):
777                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
778                            file_bytes = None
779                        else:
780                            with open(output_file, "rb") as f:
781                                file_bytes = f.read()
782                    else:
783                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
784                        file_bytes = utils.buffer_to_bytes(
785                            register_output.buffer,
786                            register_output.buffer_length
787                        )
788
789                # Ensure memory allocated is not garbage collected
790                content_management_policy, register_input, register_output
791
792                return file_bytes

Protects a file using the current content management configuration, returning the file bytes. The protected file is written to output_file if it is provided.

Args: input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes. output_file (Optional[str]): The output file path where the protected file will be written. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: file_bytes (bytes): The protected file bytes.

def protect_directory( self, input_directory: str, output_directory: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
794    def protect_directory(
795        self,
796        input_directory: str,
797        output_directory: Optional[str] = None,
798        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
799        raise_unsupported: bool = True,
800    ):
801        """ Recursively processes all files in a directory in protect mode using the given content management policy.
802        The protected files are written to output_directory maintaining the same directory structure as input_directory.
803
804        Args:
805            input_directory (str): The input directory containing files to protect.
806            output_directory (Optional[str]): The output directory where the protected file will be written, or None to not write files.
807            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
808            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
809
810        Returns:
811            protected_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.
812        """
813        protected_files_dict = {}
814        # Call protect_file on each file in input_directory to output_directory
815        for input_file in utils.list_file_paths(input_directory):
816            relative_path = os.path.relpath(input_file, input_directory)
817            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
818
819            protected_bytes = self.protect_file(
820                input_file=input_file,
821                output_file=output_file,
822                raise_unsupported=raise_unsupported,
823                content_management_policy=content_management_policy,
824            )
825
826            protected_files_dict[relative_path] = protected_bytes
827
828        return protected_files_dict

Recursively processes all files in a directory in protect mode using the given content management policy. The protected files are written to output_directory maintaining the same directory structure as input_directory.

Args: input_directory (str): The input directory containing files to protect. output_directory (Optional[str]): The output directory where the protected file will be written, or None to not write files. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: protected_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.

def analyse_file( self, input_file: Union[str, bytes, bytearray, _io.BytesIO], output_file: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
830    def analyse_file(
831        self,
832        input_file: Union[str, bytes, bytearray, io.BytesIO],
833        output_file: Optional[str] = None,
834        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
835        raise_unsupported: bool = True,
836    ):
837        """ Analyses a file, returning the analysis bytes. The analysis is written to output_file if it is provided.
838
839        Args:
840            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
841            output_file (Optional[str]): The output file path where the analysis file will be written.
842            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
843            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
844
845        Returns:
846            file_bytes (bytes): The analysis file bytes.
847        """
848        # Validate arg types
849        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
850            raise TypeError(input_file)
851        if not isinstance(output_file, (type(None), str)):
852            raise TypeError(output_file)
853        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
854            raise TypeError(content_management_policy)
855        if not isinstance(raise_unsupported, bool):
856            raise TypeError(raise_unsupported)
857
858        # Convert string path arguments to absolute paths
859        if isinstance(input_file, str):
860            if not os.path.isfile(input_file):
861                raise FileNotFoundError(input_file)
862            input_file = os.path.abspath(input_file)
863        if isinstance(output_file, str):
864            output_file = os.path.abspath(output_file)
865            # make directories that do not exist
866            os.makedirs(os.path.dirname(output_file), exist_ok=True)
867        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
868            content_management_policy = os.path.abspath(content_management_policy)
869
870        # Convert memory inputs to bytes
871        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
872            input_file = utils.as_bytes(input_file)
873
874        with utils.CwdHandler(self.library_path):
875            with self.new_session() as session:
876                content_management_policy = self.set_content_management_policy(session, content_management_policy)
877                register_input = self.register_input(session, input_file)
878                register_analysis = self.register_analysis(session, output_file)
879                status = self.run_session(session)
880
881                file_bytes = None
882                if isinstance(output_file, str):
883                    # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
884                    if os.path.isfile(output_file):
885                        with open(output_file, "rb") as f:
886                            file_bytes = f.read()
887                else:
888                    # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
889                    if register_analysis.buffer and register_analysis.buffer_length:
890                        file_bytes = utils.buffer_to_bytes(
891                            register_analysis.buffer,
892                            register_analysis.buffer_length
893                        )
894
895                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
896                if status not in successes.success_codes:
897                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
898                    if raise_unsupported:
899                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
900                else:
901                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
902
903                # Ensure memory allocated is not garbage collected
904                content_management_policy, register_input, register_analysis
905
906                return file_bytes

Analyses a file, returning the analysis bytes. The analysis is written to output_file if it is provided.

Args: input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes. output_file (Optional[str]): The output file path where the analysis file will be written. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: file_bytes (bytes): The analysis file bytes.

def analyse_directory( self, input_directory: str, output_directory: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
908    def analyse_directory(
909        self,
910        input_directory: str,
911        output_directory: Optional[str] = None,
912        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
913        raise_unsupported: bool = True,
914    ):
915        """ Analyses all files in a directory and its subdirectories. The analysis files are written to output_directory maintaining the same directory structure as input_directory.
916
917        Args:
918            input_directory (str): The input directory containing files to analyse.
919            output_directory (Optional[str]): The output directory where the analysis files will be written, or None to not write files.
920            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
921            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
922
923        Returns:
924            analysis_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.
925        """
926        analysis_files_dict = {}
927        # Call analyse_file on each file in input_directory to output_directory
928        for input_file in utils.list_file_paths(input_directory):
929            relative_path = os.path.relpath(input_file, input_directory) + ".xml"
930            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
931
932            analysis_bytes = self.analyse_file(
933                input_file=input_file,
934                output_file=output_file,
935                raise_unsupported=raise_unsupported,
936                content_management_policy=content_management_policy,
937            )
938
939            analysis_files_dict[relative_path] = analysis_bytes
940
941        return analysis_files_dict

Analyses all files in a directory and its subdirectories. The analysis files are written to output_directory maintaining the same directory structure as input_directory.

Args: input_directory (str): The input directory containing files to analyse. output_directory (Optional[str]): The output directory where the analysis files will be written, or None to not write files. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: analysis_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.

def protect_and_analyse_file( self, input_file: Union[str, bytes, bytearray, _io.BytesIO], output_file: Optional[str] = None, output_analysis_report: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
 943    def protect_and_analyse_file(
 944        self,
 945        input_file: Union[str, bytes, bytearray, io.BytesIO],
 946        output_file: Optional[str] = None,
 947        output_analysis_report: Optional[str] = None,
 948        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
 949        raise_unsupported: bool = True,
 950    ):
 951        """ Protects and analyses a file in a single session, returning both protected file bytes and analysis report bytes.
 952
 953        Args:
 954            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
 955            output_file (Optional[str]): The output file path where the protected file will be written.
 956            output_analysis_report (Optional[str]): The output file path where the XML analysis report will be written.
 957            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
 958            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
 959
 960        Returns:
 961            Tuple[Optional[bytes], Optional[bytes]]: A tuple of (protected_file_bytes, analysis_report_bytes).
 962        """
 963        # Validate arg types
 964        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
 965            raise TypeError(f"input_file expected to be of type: str, bytes, bytearray, io.BytesIO. Got: {type(input_file).__name__}")
 966        if not isinstance(output_file, (type(None), str)):
 967            raise TypeError(f"output_file expected to be of type: None, str. Got: {type(output_file).__name__}")
 968        if not isinstance(output_analysis_report, (type(None), str)):
 969            raise TypeError(f"output_analysis_report expected to be of type: None, str. Got: {type(output_analysis_report).__name__}")
 970        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
 971            raise TypeError(f"content_management_policy expected to be of type: None, str, bytes, bytearray, io.BytesIO, Policy. Got: {type(content_management_policy).__name__}")
 972        if not isinstance(raise_unsupported, bool):
 973            raise TypeError(f"raise_unsupported expected to be of type: bool. Got: {type(raise_unsupported).__name__}")
 974
 975        # Convert string path arguments to absolute paths
 976        if isinstance(input_file, str):
 977            if not os.path.isfile(input_file):
 978                raise FileNotFoundError(input_file)
 979            input_file = os.path.abspath(input_file)
 980        if isinstance(output_file, str):
 981            output_file = os.path.abspath(output_file)
 982            os.makedirs(os.path.dirname(output_file), exist_ok=True)
 983        if isinstance(output_analysis_report, str):
 984            output_analysis_report = os.path.abspath(output_analysis_report)
 985            os.makedirs(os.path.dirname(output_analysis_report), exist_ok=True)
 986        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
 987            content_management_policy = os.path.abspath(content_management_policy)
 988
 989        # Convert memory inputs to bytes
 990        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
 991            input_file = utils.as_bytes(input_file)
 992
 993        with utils.CwdHandler(self.library_path):
 994            with self.new_session() as session:
 995                content_management_policy = self.set_content_management_policy(session, content_management_policy)
 996                register_input = self.register_input(session, input_file)
 997                register_output = self.register_output(session, output_file)
 998                register_analysis = self.register_analysis(session, output_analysis_report)
 999
1000                status = self.run_session(session)
1001
1002                protected_file_bytes = None
1003                analysis_report_bytes = None
1004
1005                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1006                if status not in successes.success_codes:
1007                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1008                    if raise_unsupported:
1009                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1010
1011                # Get analysis report file bytes, even on processing failure
1012                if isinstance(output_analysis_report, str):
1013                    # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1014                    if not os.path.isfile(output_analysis_report):
1015                        log.error(f"Editor returned success code: {status} but no output file was found: {output_analysis_report}")
1016                    else:
1017                        with open(output_analysis_report, "rb") as f:
1018                            analysis_report_bytes = f.read()
1019                else:
1020                    # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1021                    if register_analysis.buffer and register_analysis.buffer_length:
1022                        analysis_report_bytes = utils.buffer_to_bytes(
1023                            register_analysis.buffer,
1024                            register_analysis.buffer_length
1025                        )
1026
1027                # On success, get protected file bytes
1028                if status in successes.success_codes:
1029                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1030                    # Get file bytes
1031                    if isinstance(output_file, str):
1032                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1033                        if not os.path.isfile(output_file):
1034                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1035                        else:
1036                            with open(output_file, "rb") as f:
1037                                protected_file_bytes = f.read()
1038                    else:
1039                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1040                        protected_file_bytes = utils.buffer_to_bytes(
1041                            register_output.buffer,
1042                            register_output.buffer_length
1043                        )
1044
1045                # Ensure memory allocated is not garbage collected
1046                content_management_policy, register_input, register_output, register_analysis
1047
1048                return protected_file_bytes, analysis_report_bytes

Protects and analyses a file in a single session, returning both protected file bytes and analysis report bytes.

Args: input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes. output_file (Optional[str]): The output file path where the protected file will be written. output_analysis_report (Optional[str]): The output file path where the XML analysis report will be written. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: Tuple[Optional[bytes], Optional[bytes]]: A tuple of (protected_file_bytes, analysis_report_bytes).

def protect_and_analyse_directory( self, input_directory: str, output_directory: Optional[str] = None, analysis_directory: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
1050    def protect_and_analyse_directory(
1051        self,
1052        input_directory: str,
1053        output_directory: Optional[str] = None,
1054        analysis_directory: Optional[str] = None,
1055        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1056        raise_unsupported: bool = True,
1057    ):
1058        """ Recursively processes all files in a directory using protect and analyse mode with the given content management policy.
1059        Outputs are written to output_directory and analysis_directory maintaining the same structure as input_directory.
1060
1061        Args:
1062            input_directory (str): The input directory containing files to process.
1063            output_directory (Optional[str]): The output directory for protected files.
1064            analysis_directory (Optional[str]): The output directory for XML analysis reports.
1065            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply.
1066            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1067
1068        Returns:
1069            result_dict (dict): A dictionary mapping relative file paths to tuples of (protected_file_bytes, analysis_report_bytes).
1070        """
1071        result_dict = {}
1072        for input_file in utils.list_file_paths(input_directory):
1073            relative_path = os.path.relpath(input_file, input_directory)
1074            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
1075            output_analysis_report = None if analysis_directory is None else os.path.join(os.path.abspath(analysis_directory), relative_path + ".xml")
1076
1077            protected_file_bytes, analysis_report_bytes = self.protect_and_analyse_file(
1078                input_file=input_file,
1079                output_file=output_file,
1080                output_analysis_report=output_analysis_report,
1081                content_management_policy=content_management_policy,
1082                raise_unsupported=raise_unsupported,
1083            )
1084
1085            result_dict[relative_path] = (protected_file_bytes, analysis_report_bytes)
1086
1087        return result_dict

Recursively processes all files in a directory using protect and analyse mode with the given content management policy. Outputs are written to output_directory and analysis_directory maintaining the same structure as input_directory.

Args: input_directory (str): The input directory containing files to process. output_directory (Optional[str]): The output directory for protected files. analysis_directory (Optional[str]): The output directory for XML analysis reports. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: result_dict (dict): A dictionary mapping relative file paths to tuples of (protected_file_bytes, analysis_report_bytes).

def register_export(self, session: int, output_file: Optional[str] = None):
1149    def register_export(self, session: int, output_file: Optional[str] = None):
1150        """ Registers a file to be exported for the given session. The export file will be created during the session's run_session call.
1151
1152        Args:
1153            session (int): The session integer.
1154            output_file (Optional[str]): Default None. The file path where the export will be written. None exports the file in memory.
1155
1156        Returns:
1157            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attribute 'status' indicating the result of the function call and 'session', the session integer. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size.
1158        """
1159        if not isinstance(output_file, (type(None), str)):
1160            raise TypeError(output_file)
1161
1162        if isinstance(output_file, str):
1163            output_file = os.path.abspath(output_file)
1164
1165            result = self._GW2RegisterExportFile(session, output_file)
1166
1167        elif isinstance(output_file, type(None)):
1168            result = self._GW2RegisterExportMemory(session)
1169
1170        if result.status not in successes.success_codes:
1171            log.error(format_object(result))
1172            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
1173        else:
1174            log.debug(format_object(result))
1175
1176        return result

Registers a file to be exported for the given session. The export file will be created during the session's run_session call.

Args: session (int): The session integer. output_file (Optional[str]): Default None. The file path where the export will be written. None exports the file in memory.

Returns: gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attribute 'status' indicating the result of the function call and 'session', the session integer. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size.

def export_file( self, input_file: Union[str, bytes, bytearray, _io.BytesIO], output_file: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
1178    def export_file(
1179        self,
1180        input_file: Union[str, bytes, bytearray, io.BytesIO],
1181        output_file: Optional[str] = None,
1182        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1183        raise_unsupported: bool = True,
1184    ):
1185        """ Export a file, returning the .zip file bytes. The .zip file is written to output_file if it is provided.
1186
1187        Args:
1188            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
1189            output_file (Optional[str]): The output file path where the .zip file will be written.
1190            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
1191            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1192
1193        Returns:
1194            file_bytes (bytes): The exported .zip file.
1195        """
1196        # Validate arg types
1197        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
1198            raise TypeError(input_file)
1199        if not isinstance(output_file, (type(None), str)):
1200            raise TypeError(output_file)
1201        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
1202            raise TypeError(content_management_policy)
1203        if not isinstance(raise_unsupported, bool):
1204            raise TypeError(raise_unsupported)
1205
1206        # Convert string path arguments to absolute paths
1207        if isinstance(input_file, str):
1208            if not os.path.isfile(input_file):
1209                raise FileNotFoundError(input_file)
1210            input_file = os.path.abspath(input_file)
1211        if isinstance(output_file, str):
1212            output_file = os.path.abspath(output_file)
1213            # make directories that do not exist
1214            os.makedirs(os.path.dirname(output_file), exist_ok=True)
1215        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
1216            content_management_policy = os.path.abspath(content_management_policy)
1217
1218        # Convert memory inputs to bytes
1219        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
1220            input_file = utils.as_bytes(input_file)
1221
1222        with utils.CwdHandler(self.library_path):
1223            with self.new_session() as session:
1224                content_management_policy = self.set_content_management_policy(session, content_management_policy)
1225                register_input = self.register_input(session, input_file)
1226                register_export = self.register_export(session, output_file)
1227                status = self.run_session(session)
1228
1229                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1230                if status not in successes.success_codes:
1231                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
1232                    if raise_unsupported:
1233                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1234                    else:
1235                        file_bytes = None
1236                else:
1237                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
1238                    # Get file bytes
1239                    if isinstance(output_file, str):
1240                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1241                        if not os.path.isfile(output_file):
1242                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1243                            file_bytes = None
1244                        else:
1245                            with open(output_file, "rb") as f:
1246                                file_bytes = f.read()
1247                    else:
1248                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1249                        file_bytes = utils.buffer_to_bytes(
1250                            register_export.buffer,
1251                            register_export.buffer_length
1252                        )
1253
1254                # Ensure memory allocated is not garbage collected
1255                content_management_policy, register_input, register_export
1256
1257                return file_bytes

Export a file, returning the .zip file bytes. The .zip file is written to output_file if it is provided.

Args: input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes. output_file (Optional[str]): The output file path where the .zip file will be written. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: file_bytes (bytes): The exported .zip file.

def export_directory( self, input_directory: str, output_directory: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
1259    def export_directory(
1260        self,
1261        input_directory: str,
1262        output_directory: Optional[str] = None,
1263        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1264        raise_unsupported: bool = True
1265    ):
1266        """ Exports all files in a directory and its subdirectories. The export files are written to output_directory maintaining the same directory structure as input_directory.
1267
1268        Args:
1269            input_directory (str): The input directory containing files to export.
1270            output_directory (Optional[str]): The output directory where the export files will be written, or None to not write files.
1271            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
1272            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1273
1274        Returns:
1275            export_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.
1276        """
1277        export_files_dict = {}
1278        # Call export_file on each file in input_directory to output_directory
1279        for input_file in utils.list_file_paths(input_directory):
1280            relative_path = os.path.relpath(input_file, input_directory) + ".zip"
1281            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
1282
1283            export_bytes = self.export_file(
1284                input_file=input_file,
1285                output_file=output_file,
1286                raise_unsupported=raise_unsupported,
1287                content_management_policy=content_management_policy,
1288            )
1289
1290            export_files_dict[relative_path] = export_bytes
1291
1292        return export_files_dict

Exports all files in a directory and its subdirectories. The export files are written to output_directory maintaining the same directory structure as input_directory.

Args: input_directory (str): The input directory containing files to export. output_directory (Optional[str]): The output directory where the export files will be written, or None to not write files. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: export_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.

def export_and_analyse_file( self, input_file: Union[str, bytes, bytearray, _io.BytesIO], output_file: Optional[str] = None, output_analysis_report: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
1294    def export_and_analyse_file(
1295        self,
1296        input_file: Union[str, bytes, bytearray, io.BytesIO],
1297        output_file: Optional[str] = None,
1298        output_analysis_report: Optional[str] = None,
1299        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1300        raise_unsupported: bool = True,
1301    ):
1302        """ Exports and analyses a file in a single session, returning both exported .zip bytes and analysis report bytes.
1303
1304        Args:
1305            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
1306            output_file (Optional[str]): The output file path where the .zip export will be written.
1307            output_analysis_report (Optional[str]): The output file path where the XML analysis report will be written.
1308            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
1309            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1310
1311        Returns:
1312            Tuple[Optional[bytes], Optional[bytes]]: A tuple of (export_file_bytes, analysis_report_bytes).
1313        """
1314        # Validate arg types
1315        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
1316            raise TypeError(f"input_file expected to be of type: str, bytes, bytearray, io.BytesIO. Got: {type(input_file).__name__}")
1317        if not isinstance(output_file, (type(None), str)):
1318            raise TypeError(f"output_file expected to be of type: None, str. Got: {type(output_file).__name__}")
1319        if not isinstance(output_analysis_report, (type(None), str)):
1320            raise TypeError(f"output_analysis_report expected to be of type: None, str. Got: {type(output_analysis_report).__name__}")
1321        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
1322            raise TypeError(f"content_management_policy expected to be of type: None, str, bytes, bytearray, io.BytesIO, Policy. Got: {type(content_management_policy).__name__}")
1323        if not isinstance(raise_unsupported, bool):
1324            raise TypeError(f"raise_unsupported expected to be of type: bool. Got: {type(raise_unsupported).__name__}")
1325
1326        # Convert string path arguments to absolute paths
1327        if isinstance(input_file, str):
1328            if not os.path.isfile(input_file):
1329                raise FileNotFoundError(input_file)
1330            input_file = os.path.abspath(input_file)
1331        if isinstance(output_file, str):
1332            output_file = os.path.abspath(output_file)
1333            os.makedirs(os.path.dirname(output_file), exist_ok=True)
1334        if isinstance(output_analysis_report, str):
1335            output_analysis_report = os.path.abspath(output_analysis_report)
1336            os.makedirs(os.path.dirname(output_analysis_report), exist_ok=True)
1337        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
1338            content_management_policy = os.path.abspath(content_management_policy)
1339
1340        # Convert memory inputs to bytes
1341        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
1342            input_file = utils.as_bytes(input_file)
1343
1344        with utils.CwdHandler(self.library_path):
1345            with self.new_session() as session:
1346                content_management_policy = self.set_content_management_policy(session, content_management_policy)
1347                register_input = self.register_input(session, input_file)
1348                register_export = self.register_export(session, output_file)
1349                register_analysis = self.register_analysis(session, output_analysis_report)
1350
1351                status = self.run_session(session)
1352
1353                export_file_bytes = None
1354                analysis_report_bytes = None
1355
1356                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1357                if status not in successes.success_codes:
1358                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1359                    if raise_unsupported:
1360                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1361
1362                # Get analysis report file bytes, even on processing failure
1363                if isinstance(output_analysis_report, str):
1364                    # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1365                    if not os.path.isfile(output_analysis_report):
1366                        log.error(f"Editor returned success code: {status} but no output file was found: {output_analysis_report}")
1367                    else:
1368                        with open(output_analysis_report, "rb") as f:
1369                            analysis_report_bytes = f.read()
1370                else:
1371                    # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1372                    if register_analysis.buffer and register_analysis.buffer_length:
1373                        analysis_report_bytes = utils.buffer_to_bytes(
1374                            register_analysis.buffer,
1375                            register_analysis.buffer_length
1376                        )
1377
1378                # On success, get export file bytes
1379                if status in successes.success_codes:
1380                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1381                    # Get export file bytes
1382                    if isinstance(output_file, str):
1383                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1384                        if not os.path.isfile(output_file):
1385                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1386                        else:
1387                            with open(output_file, "rb") as f:
1388                                export_file_bytes = f.read()
1389                    else:
1390                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1391                        export_file_bytes = utils.buffer_to_bytes(
1392                            register_export.buffer,
1393                            register_export.buffer_length
1394                        )
1395
1396                # Ensure memory allocated is not garbage collected
1397                content_management_policy, register_input, register_export, register_analysis
1398
1399                return export_file_bytes, analysis_report_bytes

Exports and analyses a file in a single session, returning both exported .zip bytes and analysis report bytes.

Args: input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes. output_file (Optional[str]): The output file path where the .zip export will be written. output_analysis_report (Optional[str]): The output file path where the XML analysis report will be written. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: Tuple[Optional[bytes], Optional[bytes]]: A tuple of (export_file_bytes, analysis_report_bytes).

def export_and_analyse_directory( self, input_directory: str, output_directory: Optional[str] = None, analysis_directory: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
1401    def export_and_analyse_directory(
1402        self,
1403        input_directory: str,
1404        output_directory: Optional[str] = None,
1405        analysis_directory: Optional[str] = None,
1406        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1407        raise_unsupported: bool = True,
1408    ):
1409        """ Recursively processes all files in a directory using export and analyse mode with the given content management policy.
1410        Outputs are written to output_directory and analysis_directory maintaining the same structure as input_directory.
1411
1412        Args:
1413            input_directory (str): The input directory containing files to process.
1414            output_directory (Optional[str]): The output directory for exported .zip files.
1415            analysis_directory (Optional[str]): The output directory for XML analysis reports.
1416            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply.
1417            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1418
1419        Returns:
1420            result_dict (dict): A dictionary mapping relative file paths to tuples of (export_file_bytes, analysis_report_bytes).
1421        """
1422        result_dict = {}
1423
1424        for input_file in utils.list_file_paths(input_directory):
1425            relative_path = os.path.relpath(input_file, input_directory)
1426            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path + ".zip")
1427            output_analysis_report = None if analysis_directory is None else os.path.join(os.path.abspath(analysis_directory), relative_path + ".xml")
1428
1429            export_file_bytes, analysis_report_bytes = self.export_and_analyse_file(
1430                input_file=input_file,
1431                output_file=output_file,
1432                output_analysis_report=output_analysis_report,
1433                content_management_policy=content_management_policy,
1434                raise_unsupported=raise_unsupported,
1435            )
1436
1437            result_dict[relative_path] = (export_file_bytes, analysis_report_bytes)
1438
1439        return result_dict

Recursively processes all files in a directory using export and analyse mode with the given content management policy. Outputs are written to output_directory and analysis_directory maintaining the same structure as input_directory.

Args: input_directory (str): The input directory containing files to process. output_directory (Optional[str]): The output directory for exported .zip files. analysis_directory (Optional[str]): The output directory for XML analysis reports. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: result_dict (dict): A dictionary mapping relative file paths to tuples of (export_file_bytes, analysis_report_bytes).

def register_import( self, session: int, input_file: Union[str, bytes, bytearray, _io.BytesIO]):
1502    def register_import(self, session: int, input_file: Union[str, bytes, bytearray, io.BytesIO]):
1503        """ Registers a .zip file to be imported for the given session. The constructed file will be created during the session's run_session call.
1504
1505        Args:
1506            session (int): The session integer.
1507            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input import file path or bytes.
1508
1509        Returns:
1510            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attribute 'status' indicating the result of the function call. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size.
1511        """
1512        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO,)):
1513            raise TypeError(input_file)
1514
1515        if isinstance(input_file, str):
1516            if not os.path.isfile(input_file):
1517                raise FileNotFoundError(input_file)
1518
1519            input_file = os.path.abspath(input_file)
1520
1521            result = self._GW2RegisterImportFile(session, input_file)
1522
1523        elif isinstance(input_file, (bytes, bytearray, io.BytesIO,)):
1524            # Convert bytearray and io.BytesIO to bytes
1525            input_file = utils.as_bytes(input_file)
1526
1527            result = self._GW2RegisterImportMemory(session, input_file)
1528
1529        if result.status not in successes.success_codes:
1530            log.error(format_object(result))
1531            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
1532        else:
1533            log.debug(format_object(result))
1534
1535        return result

Registers a .zip file to be imported for the given session. The constructed file will be created during the session's run_session call.

Args: session (int): The session integer. input_file (Union[str, bytes, bytearray, io.BytesIO]): The input import file path or bytes.

Returns: gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attribute 'status' indicating the result of the function call. If output_file is None (memory mode), 'buffer', and 'buffer_length' are included containing the file content and file size.

def import_file( self, input_file: Union[str, bytes, bytearray, _io.BytesIO], output_file: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
1537    def import_file(
1538        self,
1539        input_file: Union[str, bytes, bytearray, io.BytesIO],
1540        output_file: Optional[str] = None,
1541        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1542        raise_unsupported: bool = True,
1543    ):
1544        """ Import a .zip file, constructs a file from the .zip file and returns the file bytes. The file is written to output_file if it is provided.
1545
1546        Args:
1547            input_file (Union[str, bytes, bytearray, io.BytesIO]): The .zip input file path or bytes.
1548            output_file (Optional[str]): The output file path where the constructed file will be written.
1549            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
1550            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1551
1552        Returns:
1553            file_bytes (bytes): The imported file bytes.
1554        """
1555        # Validate arg types
1556        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
1557            raise TypeError(input_file)
1558        if not isinstance(output_file, (type(None), str)):
1559            raise TypeError(output_file)
1560        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
1561            raise TypeError(content_management_policy)
1562        if not isinstance(raise_unsupported, bool):
1563            raise TypeError(raise_unsupported)
1564
1565        # Convert string path arguments to absolute paths
1566        if isinstance(input_file, str):
1567            if not os.path.isfile(input_file):
1568                raise FileNotFoundError(input_file)
1569            input_file = os.path.abspath(input_file)
1570        if isinstance(output_file, str):
1571            output_file = os.path.abspath(output_file)
1572            # make directories that do not exist
1573            os.makedirs(os.path.dirname(output_file), exist_ok=True)
1574        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
1575            content_management_policy = os.path.abspath(content_management_policy)
1576
1577        # Convert memory inputs to bytes
1578        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
1579            input_file = utils.as_bytes(input_file)
1580
1581        with utils.CwdHandler(self.library_path):
1582            with self.new_session() as session:
1583                content_management_policy = self.set_content_management_policy(session, content_management_policy)
1584                register_import = self.register_import(session, input_file)
1585                register_output = self.register_output(session, output_file)
1586                status = self.run_session(session)
1587
1588                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray,)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1589                if status not in successes.success_codes:
1590                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
1591                    if raise_unsupported:
1592                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1593                    else:
1594                        file_bytes = None
1595                else:
1596                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\tsession: {session}\n\tstatus: {status}")
1597                    # Get file bytes
1598                    if isinstance(output_file, str):
1599                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1600                        if not os.path.isfile(output_file):
1601                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1602                            file_bytes = None
1603                        else:
1604                            with open(output_file, "rb") as f:
1605                                file_bytes = f.read()
1606                    else:
1607                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1608                        file_bytes = utils.buffer_to_bytes(
1609                            register_output.buffer,
1610                            register_output.buffer_length
1611                        )
1612
1613                # Ensure memory allocated is not garbage collected
1614                content_management_policy, register_import, register_output
1615
1616                return file_bytes

Import a .zip file, constructs a file from the .zip file and returns the file bytes. The file is written to output_file if it is provided.

Args: input_file (Union[str, bytes, bytearray, io.BytesIO]): The .zip input file path or bytes. output_file (Optional[str]): The output file path where the constructed file will be written. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: file_bytes (bytes): The imported file bytes.

def import_directory( self, input_directory: str, output_directory: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
1618    def import_directory(
1619        self,
1620        input_directory: str,
1621        output_directory: Optional[str] = None,
1622        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1623        raise_unsupported: bool = True,
1624    ):
1625        """ Imports all files in a directory and its subdirectories. Files are expected as .zip but this is not forced.
1626        The constructed files are written to output_directory maintaining the same directory structure as input_directory.
1627
1628        Args:
1629            input_directory (str): The input directory containing files to import.
1630            output_directory (Optional[str]): The output directory where the constructed files will be written, or None to not write files.
1631            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply.
1632            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1633
1634        Returns:
1635            import_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.
1636        """
1637        import_files_dict = {}
1638        # Call import_file on each file in input_directory to output_directory
1639        for input_file in utils.list_file_paths(input_directory):
1640            relative_path = os.path.relpath(input_file, input_directory)
1641            # Remove .zip extension from relative_path
1642            if relative_path.endswith(".zip"):
1643                relative_path = os.path.splitext(relative_path)[0]
1644            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
1645
1646            import_bytes = self.import_file(
1647                input_file=input_file,
1648                output_file=output_file,
1649                raise_unsupported=raise_unsupported,
1650                content_management_policy=content_management_policy,
1651            )
1652
1653            import_files_dict[relative_path] = import_bytes
1654
1655        return import_files_dict

Imports all files in a directory and its subdirectories. Files are expected as .zip but this is not forced. The constructed files are written to output_directory maintaining the same directory structure as input_directory.

Args: input_directory (str): The input directory containing files to import. output_directory (Optional[str]): The output directory where the constructed files will be written, or None to not write files. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): Default None (sanitise). The content management policy to apply. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: import_files_dict (dict): A dictionary of file paths relative to input_directory, and file bytes.

def import_and_analyse_file( self, input_file: Union[str, bytes, bytearray, _io.BytesIO], output_file: Optional[str] = None, output_analysis_report: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
1657    def import_and_analyse_file(
1658        self,
1659        input_file: Union[str, bytes, bytearray, io.BytesIO],
1660        output_file: Optional[str] = None,
1661        output_analysis_report: Optional[str] = None,
1662        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1663        raise_unsupported: bool = True,
1664    ):
1665        """ Imports and analyses a file in a single session, returning both imported file bytes and analysis report bytes.
1666
1667        Args:
1668            input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes.
1669            output_file (Optional[str]): The output file path where the imported file will be written.
1670            output_analysis_report (Optional[str]): The output file path where the XML analysis report will be written.
1671            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session.
1672            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1673
1674        Returns:
1675            Tuple[Optional[bytes], Optional[bytes]]: A tuple of (import_file_bytes, analysis_report_bytes).
1676        """
1677        # Validate arg types
1678        if not isinstance(input_file, (str, bytes, bytearray, io.BytesIO)):
1679            raise TypeError(f"input_file expected to be of type: str, bytes, bytearray, io.BytesIO. Got: {type(input_file).__name__}")
1680        if not isinstance(output_file, (type(None), str)):
1681            raise TypeError(f"output_file expected to be of type: None, str. Got: {type(output_file).__name__}")
1682        if not isinstance(output_analysis_report, (type(None), str)):
1683            raise TypeError(f"output_analysis_report expected to be of type: None, str. Got: {type(output_analysis_report).__name__}")
1684        if not isinstance(content_management_policy, (type(None), str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy)):
1685            raise TypeError(f"content_management_policy expected to be of type: None, str, bytes, bytearray, io.BytesIO, Policy. Got: {type(content_management_policy).__name__}")
1686        if not isinstance(raise_unsupported, bool):
1687            raise TypeError(f"raise_unsupported expected to be of type: bool. Got: {type(raise_unsupported).__name__}")
1688
1689        # Convert string path arguments to absolute paths
1690        if isinstance(input_file, str):
1691            if not os.path.isfile(input_file):
1692                raise FileNotFoundError(input_file)
1693            input_file = os.path.abspath(input_file)
1694        if isinstance(output_file, str):
1695            output_file = os.path.abspath(output_file)
1696            os.makedirs(os.path.dirname(output_file), exist_ok=True)
1697        if isinstance(output_analysis_report, str):
1698            output_analysis_report = os.path.abspath(output_analysis_report)
1699            os.makedirs(os.path.dirname(output_analysis_report), exist_ok=True)
1700        if isinstance(content_management_policy, str) and os.path.isfile(content_management_policy):
1701            content_management_policy = os.path.abspath(content_management_policy)
1702
1703        # Convert memory inputs to bytes
1704        if isinstance(input_file, (bytes, bytearray, io.BytesIO)):
1705            input_file = utils.as_bytes(input_file)
1706
1707        with utils.CwdHandler(self.library_path):
1708            with self.new_session() as session:
1709                content_management_policy = self.set_content_management_policy(session, content_management_policy)
1710                register_import = self.register_import(session, input_file)
1711                register_output = self.register_output(session, output_file)
1712                register_analysis = self.register_analysis(session, output_analysis_report)
1713
1714                status = self.run_session(session)
1715
1716                import_file_bytes = None
1717                analysis_report_bytes = None
1718
1719                input_file_repr = f"{type(input_file)} length {len(input_file)}" if isinstance(input_file, (bytes, bytearray)) else input_file.__sizeof__() if isinstance(input_file, io.BytesIO) else input_file
1720                if status not in successes.success_codes:
1721                    log.error(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1722                    if raise_unsupported:
1723                        raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1724
1725                # Get analysis report file bytes, even on processing failure
1726                if isinstance(output_analysis_report, str):
1727                    # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1728                    if not os.path.isfile(output_analysis_report):
1729                        log.error(f"Editor returned success code: {status} but no output file was found: {output_analysis_report}")
1730                    else:
1731                        with open(output_analysis_report, "rb") as f:
1732                            analysis_report_bytes = f.read()
1733                else:
1734                    # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1735                    if register_analysis.buffer and register_analysis.buffer_length:
1736                        analysis_report_bytes = utils.buffer_to_bytes(
1737                            register_analysis.buffer,
1738                            register_analysis.buffer_length
1739                        )
1740
1741                # On success, get import file bytes
1742                if status in successes.success_codes:
1743                    log.debug(f"\n\tinput_file: {input_file_repr}\n\toutput_file: {output_file}\n\toutput_analysis_report: {output_analysis_report}\n\tsession: {session}\n\tstatus: {status}")
1744                    # Get import file bytes
1745                    if isinstance(output_file, str):
1746                        # File to file and memory to file, Editor wrote to a file, read it to get the file bytes
1747                        if not os.path.isfile(output_file):
1748                            log.error(f"Editor returned success code: {status} but no output file was found: {output_file}")
1749                        else:
1750                            with open(output_file, "rb") as f:
1751                                import_file_bytes = f.read()
1752                    else:
1753                        # File to memory and memory to memory, Editor wrote to a buffer, convert it to bytes
1754                        import_file_bytes = utils.buffer_to_bytes(
1755                            register_import.buffer,
1756                            register_import.buffer_length
1757                        )
1758
1759                # Ensure memory allocated is not garbage collected
1760                content_management_policy, register_import, register_output, register_analysis
1761
1762                return import_file_bytes, analysis_report_bytes

Imports and analyses a file in a single session, returning both imported file bytes and analysis report bytes.

Args: input_file (Union[str, bytes, bytearray, io.BytesIO]): The input file path or bytes. output_file (Optional[str]): The output file path where the imported file will be written. output_analysis_report (Optional[str]): The output file path where the XML analysis report will be written. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply to the session. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: Tuple[Optional[bytes], Optional[bytes]]: A tuple of (import_file_bytes, analysis_report_bytes).

def import_and_analyse_directory( self, input_directory: str, output_directory: Optional[str] = None, analysis_directory: Optional[str] = None, content_management_policy: Union[NoneType, str, bytes, bytearray, _io.BytesIO, glasswall.content_management.policies.policy.Policy] = None, raise_unsupported: bool = True):
1764    def import_and_analyse_directory(
1765        self,
1766        input_directory: str,
1767        output_directory: Optional[str] = None,
1768        analysis_directory: Optional[str] = None,
1769        content_management_policy: Union[None, str, bytes, bytearray, io.BytesIO, "glasswall.content_management.policies.policy.Policy"] = None,
1770        raise_unsupported: bool = True,
1771    ):
1772        """ Recursively processes all files in a directory using import and analyse mode with the given content management policy.
1773        Outputs are written to output_directory and analysis_directory maintaining the same structure as input_directory.
1774
1775        Args:
1776            input_directory (str): The input directory containing export .zip files to process.
1777            output_directory (Optional[str]): The output directory for imported files.
1778            analysis_directory (Optional[str]): The output directory for XML analysis reports.
1779            content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply.
1780            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
1781
1782        Returns:
1783            result_dict (dict): A dictionary mapping relative file paths to tuples of (import_file_bytes, analysis_report_bytes).
1784        """
1785        result_dict = {}
1786
1787        for input_file in utils.list_file_paths(input_directory):
1788            relative_path = os.path.relpath(input_file, input_directory)
1789            # Remove .zip extension from relative_path
1790            if relative_path.endswith(".zip"):
1791                relative_path = os.path.splitext(relative_path)[0]
1792            output_file = None if output_directory is None else os.path.join(os.path.abspath(output_directory), relative_path)
1793            output_analysis_report = None if analysis_directory is None else os.path.join(os.path.abspath(analysis_directory), relative_path + ".xml")
1794
1795            import_file_bytes, analysis_report_bytes = self.import_and_analyse_file(
1796                input_file=input_file,
1797                output_file=output_file,
1798                output_analysis_report=output_analysis_report,
1799                content_management_policy=content_management_policy,
1800                raise_unsupported=raise_unsupported,
1801            )
1802
1803            result_dict[relative_path] = (import_file_bytes, analysis_report_bytes)
1804
1805        return result_dict

Recursively processes all files in a directory using import and analyse mode with the given content management policy. Outputs are written to output_directory and analysis_directory maintaining the same structure as input_directory.

Args: input_directory (str): The input directory containing export .zip files to process. output_directory (Optional[str]): The output directory for imported files. analysis_directory (Optional[str]): The output directory for XML analysis reports. content_management_policy (Union[None, str, bytes, bytearray, io.BytesIO, glasswall.content_management.policies.policy.Policy], optional): The content management policy to apply. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: result_dict (dict): A dictionary mapping relative file paths to tuples of (import_file_bytes, analysis_report_bytes).

@functools.lru_cache()
def file_error_message(self, session: int) -> str:
1846    @functools.lru_cache()
1847    def file_error_message(self, session: int) -> str:
1848        """ Retrieve the Glasswall Session Process error message.
1849
1850        Args:
1851            session (int): The session integer.
1852
1853        Returns:
1854            error_message (str): The Glasswall Session Process error message.
1855        """
1856        # Validate arg types
1857        if not isinstance(session, int):
1858            raise TypeError(session)
1859
1860        result = self._GW2FileErrorMsg(session)
1861
1862        if result.status not in successes.success_codes:
1863            log.error(format_object(result))
1864            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
1865        else:
1866            log.debug(format_object(result))
1867
1868        return result.error_message

Retrieve the Glasswall Session Process error message.

Args: session (int): The session integer.

Returns: error_message (str): The Glasswall Session Process error message.

def GW2GetFileType(self, session: int, file_type_id):
1870    def GW2GetFileType(self, session: int, file_type_id):
1871        """ Retrieve the file type as a string.
1872
1873        Args:
1874            session (int): The session integer.
1875            file_type_id (int): The file type id.
1876
1877        Returns:
1878            file_type (str): The formal file name for the corresponding file id.
1879        """
1880        # Validate arg types
1881        if not isinstance(session, int):
1882            raise TypeError(session)
1883
1884        # API function declaration
1885        self.library.GW2GetFileType.argtypes = [
1886            ct.c_size_t,
1887            ct.c_size_t,
1888            ct.POINTER(ct.c_size_t),
1889            ct.POINTER(ct.c_void_p)
1890        ]
1891
1892        # Variable initialisation
1893        ct_session = ct.c_size_t(session)
1894        ct_file_type = ct.c_size_t(file_type_id)
1895        ct_buffer_length = ct.c_size_t()
1896        ct_buffer = ct.c_void_p()
1897
1898        # API call
1899        status = self.library.GW2GetFileType(
1900            ct_session,
1901            ct_file_type,
1902            ct.byref(ct_buffer_length),
1903            ct.byref(ct_buffer)
1904        )
1905
1906        if status not in successes.success_codes:
1907            log.error(f"\n\tsession: {session}\n\tstatus: {status}")
1908            raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1909        else:
1910            log.debug(f"\n\tsession: {session}\n\tstatus: {status}")
1911
1912        # Editor wrote to a buffer, convert it to bytes
1913        file_type_bytes = utils.buffer_to_bytes(
1914            ct_buffer,
1915            ct_buffer_length
1916        )
1917
1918        file_type = file_type_bytes.decode()
1919
1920        return file_type

Retrieve the file type as a string.

Args: session (int): The session integer. file_type_id (int): The file type id.

Returns: file_type (str): The formal file name for the corresponding file id.

def GW2GetFileTypeID(self, session: int, file_type_str):
1922    def GW2GetFileTypeID(self, session: int, file_type_str):
1923        """ Retrieve the Glasswall file type id given a file type string.
1924
1925        Args:
1926            session (int): The session integer.
1927            file_type_str (str): The file type as a string.
1928
1929        Returns:
1930            file_type_id (str): The Glasswall file type id for the specified file type.
1931        """
1932        # Validate arg types
1933        if not isinstance(session, int):
1934            raise TypeError(session)
1935
1936        # API function declaration
1937        self.library.GW2GetFileTypeID.argtypes = [
1938            ct.c_size_t,
1939            ct.c_char_p,
1940            ct.POINTER(ct.c_size_t),
1941            ct.POINTER(ct.c_void_p)
1942        ]
1943
1944        # Variable initialisation
1945        ct_session = ct.c_size_t(session)
1946        ct_file_type = ct.c_char_p(file_type_str.encode('utf-8'))
1947        ct_buffer_length = ct.c_size_t()
1948        ct_buffer = ct.c_void_p()
1949
1950        # API call
1951        status = self.library.GW2GetFileTypeID(
1952            ct_session,
1953            ct_file_type,
1954            ct.byref(ct_buffer_length),
1955            ct.byref(ct_buffer)
1956        )
1957
1958        if status not in successes.success_codes:
1959            log.error(f"\n\tsession: {session}\n\tstatus: {status}")
1960            raise errors.error_codes.get(status, errors.UnknownErrorCode)(status)
1961        else:
1962            log.debug(f"\n\tsession: {session}\n\tstatus: {status}")
1963
1964        # Editor wrote to a buffer, convert it to bytes
1965        file_type_bytes = utils.buffer_to_bytes(
1966            ct_buffer,
1967            ct_buffer_length
1968        )
1969
1970        file_type_id = file_type_bytes.decode()
1971
1972        return file_type_id

Retrieve the Glasswall file type id given a file type string.

Args: session (int): The session integer. file_type_str (str): The file type as a string.

Returns: file_type_id (str): The Glasswall file type id for the specified file type.

def get_file_type_info(self, file_type: Union[str, int]):
1974    def get_file_type_info(self, file_type: Union[str, int]):
1975        """ Retrieve information about a file type based on its identifier.
1976
1977        Args:
1978            file_type (Union[str, int]): The file type identifier. This can be either a string representing a file
1979            extension (e.g. 'bmp') or an integer corresponding to a file type (e.g. 29).
1980
1981        Returns:
1982            - file_type_info (Union[int, str]): Depending on the input 'file_type':
1983                - If `file_type` is a string (e.g. 'bmp'):
1984                    - If the file type is recognised, returns an integer corresponding to that file type.
1985                    - If the file type is not recognised, returns 0.
1986                - If `file_type` is an integer (e.g. 29):
1987                    - If the integer corresponds to a recognised file type, returns a more detailed string description
1988                        of the file type (e.g. 'BMP Image').
1989                    - If the integer does not match any recognised file type, returns an empty string.
1990        """
1991        # Validate arg types
1992        if not isinstance(file_type, (str, int)):
1993            raise TypeError(file_type)
1994
1995        with utils.CwdHandler(self.library_path):
1996            with self.new_session() as session:
1997
1998                if isinstance(file_type, int):
1999                    file_type_info = self.GW2GetFileType(session, file_type)
2000                if isinstance(file_type, str):
2001                    file_type_info = self.GW2GetFileTypeID(session, file_type)
2002
2003                return file_type_info

Retrieve information about a file type based on its identifier.

Args: file_type (Union[str, int]): The file type identifier. This can be either a string representing a file extension (e.g. 'bmp') or an integer corresponding to a file type (e.g. 29).

Returns: - file_type_info (Union[int, str]): Depending on the input 'file_type': - If file_type is a string (e.g. 'bmp'): - If the file type is recognised, returns an integer corresponding to that file type. - If the file type is not recognised, returns 0. - If file_type is an integer (e.g. 29): - If the integer corresponds to a recognised file type, returns a more detailed string description of the file type (e.g. 'BMP Image'). - If the integer does not match any recognised file type, returns an empty string.

@utils.deprecated_function(replacement_function=get_file_type_info)
def get_file_info(self, *args, **kwargs):
2005    @utils.deprecated_function(replacement_function=get_file_type_info)
2006    def get_file_info(self, *args, **kwargs):
2007        """ Deprecated in 1.0.6. Use get_file_type_info. """
2008        pass

Deprecated in 1.0.6. Use get_file_type_info.

def register_report_file(self, session: int, output_file: str):
2039    def register_report_file(self, session: int, output_file: str):
2040        """ Register the report file path for the given session.
2041
2042        Args:
2043            session (int): The session integer.
2044            output_file (str): The file path of the report file.
2045
2046        Returns:
2047            gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.
2048        """
2049        # Validate arg types
2050        if not isinstance(session, int):
2051            raise TypeError(session)
2052        if not isinstance(output_file, (type(None), str)):
2053            raise TypeError(output_file)
2054
2055        result = self._GW2RegisterReportFile(session, output_file)
2056
2057        if result.status not in successes.success_codes:
2058            log.error(format_object(result))
2059            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2060        else:
2061            log.debug(format_object(result))
2062
2063        return result

Register the report file path for the given session.

Args: session (int): The session integer. output_file (str): The file path of the report file.

Returns: gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'output_file', 'status'.

def get_id_info(self, issue_id: int, raise_unsupported: bool = True):
2109    def get_id_info(self, issue_id: int, raise_unsupported: bool = True):
2110        """ Retrieves the group description for the given Issue ID. e.g. issue_id 96 returns "Document Processing Instances"
2111
2112        Args:
2113            issue_id (int): The issue id.
2114            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
2115
2116        Returns:
2117            id_info (str): The group description for the given Issue ID.
2118        """
2119        # Validate arg types
2120        if not isinstance(issue_id, int):
2121            raise TypeError(issue_id)
2122
2123        with utils.CwdHandler(self.library_path):
2124            with self.new_session() as session:
2125                result = self._GW2GetIdInfo(session, issue_id)
2126
2127                if result.status not in successes.success_codes:
2128                    log.error(format_object(result))
2129                    if raise_unsupported:
2130                        raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2131                else:
2132                    log.debug(format_object(result))
2133
2134                return result.id_info

Retrieves the group description for the given Issue ID. e.g. issue_id 96 returns "Document Processing Instances"

Args: issue_id (int): The issue id. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: id_info (str): The group description for the given Issue ID.

def get_all_id_info( self, output_file: Optional[str] = None, raise_unsupported: bool = True) -> str:
2174    def get_all_id_info(self, output_file: Optional[str] = None, raise_unsupported: bool = True) -> str:
2175        """ Retrieves the XML containing all the Issue ID ranges with their group descriptions
2176
2177        Args:
2178            output_file (Optional[str]): The output file path where the analysis file will be written.
2179            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
2180
2181        Returns:
2182            all_id_info (str): A string XML analysis report containing all id info.
2183        """
2184        # Validate arg types
2185        if not isinstance(output_file, (type(None), str)):
2186            raise TypeError(output_file)
2187        if isinstance(output_file, str):
2188            output_file = os.path.abspath(output_file)
2189
2190        with utils.CwdHandler(self.library_path):
2191            with self.new_session() as session:
2192                result = self._GW2GetAllIdInfo(session)
2193
2194                if result.status not in successes.success_codes:
2195                    log.error(format_object(result))
2196                    if raise_unsupported:
2197                        raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2198                else:
2199                    log.debug(format_object(result))
2200
2201                if isinstance(output_file, str):
2202                    # GW2GetAllIdInfo is memory only, write to file
2203                    # make directories that do not exist
2204                    os.makedirs(os.path.dirname(output_file), exist_ok=True)
2205                    with open(output_file, "w") as f:
2206                        f.write(result.all_id_info)
2207
2208                return result.all_id_info

Retrieves the XML containing all the Issue ID ranges with their group descriptions

Args: output_file (Optional[str]): The output file path where the analysis file will be written. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: all_id_info (str): A string XML analysis report containing all id info.

def file_session_status_message(self, session: int, raise_unsupported: bool = True) -> str:
2255    def file_session_status_message(self, session: int, raise_unsupported: bool = True) -> str:
2256        """ Retrieves the Glasswall session status message. Gives a high level indication of the processing that was carried out.
2257
2258        Args:
2259            session (int): The session integer.
2260            raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.
2261
2262        Returns:
2263            result.message (str):The file session status message.
2264        """
2265        # Validate arg types
2266        if not isinstance(session, int):
2267            raise TypeError(session)
2268
2269        result = self._GW2FileSessionStatus(session)
2270
2271        if result.status not in successes.success_codes:
2272            log.error(format_object(result))
2273            if raise_unsupported:
2274                raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2275        else:
2276            log.debug(format_object(result))
2277
2278        return result.message

Retrieves the Glasswall session status message. Gives a high level indication of the processing that was carried out.

Args: session (int): The session integer. raise_unsupported (bool, optional): Default True. Raise exceptions when Glasswall encounters an error. Fail silently if False.

Returns: result.message (str):The file session status message.

def licence_details(self):
2304    def licence_details(self):
2305        """ Returns a string containing details of the licence.
2306
2307        Returns:
2308            result (str): A string containing details of the licence.
2309        """
2310        with self.new_session() as session:
2311            result = self._GW2LicenceDetails(session)
2312
2313            log.debug(f"\n\tsession: {session}\n\tGW2LicenceDetails: {result}")
2314
2315        return result

Returns a string containing details of the licence.

Returns: result (str): A string containing details of the licence.

def register_licence( self, session: int, input_file: Union[str, bytes, bytearray, _io.BytesIO]):
2438    def register_licence(self, session: int, input_file: Union[str, bytes, bytearray, io.BytesIO]):
2439        """ Registers a "gwkey.lic" licence from file path or memory.
2440
2441        Args:
2442            session (int): The session integer.
2443            input_file (Union[str, bytes, bytearray, io.BytesIO]): The "gwkey.lic" licence. It can be provided as a file path (str), bytes, bytearray, or a BytesIO object.
2444
2445        Returns:
2446            - result (glasswall.GwReturnObj): Depending on the input 'input_file':
2447                - If input_file is a str file path:
2448                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'status'.
2449
2450                - If input_file is a file in memory:
2451                    - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.
2452        """
2453        if isinstance(input_file, str):
2454            if not os.path.isfile(input_file):
2455                raise FileNotFoundError(input_file)
2456
2457            input_file = os.path.abspath(input_file)
2458
2459            result = self._GW2RegisterLicenceFile(session, input_file)
2460
2461        elif isinstance(input_file, (bytes, bytearray, io.BytesIO,)):
2462            # Convert bytearray and io.BytesIO to bytes
2463            input_file = utils.as_bytes(input_file)
2464
2465            result = self._GW2RegisterLicenceMemory(session, input_file)
2466
2467        else:
2468            raise TypeError(input_file)
2469
2470        if result.status not in successes.success_codes:
2471            log.error(format_object(result))
2472            raise errors.error_codes.get(result.status, errors.UnknownErrorCode)(result.status)
2473        else:
2474            log.debug(format_object(result))
2475
2476        return result

Registers a "gwkey.lic" licence from file path or memory.

Args: session (int): The session integer. input_file (Union[str, bytes, bytearray, io.BytesIO]): The "gwkey.lic" licence. It can be provided as a file path (str), bytes, bytearray, or a BytesIO object.

Returns: - result (glasswall.GwReturnObj): Depending on the input 'input_file': - If input_file is a str file path: - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'input_file', 'status'.

    - If input_file is a file in memory:
        - gw_return_object (glasswall.GwReturnObj): A GwReturnObj instance with the attributes 'session', 'buffer', 'buffer_length', 'status'.