Skip to content

Commit 61672e5

Browse files
committed
upload without temp file
1 parent 2e115f6 commit 61672e5

File tree

2 files changed

+78
-119
lines changed

2 files changed

+78
-119
lines changed

lib/Utilities/PGP/BatchUpload/MutualAuthUploadUtility.php

Lines changed: 76 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class MutualAuthUploadUtility
99
/**
1010
* Uploads an encrypted file using mTLS with a PKCS#12 (.p12/.pfx) client certificate.
1111
*
12-
* @param string $encryptedPgpBytes The encrypted file content (ASCII-armored).
12+
* @param string $encryptedPgpBytes The encrypted file content.
1313
* @param string $endpointUrl The full API endpoint URL.
1414
* @param string $fileName The file name to use in the multipart upload.
1515
* @param string $p12FilePath Path to the PKCS#12 client certificate file.
@@ -28,103 +28,25 @@ public static function uploadWithP12(
2828
$verify_ssl = true,
2929
$logger = null
3030
) {
31-
$tmpFile = tempnam(sys_get_temp_dir(), 'pgp_');
32-
file_put_contents($tmpFile, $encryptedPgpBytes);
33-
34-
$ch = curl_init();
35-
$cfile = new \CURLFile($tmpFile, 'application/octet-stream', $fileName);
36-
$postFields = ['file' => $cfile];
37-
38-
curl_setopt($ch, CURLOPT_URL, $endpointUrl);
39-
curl_setopt($ch, CURLOPT_POST, 1);
40-
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
41-
42-
// mTLS: client cert (PKCS#12)
43-
curl_setopt($ch, CURLOPT_SSLCERT, $p12FilePath);
44-
curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'P12');
45-
curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $p12Password);
46-
47-
// CA cert for server validation
48-
if ($caCertPath) {
49-
curl_setopt($ch, CURLOPT_CAINFO, $caCertPath);
50-
}
51-
52-
// SSL verification
53-
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verify_ssl);
54-
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verify_ssl ? 2 : 0);
55-
56-
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
57-
58-
// Optional: Add correlation ID for logging/tracing
59-
$correlationId = self::generateCorrelationId();
60-
curl_setopt($ch, CURLOPT_HTTPHEADER, [
61-
"v-c-correlation-id: $correlationId"
62-
]);
63-
64-
// Use CURLOPT_HEADERFUNCTION to parse headers
65-
$responseHeaders = [];
66-
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header_line) use (&$responseHeaders) {
67-
$len = strlen($header_line);
68-
$header_line = trim($header_line);
69-
if (empty($header_line) || strpos($header_line, ':') === false) {
70-
return $len;
71-
}
72-
73-
list($key, $value) = explode(':', $header_line, 2);
74-
$key = trim($key);
75-
$value = trim($value);
76-
77-
if (isset($responseHeaders[$key])) {
78-
if (is_array($responseHeaders[$key])) {
79-
$responseHeaders[$key][] = $value;
80-
} else {
81-
$responseHeaders[$key] = [$responseHeaders[$key], $value];
82-
}
83-
} else {
84-
$responseHeaders[$key] = $value;
85-
}
86-
87-
return $len;
88-
});
89-
90-
$body = curl_exec($ch);
91-
92-
if ($body === false) {
93-
$err = curl_error($ch);
94-
curl_close($ch);
95-
unlink($tmpFile);
96-
$error_message = "cURL error: $err";
97-
if ($logger) $logger->error($error_message);
98-
throw new ApiException($error_message, 500);
99-
}
100-
101-
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
102-
$err = curl_error($ch);
103-
104-
curl_close($ch);
105-
unlink($tmpFile);
106-
107-
if ($err || $httpCode === 0) {
108-
$error_message = $err ? "cURL error: $err" : "API call failed, but for an unknown reason. This could happen if you are disconnected from the network.";
109-
if ($logger) $logger->error($error_message);
110-
throw new ApiException($error_message, 500);
111-
} elseif ($httpCode >= 200 && $httpCode < 300) {
112-
if ($logger) $logger->info("Upload completed for correlationId: $correlationId. Status: $httpCode");
113-
// Check if response is JSON and return decoded JSON if it is, otherwise return as string
114-
$jsonResponse = json_decode($body, true);
115-
$processedBody = (json_last_error() === JSON_ERROR_NONE) ? $jsonResponse : $body;
116-
return [$processedBody, $httpCode, $responseHeaders];
117-
} else {
118-
$msg = "File upload failed. Status code: $httpCode, body: $body";
119-
if ($logger) $logger->error($msg);
120-
throw new ApiException($msg, $httpCode, $responseHeaders, $body);
121-
}
31+
return self::doMultipartUpload(
32+
$encryptedPgpBytes,
33+
$endpointUrl,
34+
$fileName,
35+
[
36+
'type' => 'p12',
37+
'cert' => $p12FilePath,
38+
'password' => $p12Password,
39+
],
40+
$caCertPath,
41+
$verify_ssl,
42+
$logger
43+
);
12244
}
12345

12446
/**
12547
* Uploads an encrypted file using mTLS with client key/cert PEM files.
12648
*
127-
* @param string $encryptedPgpBytes The encrypted file content (ASCII-armored).
49+
* @param string $encryptedPgpBytes The encrypted file content.
12850
* @param string $endpointUrl The full API endpoint URL.
12951
* @param string $fileName The file name to use in the multipart upload.
13052
* @param string $clientCertPath Path to the client certificate (PEM).
@@ -145,22 +67,67 @@ public static function uploadWithKeyAndCert(
14567
$verify_ssl = true,
14668
$logger = null
14769
) {
148-
$tmpFile = tempnam(sys_get_temp_dir(), 'pgp_');
149-
file_put_contents($tmpFile, $encryptedPgpBytes);
70+
return self::doMultipartUpload(
71+
$encryptedPgpBytes,
72+
$endpointUrl,
73+
$fileName,
74+
[
75+
'type' => 'pem',
76+
'cert' => $clientCertPath,
77+
'key' => $clientKeyPath,
78+
'password' => $clientKeyPassword,
79+
],
80+
$caCertPath,
81+
$verify_ssl,
82+
$logger
83+
);
84+
}
15085

86+
/**
87+
* Shared logic for multipart upload with mTLS.
88+
*/
89+
private static function doMultipartUpload(
90+
$encryptedPgpBytes,
91+
$endpointUrl,
92+
$fileName,
93+
$tlsConfig,
94+
$caCertPath,
95+
$verify_ssl,
96+
$logger
97+
){
98+
$boundary = '--------------------------' . microtime(true);
99+
$eol = "\r\n";
100+
// works without this header too: Content-Transfer-Encoding
101+
$multipartBody =
102+
"--$boundary$eol" .
103+
"Content-Disposition: form-data; name=\"file\"; filename=\"$fileName\"$eol" .
104+
"Content-Type: application/octet-stream$eol" .
105+
"Content-Transfer-Encoding: binary$eol$eol" .
106+
$encryptedPgpBytes . $eol .
107+
"--$boundary--$eol";
108+
109+
$correlationId = self::generateCorrelationId();
110+
151111
$ch = curl_init();
152-
$cfile = new \CURLFile($tmpFile, 'application/octet-stream', $fileName);
153-
$postFields = ['file' => $cfile];
154-
155112
curl_setopt($ch, CURLOPT_URL, $endpointUrl);
156113
curl_setopt($ch, CURLOPT_POST, 1);
157-
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
114+
curl_setopt($ch, CURLOPT_POSTFIELDS, $multipartBody);
115+
curl_setopt($ch, CURLOPT_HTTPHEADER, [
116+
"Content-Type: multipart/form-data; boundary=$boundary",
117+
"v-c-correlation-id: $correlationId"
118+
]);
158119

159-
// mTLS: client cert and key
160-
curl_setopt($ch, CURLOPT_SSLCERT, $clientCertPath);
161-
curl_setopt($ch, CURLOPT_SSLKEY, $clientKeyPath);
162-
if ($clientKeyPassword) {
163-
curl_setopt($ch, CURLOPT_KEYPASSWD, $clientKeyPassword);
120+
// mTLS: handle both PKCS#12 and cert and key
121+
if ($tlsConfig['type'] === 'p12') {
122+
curl_setopt($ch, CURLOPT_SSLCERT, $tlsConfig['cert']);
123+
curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'P12');
124+
curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $tlsConfig['password']);
125+
} else {
126+
curl_setopt($ch, CURLOPT_SSLCERT, $tlsConfig['cert']);
127+
curl_setopt($ch, CURLOPT_SSLKEY, $tlsConfig['key']);
128+
if (!empty($tlsConfig['password'])) {
129+
curl_setopt($ch, CURLOPT_KEYPASSWD, $tlsConfig['password']);
130+
}
164131
}
165132

