Skip to content

Commit f693f55

Browse files
committed
fix: Improve pointer handling and refactor
1 parent fced710 commit f693f55

File tree

2 files changed

+28
-38
lines changed

2 files changed

+28
-38
lines changed

src/c2pa/c2pa.py

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -683,26 +683,18 @@ def sign_file_with_callback_signer(
683683
source_path: Union[str, Path],
684684
dest_path: Union[str, Path],
685685
manifest: str,
686-
callback: Callable[[bytes], bytes],
687-
alg: C2paSigningAlg,
688-
certs: str,
689-
tsa_url: Optional[str] = None,
690-
data_dir: Optional[Union[str, Path]] = None
686+
signer: 'Signer'
691687
) -> bytes:
692-
"""Sign a file with a C2PA manifest using a callback signer.
688+
"""Sign a file with a C2PA manifest using a signer.
693689
694-
This function provides a shortcut to sign files using a callback-based signer
690+
This function provides a shortcut to sign files using a signer
695691
and returns the raw manifest bytes.
696692
697693
Args:
698694
source_path: Path to the source file
699695
dest_path: Path to write the signed file to
700696
manifest: The manifest JSON string
701-
callback: Function that signs data and returns the signature
702-
alg: The signing algorithm to use
703-
certs: Certificate chain in PEM format
704-
tsa_url: Optional RFC 3161 timestamp authority URL
705-
data_dir: Optional directory to write binary resources to
697+
signer: The signer to use
706698
707699
Returns:
708700
The manifest bytes (binary data)
@@ -713,9 +705,6 @@ def sign_file_with_callback_signer(
713705
C2paError.NotSupported: If the file type cannot be determined
714706
"""
715707
try:
716-
# Create a signer from the callback
717-
signer = Signer.from_callback(callback, alg, certs, tsa_url)
718-
719708
# Create a builder from the manifest
720709
builder = Builder(manifest)
721710

@@ -733,12 +722,6 @@ def sign_file_with_callback_signer(
733722
# Use the builder's internal signing logic
734723
result, manifest_bytes = builder._sign_internal(signer, mime_type, source_stream, dest_stream)
735724

736-
# If we have manifest bytes and a data directory, write them
737-
if manifest_bytes and data_dir:
738-
manifest_path = os.path.join(str(data_dir), 'manifest.json')
739-
with open(manifest_path, 'wb') as f:
740-
f.write(manifest_bytes)
741-
742725
return manifest_bytes
743726

744727
except Exception as e:
@@ -755,8 +738,6 @@ def sign_file_with_callback_signer(
755738
# Ensure resources are cleaned up
756739
if 'builder' in locals():
757740
builder.close()
758-
if 'signer' in locals():
759-
signer.close()
760741

761742

762743
class Stream:
@@ -1349,19 +1330,14 @@ def from_info(cls, signer_info: C2paSignerInfo) -> 'Signer':
13491330
Raises:
13501331
C2paError: If there was an error creating the signer
13511332
"""
1352-
# Validate signer info before creating
1353-
if not signer_info.sign_cert or not signer_info.private_key:
1354-
raise C2paError("Missing certificate or private key")
1355-
13561333
signer_ptr = _lib.c2pa_signer_from_info(ctypes.byref(signer_info))
13571334

13581335
if not signer_ptr:
13591336
error = _parse_operation_result_for_error(_lib.c2pa_error())
13601337
if error:
13611338
# More detailed error message when possible
13621339
raise C2paError(error)
1363-
raise C2paError(
1364-
"Failed to create signer from configured signer info")
1340+
raise C2paError("Failed to create signer from info")
13651341

13661342
return cls(signer_ptr)
13671343

@@ -1898,11 +1874,20 @@ def _sign_internal(
18981874

18991875
# Capture the manifest bytes if available
19001876
manifest_bytes = b""
1901-
if manifest_bytes_ptr:
1902-
# Convert the C pointer to Python bytes
1903-
manifest_bytes = bytes(manifest_bytes_ptr[:result])
1904-
# Free the C-allocated memory
1905-
_lib.c2pa_manifest_bytes_free(manifest_bytes_ptr)
1877+
if manifest_bytes_ptr and result > 0:
1878+
try:
1879+
# Convert the C pointer to Python bytes
1880+
manifest_bytes = bytes(manifest_bytes_ptr[:result])
1881+
except Exception:
1882+
# If there's any error accessing the memory, just return empty bytes
1883+
manifest_bytes = b""
1884+
finally:
1885+
# Always free the C-allocated memory, even if we failed to copy it
1886+
try:
1887+
_lib.c2pa_manifest_bytes_free(manifest_bytes_ptr)
1888+
except Exception:
1889+
# Ignore errors during cleanup
1890+
pass
19061891

19071892
return result, manifest_bytes
19081893
finally:

tests/test_unit_tests.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,14 @@ def sign_callback(data: bytes) -> bytes:
928928

929929
return signature
930930

931+
# Create signer with callback
932+
signer = Signer.from_callback(
933+
callback=sign_callback,
934+
alg=SigningAlg.ES256,
935+
certs=self.certs.decode('utf-8'),
936+
tsa_url="http://timestamp.digicert.com"
937+
)
938+
931939
# Import the new function
932940
from c2pa.c2pa import sign_file_with_callback_signer
933941

@@ -936,10 +944,7 @@ def sign_callback(data: bytes) -> bytes:
936944
source_path=self.testPath,
937945
dest_path=output_path,
938946
manifest=self.manifestDefinition,
939-
callback=sign_callback,
940-
alg=SigningAlg.ES256,
941-
certs=self.certs.decode('utf-8'),
942-
tsa_url="http://timestamp.digicert.com"
947+
signer=signer
943948
)
944949

945950
# Verify the output file was created

0 commit comments

Comments
 (0)