[PECL-CVS] [pecl-mail-mailparse] fix/gh20-content-id-parentheses: address reviews
[email protected] (Rasmus Lerdorf) Sun, 5 Apr 2026 11:26:43 +0000
| Newsgroups | php.pecl.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: Rasmus Lerdorf (rlerdorf)
Date: 2026-04-05T07:26:36-04:00
Commit: https://github.com/php/pecl-mail-mailparse/commit/d37d27aa0db7137ee00a98d683d436a99596067e
Raw diff: https://github.com/php/pecl-mail-mailparse/commit/d37d27aa0db7137ee00a98d683d436a99596067e.diff
address reviews
Changed paths:
M mailparse.c
M tests/gh20.phpt
Diff:
diff --git a/mailparse.c b/mailparse.c
index c7a810a..9894a76 100644
--- a/mailparse.c
+++ b/mailparse.c
@@ -1510,18 +1510,37 @@ static int mailparse_get_part_data(php_mimepart *part, zval *return_value)
add_assoc_string(return_value, "content-boundary", part->boundary);
if ((tmpval = zend_hash_find(Z_ARRVAL_P(&headers), hash_key)) != NULL) {
- /* Extract content-id value directly instead of parsing it as an
- * RFC 822 address, which incorrectly strips parenthesized text
- * as comments (GH-20). Just strip angle brackets if present. */
- char *id = Z_STRVAL_P(tmpval);
- size_t len = Z_STRLEN_P(tmpval);
-
- while (len > 0 && (id[0] == ' ' || id[0] == '\t')) { id++; len--; }
- while (len > 0 && (id[len-1] == ' ' || id[len-1] == '\t')) { len--; }
- if (len >= 2 && id[0] == '<' && id[len-1] == '>') {
- add_assoc_stringl(return_value, "content-id", id + 1, len - 2);
- } else {
- add_assoc_stringl(return_value, "content-id", id, len);
+ zval *content_id = tmpval;
+ char *id, *open, *close;
+ size_t len;
+
+ /* Handle multiple Content-ID headers (stored as array) */
+ if (Z_TYPE_P(content_id) == IS_ARRAY) {
+ content_id = zend_hash_index_find(Z_ARRVAL_P(content_id), 0);
+ }
+
+ if (content_id != NULL && Z_TYPE_P(content_id) == IS_STRING) {
+ /* Extract content-id value directly instead of parsing it as an
+ * RFC 822 address, which incorrectly strips parenthesized text
+ * as comments (GH-20). Extract the msg-id between '<' and '>'
+ * if present, otherwise return the trimmed value as-is. */
+ id = Z_STRVAL_P(content_id);
+ len = Z_STRLEN_P(content_id);
+
+ open = memchr(id, '<', len);
+ if (open) {
+ close = memchr(open + 1, '>', len - (open - id) - 1);
+ if (close) {
+ add_assoc_stringl(return_value, "content-id", open + 1, close - open - 1);
+ } else {
+ add_assoc_stringl(return_value, "content-id", id, len);
+ }
+ } else {
+ /* No angle brackets — trim whitespace and return as-is */
+ while (len > 0 && (id[0] == ' ' || id[0] == '\t')) { id++; len--; }
+ while (len > 0 && (id[len-1] == ' ' || id[len-1] == '\t')) { len--; }
+ add_assoc_stringl(return_value, "content-id", id, len);
+ }
}
}
zend_string_release(hash_key);
diff --git a/tests/gh20.phpt b/tests/gh20.phpt
index 451f86f..3a8647e 100644
--- a/tests/gh20.phpt
+++ b/tests/gh20.phpt
@@ -49,10 +49,25 @@ $data3 = mailparse_msg_get_part_data($part3);
echo "content-id rfc: " . $data3['content-id'] . "\n";
+/* RFC-compliant content-id with trailing comment */
+$mime4 = "Content-Type: image/png\r\n" .
+ "Content-ID: <[email protected]> (comment)\r\n" .
+ "\r\n" .
+ "iVBOR\r\n";
+
+$resource4 = mailparse_msg_create();
+mailparse_msg_parse($resource4, $mime4);
+
+$part4 = mailparse_msg_get_part($resource4, 1);
+$data4 = mailparse_msg_get_part_data($part4);
+
+echo "content-id trailing comment: " . $data4['content-id'] . "\n";
+
echo "ok\n";
?>
--EXPECT--
content-id: Facebook_32x32(1)_aa284ba9-f148-4698-9c1f-c8e92bdb842e.png
content-id bare: Facebook_32x32(1)_test.png
content-id rfc: [email protected]
+content-id trailing comment: [email protected]
ok