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