166133
// CA cert for server validation
@@ -173,12 +140,6 @@ public static function uploadWithKeyAndCert(
173140
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verify_ssl ? 2 : 0);
174141

175142
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
176-
177-
// Optional: Add correlation ID for logging/tracing
178-
$correlationId = self::generateCorrelationId();
179-
curl_setopt($ch, CURLOPT_HTTPHEADER, [
180-
"v-c-correlation-id: $correlationId"
181-
]);
182143

183144
// Use CURLOPT_HEADERFUNCTION to parse headers
184145
$responseHeaders = [];
@@ -211,18 +172,15 @@ public static function uploadWithKeyAndCert(
211172
if ($body === false) {
212173
$err = curl_error($ch);
213174
curl_close($ch);
214-
unlink($tmpFile);
215175
$error_message = "cURL error: $err";
216176
if ($logger) $logger->error($error_message);
217177
throw new ApiException($error_message, 500);
218178
}
219179

220180
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
221181
$err = curl_error($ch);
222-
$http_header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
223-
$http_body = substr($body, $http_header_size);
182+
224183
curl_close($ch);
225-
unlink($tmpFile);
226184

227185
if ($err || $httpCode === 0) {
228186
if ($err) {
@@ -236,13 +194,14 @@ public static function uploadWithKeyAndCert(
236194
throw new ApiException($error_message, 500);
237195
} elseif ($httpCode >= 200 && $httpCode < 300) {
238196
if ($logger) $logger->info("Upload completed for correlationId: $correlationId. Status: $httpCode");
239-
$jsonResponse = json_decode($http_body);
197+
// Check if response is JSON and return decoded JSON if it is, otherwise return as string
198+
$jsonResponse = json_decode($body, true);
240199
$processedBody = (json_last_error() === JSON_ERROR_NONE) ? $jsonResponse : $body;
241200
return [$processedBody, $httpCode, $responseHeaders];
242201
} else {
243-
$msg = "File upload failed. Status code: $httpCode, body: $http_body";
202+
$msg = "File upload failed. Status code: $httpCode, body: $body";
244203
if ($logger) $logger->error($msg);
245-
throw new ApiException($msg, $httpCode, $responseHeaders, $http_body);
204+
throw new ApiException($msg, $httpCode, $responseHeaders, $body);
246205
}
247206
}
248207

lib/Utilities/PGP/BatchUpload/PgpEncryptionUtility.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public static function encryptFileToBytes($inputFile, $publicKeyFile)
4141
$literal = new \OpenPGP_LiteralDataPacket($data, ['format' => 'b', 'filename' => basename($inputFile)]);
4242

4343
$encrypted = \OpenPGP_Crypt_Symmetric::encrypt([$pubKeyPacket], new \OpenPGP_Message([$literal]));
44-
45-
return $encrypted->to_bytes();
44+
//also works without converting to string
45+
return (string)$encrypted->to_bytes();
4646
}
4747
}

0 commit comments

Comments
 (0)