diff --git a/.github/actions/setup-x64/action.yml b/.github/actions/setup-x64/action.yml index bf4a71d69c337..e5856c2fcdbe4 100644 --- a/.github/actions/setup-x64/action.yml +++ b/.github/actions/setup-x64/action.yml @@ -23,9 +23,3 @@ runs: sudo cp ext/snmp/tests/snmpd.conf /etc/snmp sudo cp ext/snmp/tests/bigtest /etc/snmp sudo service snmpd restart - - sudo groupadd -g 5000 vmail - sudo useradd -m -d /var/vmail -s /bin/false -u 5000 -g vmail vmail - sudo cp ext/imap/tests/setup/dovecot.conf /etc/dovecot/dovecot.conf - sudo cp ext/imap/tests/setup/dovecotpass /etc/dovecot/dovecotpass - sudo service dovecot restart diff --git a/.github/scripts/windows/test_task.bat b/.github/scripts/windows/test_task.bat index d7f76bfc483e0..d5409ee425a74 100644 --- a/.github/scripts/windows/test_task.bat +++ b/.github/scripts/windows/test_task.bat @@ -112,7 +112,7 @@ rem prepare for mail curl -sLo hMailServer.exe https://www.hmailserver.com/download_file/?downloadid=271 hMailServer.exe /verysilent cd %APPVEYOR_BUILD_FOLDER% -%PHP_BUILD_DIR%\php.exe -dextension_dir=%PHP_BUILD_DIR% -dextension=com_dotnet appveyor\setup_hmailserver.php +%PHP_BUILD_DIR%\php.exe -dextension_dir=%PHP_BUILD_DIR% -dextension=com_dotnet .github\setup_hmailserver.php mkdir %PHP_BUILD_DIR%\test_file_cache rem generate php.ini diff --git a/appveyor/setup_hmailserver.php b/.github/setup_hmailserver.php similarity index 65% rename from appveyor/setup_hmailserver.php rename to .github/setup_hmailserver.php index fe661756ad6ca..4ff132296204c 100644 --- a/appveyor/setup_hmailserver.php +++ b/.github/setup_hmailserver.php @@ -1,21 +1,25 @@ -authenticate("Administrator", ""); - -$domain = $hmail->Domains->Add(); -$domain->Name = IMAP_MAIL_DOMAIN; -$domain->Active = true; -$domain->Save(); - -$accounts = $domain->accounts(); - -foreach (IMAP_USERS as $user) { - $account = $accounts->Add(); - $account->Address = "$user@" . IMAP_MAIL_DOMAIN; - $account->Password = IMAP_MAILBOX_PASSWORD; - $account->Active = true; - $account->Save(); -} +authenticate("Administrator", ""); + +$domain = $hmail->Domains->Add(); +$domain->Name = IMAP_MAIL_DOMAIN; +$domain->Active = true; +$domain->Save(); + +$accounts = $domain->accounts(); + +foreach (IMAP_USERS as $user) { + $account = $accounts->Add(); + $account->Address = "$user@" . IMAP_MAIL_DOMAIN; + $account->Password = IMAP_MAILBOX_PASSWORD; + $account->Active = true; + $account->Save(); +} diff --git a/ext/imap/tests/setup/clean.inc b/ext/imap/tests/setup/clean.inc deleted file mode 100644 index 608d553b4ff3f..0000000000000 --- a/ext/imap/tests/setup/clean.inc +++ /dev/null @@ -1,32 +0,0 @@ -Nmsgs; $i++) { - imap_delete($imap_stream, $i); -} - - -$mailboxes = imap_getmailboxes($imap_stream, IMAP_SERVER, '*'); - -if (!is_array($mailbox_suffix)) { - $mailbox_suffixes = [$mailbox_suffix]; -} else { - $mailbox_suffixes = $mailbox_suffix; -} - -foreach ($mailbox_suffixes as $mailbox_suffix) { - foreach($mailboxes as $value) { - // Only delete mailbox with our prefix (+ optional test suffix) - if (preg_match('/\{.*?\}INBOX\.' . IMAP_MAILBOX_PHPT_PREFIX . $mailbox_suffix .'$/', $value->name, $match) == 1) { - imap_deletemailbox($imap_stream, $value->name); - } - } -} - -imap_close($imap_stream, CL_EXPUNGE); diff --git a/ext/imap/tests/setup/dovecot.conf b/ext/imap/tests/setup/dovecot.conf deleted file mode 100644 index 21687a31e4bfe..0000000000000 --- a/ext/imap/tests/setup/dovecot.conf +++ /dev/null @@ -1,47 +0,0 @@ -# 2.2.33.2 (d6601f4ec): /etc/dovecot/dovecot.conf -# Pigeonhole version 0.4.21 (92477967) -listen = *, :: - -# For SSL need to setup a certificate -# See https://wiki.dovecot.org/SSL/DovecotConfiguration -ssl = no - -# Disable plaintext to prevent a warning at each login -disable_plaintext_auth = yes - -auth_mechanisms = cram-md5 -auth_username_format = %u -auth_verbose = yes -auth_debug = yes -auth_failure_delay = 1secs - -# This need dovecot 2.3.12. -# login_proxy_timeout = 500milliseconds -# ^ This would allow to kill login processes early, but needs testing. So would use v instead -# login_proxy_timeout = 5s -# There is a 1 second delay between each reconnection attempt. -# https://doc.dovecot.org/settings/core/#login-proxy-max-reconnects -# This need dovecot 2.3.12. -# login_proxy_max_reconnects = 3 - -# Log config -log_path = /var/log/dovecot.log -# If not set, use the value from log_path -info_log_path = /var/log/dovecot-info.log -# If not set, use the value from info_log_path -debug_log_path = /var/log/dovecot-debug.log -## Mailbox locations and namespaces -mail_location = maildir:/var/vmail/dovecot/mail/%d/%n/Maildir -passdb { - args = scheme=cram-md5 /etc/dovecot/dovecotpass - driver = passwd-file -} -protocols = imap -service auth { - user = root -} -userdb { - args = /etc/dovecot/dovecotpass - driver = passwd-file - override_fields = home=/var/vmail/dovecot/mail/%d/%n -} diff --git a/ext/imap/tests/setup/dovecotpass b/ext/imap/tests/setup/dovecotpass deleted file mode 100644 index f036e583007a4..0000000000000 --- a/ext/imap/tests/setup/dovecotpass +++ /dev/null @@ -1 +0,0 @@ -webmaster@example.com:{CRAM-MD5}be5f3177e9c7c06403272f25d983ba630df4ef40476b353bb3087a8401713451:vmail:vmail diff --git a/ext/imap/tests/setup/imap_include.inc b/ext/imap/tests/setup/imap_include.inc deleted file mode 100644 index f6ee14db7c4b7..0000000000000 --- a/ext/imap/tests/setup/imap_include.inc +++ /dev/null @@ -1,213 +0,0 @@ -$mf; - if ($mf == 'udate') { - if (($z >= $start_time) && ($z <= time())) { - echo "$mf is OK\n"; - } else { - echo "$mf is BAD ($z)\n"; - } - } else { - echo "$mf is $z\n"; - } - } -} - - -/** - * Create a test mailbox and populate with msgs - * - * @param string mailbox_suffix Suffix used to uniquely identify mailboxes - * @param int message_count number of test msgs to be written to new mailbox - * @param null $new_mailbox - * @param bool $simpleMessages - * @param int $flags OP_* (or CL_EXPUNGE) flags to pass to imap_open() sub-call - * @return resource IMAP stream to new mailbox - * @throws Exception - */ -function setup_test_mailbox( - string $mailbox_suffix, - int $message_count, - &$new_mailbox = null, - bool $simpleMessages = true, - int $flags = 0, -){ - // open a stream to default mailbox - $imap_stream = imap_open(IMAP_DEFAULT_MAILBOX, IMAP_MAILBOX_USERNAME, IMAP_MAILBOX_PASSWORD, flags: $flags); - - if ($imap_stream === false) { - throw new Exception("Cannot connect to IMAP server " . IMAP_SERVER . ": " . imap_last_error()); - } - - echo "Create a temporary mailbox and add " . $message_count . " msgs\n"; - $new_mailbox = create_mailbox($imap_stream, $mailbox_suffix, $message_count, $simpleMessages); - - echo "New mailbox created\n"; - - // reopen stream to new mailbox - if (imap_reopen($imap_stream, $new_mailbox) === false) { - throw new Exception("Can't re-open '$new_mailbox' mailbox: " . imap_last_error()); - } - - return $imap_stream; -} - -/** - * Create mailbox and fill with generic emails - * - * @param resource $imap_stream - * @param string $mailbox_suffix - * @param int $message_count - * @param bool $simpleMessages - * @return string - * @throws Exception - */ -function create_mailbox($imap_stream, string $mailbox_suffix, int $message_count, bool $simpleMessages = true): string { - $mailbox = IMAP_DEFAULT_MAILBOX . '.' . IMAP_MAILBOX_PHPT_PREFIX . $mailbox_suffix; - - $mailboxes = imap_getmailboxes($imap_stream, $mailbox, '*'); - - // check mailbox does not already exist - if ($mailboxes) { - foreach($mailboxes as $value) { - if ($value->name == $mailbox) { - throw new Exception("Mailbox '$mailbox' already exists"); - } - } - } - - if (imap_createmailbox($imap_stream, $mailbox) === false) { - throw new Exception("Can't create a temporary mailbox: " . imap_last_error()); - } - - // Add number of test msgs requested - if ($message_count > 0) { - populate_mailbox($imap_stream, $mailbox, $message_count, $simpleMessages); - } - - return $mailbox; -} - -function setup_test_mailbox_for_uid_tests(string $mailbox_suffix, &$msg_no = null, &$msg_uid = null) -{ - $mail_box = setup_test_mailbox($mailbox_suffix, 10); - echo "Delete 4 messages for Unique ID generation\n"; - // Delete messages to remove the numerical ordering - imap_delete($mail_box, 3); - imap_delete($mail_box, 4); - imap_delete($mail_box, 5); - imap_delete($mail_box, 6); - imap_expunge($mail_box); - $msg_no = 5; - $msg_uid = 9; - - return $mail_box; -} - -/** - * Populate a mailbox with generic emails - * - * @param resource $imap_stream - * @param string $mailbox - * @param int $message_count - * @param bool $simpleMessages - */ -function populate_mailbox($imap_stream, string $mailbox, int $message_count, bool $simpleMessages = true): void { - for ($i = 1; $i <= $message_count; $i++) { - if ($simpleMessages) { - $msg = "From: foo@anywhere.com\r\n" - . "To: ". IMAP_USERS[0] . "@" . IMAP_MAIL_DOMAIN . "\r\n" - . "Subject: test$i\r\n" - . "\r\n" - . "$i: this is a test message, please ignore\r\nnewline"; - } else { - $envelope["from"]= "foo@anywhere.com"; - $envelope["to"] = IMAP_USERS[0] . "@" . IMAP_MAIL_DOMAIN; - $envelope["subject"] = "Test msg $i"; - - $part1["type"] = TYPEMULTIPART; - $part1["subtype"] = "mixed"; - - $part2["type"] = TYPETEXT; - $part2["subtype"] = "plain"; - $part2["description"] = "imap_mail_compose() function"; - $part2["contents.data"] = "message 1:xxxxxxxxxxxxxxxxxxxxxxxxxx"; - - $part3["type"] = TYPETEXT; - $part3["subtype"] = "plain"; - $part3["description"] = "Example"; - $part3["contents.data"] = "message 2:yyyyyyyyyyyyyyyyyyyyyyyyyy"; - - $part4["type"] = TYPETEXT; - $part4["subtype"] = "plain"; - $part4["description"] = "Return Values"; - $part4["contents.data"] = "message 3:zzzzzzzzzzzzzzzzzzzzzzzzzz"; - - $body[1] = $part1; - $body[2] = $part2; - $body[3] = $part3; - $body[4] = $part4; - - $msg = imap_mail_compose($envelope, $body); - } - - imap_append($imap_stream, $mailbox, $msg); - } -} - -/** - * Get the mailbox name from a mailbox description, i.e strip off server details. - * - * @param string mailbox complete mailbox name - * @return string mailbox name - */ -function get_mailbox_name(string $mailboxName): string { - - if (preg_match('/\{.*?\}(.*)/', $mailboxName, $match) != 1) { - throw new Exception("Unrecognized mailbox name '$mailboxName'"); - } - - return $match[1]; -} diff --git a/ext/imap/tests/setup/setup.sh b/ext/imap/tests/setup/setup.sh deleted file mode 100644 index efa7c30310c66..0000000000000 --- a/ext/imap/tests/setup/setup.sh +++ /dev/null @@ -1,6 +0,0 @@ -sudo service dovecot stop -sudo groupadd -g 5000 vmail -sudo useradd -m -d /var/vmail -s /bin/false -u 5000 -g vmail vmail -sudo cp ext/imap/tests/setup/dovecot.conf /etc/dovecot/dovecot.conf -sudo cp ext/imap/tests/setup/dovecotpass /etc/dovecot/dovecotpass -sudo service dovecot start diff --git a/ext/imap/tests/setup/skipif.inc b/ext/imap/tests/setup/skipif.inc deleted file mode 100644 index ccb8c1d39aa46..0000000000000 --- a/ext/imap/tests/setup/skipif.inc +++ /dev/null @@ -1,11 +0,0 @@ - --INI-- SMTP=localhost smtp_port=25 +sendmail_from=from@example.com --FILE-- 0) { - // sleep for a while to allow msg to be delivered - sleep(1); - - $num_messages = imap_check($imap_stream)->Nmsgs; - for ($i = $num_messages; $i > 0; $i--) { - $info = imap_headerinfo($imap_stream, $i); - if ($info->subject === $subject) { - imap_delete($imap_stream, $i); - $found = true; - break; - } - } - $repeat_count--; - } - - imap_close($imap_stream, CL_EXPUNGE); - return $found; -} - -$to = "{$users[2]}@$domain"; -$subject = bin2hex(random_bytes(16)); +$to = $users[0]; +$from = ini_get('sendmail_from'); +$cc = ['cc1' => $users[0], 'cc2' => $users[1]]; +$bcc = ['bcc1' => $users[2], 'bcc2' => $users[3]]; +$subject = 'mail_bug72964'; $message = 'hello'; -$headers = "From: webmaster@example.com\r\n" - . "Cc: {$users[0]}@$domain,\r\n\t{$users[1]}@$domain\r\n" - . "Bcc: {$users[2]}@$domain,\r\n {$users[3]}@$domain\r\n"; +$headers = "From: {$from}\r\n" + . "Cc: {$cc['cc1']},\r\n\t{$cc['cc2']}\r\n" + . "Bcc: {$bcc['bcc1']},\r\n {$bcc['bcc2']}\r\n"; $res = mail($to, $subject, $message, $headers); + if ($res !== true) { - die("TEST FAILED : Unable to send test email\n"); -} else { - echo "Message sent OK\n"; + die("Unable to send the email.\n"); } -foreach ($users as $user) { - if (!find_and_delete_message("$user@$domain", $subject)) { - echo "TEST FAILED: email not delivered\n"; - } else { - echo "TEST PASSED: Message sent and deleted OK\n"; +echo "Email sent.\n"; + +foreach ([['to' => $to], $cc, $bcc] as $mailAddresses) { + foreach ($mailAddresses as $recipient => $mailAddress) { + $mailBox = MailBox::login($mailAddress); + $mail = $mailBox->getMailsBySubject($subject); + $mailBox->logout(); + + if ($mail->isAsExpected($from, $to, $subject, $message)) { + echo "Found the email. {$recipient} received.\n"; + } } } ?> +--CLEAN-- +deleteMailsBySubject($subject); + $mailBox->logout(); +} +?> --EXPECT-- -Message sent OK -TEST PASSED: Message sent and deleted OK -TEST PASSED: Message sent and deleted OK -TEST PASSED: Message sent and deleted OK -TEST PASSED: Message sent and deleted OK +Email sent. +Found the email. to received. +Found the email. cc1 received. +Found the email. cc2 received. +Found the email. bcc1 received. +Found the email. bcc2 received. diff --git a/ext/standard/tests/mail/bug80706.phpt b/ext/standard/tests/mail/bug80706.phpt index e2774e924f067..a5ce2c8d394eb 100644 --- a/ext/standard/tests/mail/bug80706.phpt +++ b/ext/standard/tests/mail/bug80706.phpt @@ -1,80 +1,66 @@ --TEST-- -Bug #72964 (White space not unfolded for CC/Bcc headers) ---EXTENSIONS-- -imap ---CONFLICTS-- -imap +Bug #80706 (Headers after Bcc headers may be ignored) --SKIPIF-- --INI-- SMTP=localhost smtp_port=25 +sendmail_from=from@example.com --FILE-- 0) { - // sleep for a while to allow msg to be delivered - sleep(1); - - $num_messages = imap_check($imap_stream)->Nmsgs; - for ($i = $num_messages; $i > 0; $i--) { - $info = imap_headerinfo($imap_stream, $i); - if ($info->subject === $subject) { - $header = imap_fetchheader($imap_stream, $i); - echo "X-Mailer header found: "; - var_dump(strpos($header, 'X-Mailer: bug80706') !== false); - imap_delete($imap_stream, $i); - $found = true; - break; - } - } - $repeat_count--; - } - - imap_close($imap_stream, CL_EXPUNGE); - return $found; -} - -$to = "{$users[1]}@$domain"; -$subject = bin2hex(random_bytes(16)); +$to = $users[0]; +$from = ini_get('sendmail_from'); +$bcc = $users[2]; +$subject = 'mail_bug80706'; $message = 'hello'; -$headers = "From: webmaster@example.com\r\n" - . "Bcc: {$users[2]}@$domain\r\n" - . "X-Mailer: bug80706"; +$xMailer = 'bug80706_x_mailer'; +$headers = "From: {$from}\r\n" + . "Bcc: {$bcc}\r\n" + . "X-Mailer: {$xMailer}"; $res = mail($to, $subject, $message, $headers); + if ($res !== true) { - die("TEST FAILED : Unable to send test email\n"); -} else { - echo "Message sent OK\n"; + die("Unable to send the email.\n"); } -foreach ([$users[1], $users[2]] as $user) { - if (!find_and_delete_message("$user@$domain", $subject)) { - echo "TEST FAILED: email not delivered\n"; - } else { - echo "TEST PASSED: Message sent and deleted OK\n"; +echo "Email sent.\n"; + +foreach (['to' => $to, 'bcc' => $bcc] as $recipient => $mailAddress) { + $mailBox = MailBox::login($mailAddress); + $mail = $mailBox->getMailsBySubject($subject); + $mailBox->logout(); + + if ($mail->isAsExpected($from, $to, $subject, $message)) { + echo "Found the email. {$recipient} received.\n"; + } + + if ($mail->getHeader('X-Mailer') === $xMailer) { + echo "The specified x-Mailer exists.\n\n"; } } ?> +--CLEAN-- +deleteMailsBySubject($subject); + $mailBox->logout(); +} +?> --EXPECT-- -Message sent OK -X-Mailer header found: bool(true) -TEST PASSED: Message sent and deleted OK -X-Mailer header found: bool(true) -TEST PASSED: Message sent and deleted OK +Email sent. +Found the email. to received. +The specified x-Mailer exists. + +Found the email. bcc received. +The specified x-Mailer exists. diff --git a/ext/standard/tests/mail/bug80751.phpt b/ext/standard/tests/mail/bug80751.phpt index fd4fa744fe3ce..b6dc29e42f14f 100644 --- a/ext/standard/tests/mail/bug80751.phpt +++ b/ext/standard/tests/mail/bug80751.phpt @@ -1,97 +1,82 @@ --TEST-- Bug #80751 (Comma in recipient name breaks email delivery) ---EXTENSIONS-- -imap ---CONFLICTS-- -imap --SKIPIF-- --INI-- SMTP=localhost smtp_port=25 --FILE-- \" <{$to}>"; - $found = false; - $repeat_count = 20; // we will repeat a max of 20 times - while (!$found && $repeat_count > 0) { - // sleep for a while to allow msg to be delivered - sleep(1); - - $num_messages = imap_check($imap_stream)->Nmsgs; - for ($i = $num_messages; $i > 0; $i--) { - $info = imap_headerinfo($imap_stream, $i); - if ($info->subject === $subject) { - $header = imap_fetchheader($imap_stream, $i); - echo "Return-Path header found: "; - var_dump(strpos($header, 'Return-Path: joe@example.com') !== false); - echo "To header found: "; - var_dump(strpos($header, "To: \"\" <{$users[1]}@$domain>") !== false); - echo "From header found: "; - var_dump(strpos($header, 'From: "" ') !== false); - echo "Cc header found: "; - var_dump(strpos($header, "Cc: \"Lastname, Firstname\\\\\" <{$users[2]}@$domain>") !== false); - imap_delete($imap_stream, $i); - $found = true; - break; - } - } - $repeat_count--; - } +$from = 'bug80751_from@example.com'; +$fromLine = "\"\" <{$from}>"; - imap_close($imap_stream, CL_EXPUNGE); - return $found; -} +$cc = $users[1]; +$ccLine = "\"Lastname, Firstname\\\\\" <{$cc}>"; -$to = "\"\" <{$users[1]}@$domain>"; -$subject = bin2hex(random_bytes(16)); +$bcc = $users[2]; +$subject = 'mail_bug80751'; $message = 'hello'; -$headers = "From: \"\" \r\n" - . "Cc: \"Lastname, Firstname\\\\\" <{$users[2]}@$domain>\r\n" - . "Bcc: \"Firstname \\\"Ni,ck\\\" Lastname\" <{$users[3]}@$domain>\r\n"; -$res = mail($to, $subject, $message, $headers); +$headers = "From: {$fromLine}\r\n" + . "Cc: {$ccLine}\r\n" + . "Bcc: \"Firstname \\\"Ni,ck\\\" Lastname\" <{$bcc}>\r\n"; + +$res = mail($toLine, $subject, $message, $headers); + if ($res !== true) { - die("TEST FAILED : Unable to send test email\n"); -} else { - echo "Message sent OK\n"; + die("Unable to send the email.\n"); } -foreach ([$users[1], $users[2], $users[3]] as $user) { - if (!find_and_delete_message("$user@$domain", $subject)) { - echo "TEST FAILED: email not delivered\n"; - } else { - echo "TEST PASSED: Message sent and deleted OK\n"; +echo "Email sent.\n"; + +foreach (['to' => $to, 'cc' => $cc, 'bcc' => $bcc] as $recipient => $mailAddress) { + $mailBox = MailBox::login($mailAddress); + $mail = $mailBox->getMailsBySubject($subject); + $mailBox->logout(); + + if ($mail->isAsExpected($fromLine, $toLine, $subject, $message)) { + echo "Found the email. {$recipient} received.\n"; + } + + if ($mail->getHeader('Return-Path') === $from) { + echo "Return-Path is as expected.\n"; + } + + if ($mail->getHeader('Cc') === $ccLine) { + echo "Cc header is as expected.\n\n"; } } ?> +--CLEAN-- +deleteMailsBySubject($subject); + $mailBox->logout(); +} +?> --EXPECT-- -Message sent OK -Return-Path header found: bool(true) -To header found: bool(true) -From header found: bool(true) -Cc header found: bool(true) -TEST PASSED: Message sent and deleted OK -Return-Path header found: bool(true) -To header found: bool(true) -From header found: bool(true) -Cc header found: bool(true) -TEST PASSED: Message sent and deleted OK -Return-Path header found: bool(true) -To header found: bool(true) -From header found: bool(true) -Cc header found: bool(true) -TEST PASSED: Message sent and deleted OK +Email sent. +Found the email. to received. +Return-Path is as expected. +Cc header is as expected. + +Found the email. cc received. +Return-Path is as expected. +Cc header is as expected. + +Found the email. bcc received. +Return-Path is as expected. +Cc header is as expected. diff --git a/ext/standard/tests/mail/mail_basic_alt1-win32.phpt b/ext/standard/tests/mail/mail_basic_alt1-win32.phpt deleted file mode 100644 index a32f5a4443517..0000000000000 --- a/ext/standard/tests/mail/mail_basic_alt1-win32.phpt +++ /dev/null @@ -1,90 +0,0 @@ ---TEST-- -Test mail() function : basic functionality ---EXTENSIONS-- -imap ---CONFLICTS-- -imap ---SKIPIF-- - ---INI-- -max_execution_time = 120 ---FILE-- - 0) { - - // sleep for a while to allow msg to be delivered - sleep(1); - - $current_msg_count = imap_check($imap_stream)->Nmsgs; - - // Iterate over recent msgs to find the one we sent above - for ($i = 1; $i <= $current_msg_count; $i++) { - // get hdr details - $hdr = imap_headerinfo($imap_stream, $i); - - if (substr($hdr->Subject, 0 , strlen($subject_prefix)) == $subject_prefix) { - echo "Id of msg just sent is $i\n"; - echo ".. delete it\n"; - imap_delete($imap_stream, $i); - $found = true; - break; - } - } - - $repeat_count -= 1; -} - -if (!$found) { - echo "TEST FAILED: email not delivered\n"; -} else { - echo "TEST PASSED: Msgs sent and deleted OK\n"; -} - -imap_close($imap_stream, CL_EXPUNGE); -?> ---EXPECTF-- -*** Testing mail() : basic functionality *** -Msg sent OK -Id of msg just sent is %d -.. delete it -TEST PASSED: Msgs sent and deleted OK diff --git a/ext/standard/tests/mail/mail_basic_alt2-win32.phpt b/ext/standard/tests/mail/mail_basic_alt2-win32.phpt deleted file mode 100644 index 7ad202d22af63..0000000000000 --- a/ext/standard/tests/mail/mail_basic_alt2-win32.phpt +++ /dev/null @@ -1,87 +0,0 @@ ---TEST-- -Test mail() function : basic functionality ---EXTENSIONS-- -imap ---CONFLICTS-- -imap ---SKIPIF-- - ---INI-- -max_execution_time = 120 ---FILE-- - 0) { - - // sleep for a while to allow msg to be delivered - sleep(1); - - $current_msg_count = imap_check($imap_stream)->Nmsgs; - - // Iterate over recent msgs to find the one we sent above - for ($i = 1; $i <= $current_msg_count; $i++) { - // get hdr details - $hdr = imap_headerinfo($imap_stream, $i); - - if (substr($hdr->Subject, 0 , strlen($subject_prefix)) == $subject_prefix) { - echo "Id of msg just sent is $i\n"; - echo ".. delete it\n"; - imap_delete($imap_stream, $i); - $found = true; - break; - } - } - - $repeat_count -= 1; -} - -if (!$found) { - echo "TEST FAILED: email not delivered\n"; -} else { - echo "TEST PASSED: Msgs sent and deleted OK\n"; -} - -imap_close($imap_stream, CL_EXPUNGE); -?> ---EXPECTF-- -*** Testing mail() : basic functionality *** -Msg sent OK -Id of msg just sent is %d -.. delete it -TEST PASSED: Msgs sent and deleted OK diff --git a/ext/standard/tests/mail/mail_basic_alt3-win32.phpt b/ext/standard/tests/mail/mail_basic_alt3-win32.phpt deleted file mode 100644 index 15d4619257828..0000000000000 --- a/ext/standard/tests/mail/mail_basic_alt3-win32.phpt +++ /dev/null @@ -1,87 +0,0 @@ ---TEST-- -Test mail() function : basic functionality ---EXTENSIONS-- -imap ---CONFLICTS-- -imap ---SKIPIF-- - ---INI-- -max_execution_time = 120 ---FILE-- - 0) { - - // sleep for a while to allow msg to be delivered - sleep(1); - - $current_msg_count = imap_check($imap_stream)->Nmsgs; - - // Iterate over recent msgs to find the one we sent above - for ($i = 1; $i <= $current_msg_count; $i++) { - // get hdr details - $hdr = imap_headerinfo($imap_stream, $i); - - if (substr($hdr->Subject, 0 , strlen($subject_prefix)) == $subject_prefix) { - echo "Id of msg just sent is $i\n"; - echo ".. delete it\n"; - imap_delete($imap_stream, $i); - $found = true; - break; - } - } - - $repeat_count -= 1; -} - -if (!$found) { - echo "TEST FAILED: email not delivered\n"; -} else { - echo "TEST PASSED: Msgs sent and deleted OK\n"; -} - -imap_close($imap_stream, CL_EXPUNGE); -?> ---EXPECTF-- -*** Testing mail() : basic functionality *** -Msg sent OK -Id of msg just sent is %d -.. delete it -TEST PASSED: Msgs sent and deleted OK diff --git a/ext/standard/tests/mail/mail_basic_alt4-win32.phpt b/ext/standard/tests/mail/mail_basic_alt4-win32.phpt deleted file mode 100644 index 1fd41bf1928ff..0000000000000 --- a/ext/standard/tests/mail/mail_basic_alt4-win32.phpt +++ /dev/null @@ -1,88 +0,0 @@ ---TEST-- -Test mail() function : basic functionality ---EXTENSIONS-- -imap ---CONFLICTS-- -imap ---SKIPIF-- - ---INI-- -max_execution_time = 120 ---FILE-- - 0) { - - // sleep for a while to allow msg to be delivered - sleep(1); - - $current_msg_count = imap_check($imap_stream)->Nmsgs; - - // Iterate over recent msgs to find the one we sent above - for ($i = 1; $i <= $current_msg_count; $i++) { - // get hdr details - $hdr = imap_headerinfo($imap_stream, $i); - - if (substr($hdr->Subject, 0 , strlen($subject_prefix)) == $subject_prefix) { - echo "Id of msg just sent is $i\n"; - echo ".. delete it\n"; - imap_delete($imap_stream, $i); - $found = true; - break; - } - } - - $repeat_count -= 1; -} - -if (!$found) { - echo "TEST FAILED: email not delivered\n"; -} else { - echo "TEST PASSED: Msgs sent and deleted OK\n"; -} - -imap_close($imap_stream, CL_EXPUNGE); -?> ---EXPECTF-- -*** Testing mail() : basic functionality *** -Msg sent OK -Id of msg just sent is %d -.. delete it -TEST PASSED: Msgs sent and deleted OK diff --git a/ext/standard/tests/mail/mail_basic_win.phpt b/ext/standard/tests/mail/mail_basic_win.phpt new file mode 100644 index 0000000000000..8882d5945f835 --- /dev/null +++ b/ext/standard/tests/mail/mail_basic_win.phpt @@ -0,0 +1,102 @@ +--TEST-- +Test mail() function : basic functionality (win) +--SKIPIF-- + +--INI-- +SMTP=localhost +smtp_port=25 +--FILE-- + 'from@example.com', + 'premise' => function ($from, $to, $subject, $message) { + ini_set('sendmail_from', $from); + return mail($to, $subject, $message); + } + ], + [ + 'from' => 'ex_from@example.com', + 'premise' => function ($from, $to, $subject, $message) { + ini_restore('sendmail_from'); + $headers = "from: {$from}"; + return mail($to, $subject, $message, $headers); + } + ], + [ + 'from' => 'ex_from@example.com', + 'premise' => function ($from, $to, $subject, $message) { + ini_restore('sendmail_from'); + $headers = "FRom: {$from}"; + return mail($to, $subject, $message, $headers); + } + ], + [ + 'from' => 'ex_from@example.com', + 'premise' => function ($from, $to, $subject, $message) { + ini_restore('sendmail_from'); + $headers = "from: {$from}"; + $parameters = 'addons'; // should be ignored + return mail($to, $subject, $message, $headers, $parameters); + } + ], +]; + +foreach ($cases as $index => ['from' => $from, 'premise' => $premise]) { + echo "========== Case {$index} ==========\n"; + + $to = $users[$index]; + $subject = "{$index}: Basic PHPT test for mail() function"; + $message = <<getMailsBySubject($subject); + $mailBox->logout(); + + if ($mail->isAsExpected($from, $to, $subject, $message)) { + echo "Found the email.\n\n"; + } +} +?> +--CLEAN-- +deleteMailsBySubject($subject); + $mailBox->logout(); +} +?> +--EXPECT-- +========== Case 0 ========== +Email sent. +Found the email. + +========== Case 1 ========== +Email sent. +Found the email. + +========== Case 2 ========== +Email sent. +Found the email. + +========== Case 3 ========== +Email sent. +Found the email. diff --git a/ext/standard/tests/mail/mail_include.inc b/ext/standard/tests/mail/mail_include.inc deleted file mode 100644 index 60f84aa35cfe6..0000000000000 --- a/ext/standard/tests/mail/mail_include.inc +++ /dev/null @@ -1,150 +0,0 @@ -name == $mailbox) { - exit ("TEST FAILED : Mailbox '$mailbox' already exists\n"); - } - } - } - - if (imap_createmailbox($imap_stream, $mailbox) === false) { - return false; - } - - // Add number of test msgs requested - if ($message_count > 0) { - populate_mailbox($imap_stream, $mailbox, $message_count, $msg_type); - } - - return $mailbox; -} - -/** - * Populate a mailbox with generic emails - * - * @param resource $imap_stream - * @param string $mailbox - */ -function populate_mailbox($imap_stream, $mailbox, $message_count, $msg_type = "simple"){ - - global $users, $domain; - - for($i = 1; $i <= $message_count; $i++) { - if ($msg_type == "simple") { - $msg = "From: foo@anywhere.com\r\n" - . "To: $users[0]@$domain\r\n" - . "Subject: test$i\r\n" - . "\r\n" - . "$i: this is a test message, please ignore\r\n"; - } else { - $envelope["from"]= "foo@anywhere.com"; - $envelope["to"] = "$users[0]@$domain"; - $envelope["subject"] = "Test msg $i"; - - $part1["type"] = TYPEMULTIPART; - $part1["subtype"] = "mixed"; - - $part2["type"] = TYPETEXT; - $part2["subtype"] = "plain"; - $part2["description"] = "imap_mail_compose() function"; - $part2["contents.data"] = "message 1:xxxxxxxxxxxxxxxxxxxxxxxxxx"; - - $part3["type"] = TYPETEXT; - $part3["subtype"] = "plain"; - $part3["description"] = "Example"; - $part3["contents.data"] = "message 2:yyyyyyyyyyyyyyyyyyyyyyyyyy"; - - $part4["type"] = TYPETEXT; - $part4["subtype"] = "plain"; - $part4["description"] = "Return Values"; - $part4["contents.data"] = "message 3:zzzzzzzzzzzzzzzzzzzzzzzzzz"; - - $body[1] = $part1; - $body[2] = $part2; - $body[3] = $part3; - $body[4] = $part4; - - $msg = imap_mail_compose($envelope, $body); - } - - imap_append($imap_stream, $mailbox, $msg); - } -} - -/** - * Get the mailbox name from a mailbox description, i.e strip off server details. - * - * @param string mailbox complete mailbox name - * @return mailbox name - */ -function get_mailbox_name($mailbox){ - - if (preg_match('/\{.*?\}(.*)/', $mailbox, $match) != 1) { - echo "Unrecpognized mailbox name\n"; - return false; - } - - return $match[1]; -} - -?> diff --git a/ext/standard/tests/mail/mail_skipif.inc b/ext/standard/tests/mail/mail_skipif.inc deleted file mode 100644 index 83e42817a0a7f..0000000000000 --- a/ext/standard/tests/mail/mail_skipif.inc +++ /dev/null @@ -1,25 +0,0 @@ - 10, 'usec' => 10)); - // imap uses tcp port 143 - socket_connect($socket, "localhost", 143) or die ("skip can't socket to mail server"); -} - -// Change these to make tests run successfully -$mailbox = '{127.0.0.1:143}'; -$username = 'webmaster@example.com'; -$password = 'p4ssw0rd'; -$options = OP_HALFOPEN; // this should be enough to verify server present -$retries = 0; // don't retry connect on failure - -$mbox = @imap_open($mailbox, $username, $password, $options, $retries); -if (!$mbox) { - die("skip could not connect to mailbox $mailbox"); -} -imap_close($mbox); -?> diff --git a/ext/standard/tests/mail/mail_util.inc b/ext/standard/tests/mail/mail_util.inc new file mode 100644 index 0000000000000..72e9f32fb82b3 --- /dev/null +++ b/ext/standard/tests/mail/mail_util.inc @@ -0,0 +1,261 @@ +fp = @fsockopen(self::HOST, self::PORT)) === false) { + die('cannot open imap socket'); + } + } + + public static function getConnection(): self + { + if (!static::$init) { + static::$instance = new self(); + } + + return static::$instance; + } + + public function disconnect(): void + { + fclose($this->fp); + } + + public function fail(string $message): void + { + $this->disconnect(); + die($message); + } + + public function send(string $tag, string $command): void + { + fputs($this->fp, "{$tag} {$command}\r\n"); + } + + public function isSuccess(string $tag): bool + { + $start = time(); + while (!feof($this->fp)) { + $line = fgets($this->fp); + if (!$line) { + return false; + } + if (str_contains($line, $tag)) { + if (preg_match('/(NO|BAD|failed|Failure)/i', $line)) { + return false; + } + return true; + } + if (time() - $start > self::TIMEOUT) { + $this->fail("{$tag} timeout"); + } + } + return false; + } + + public function getResponse(string $tag, bool $returnArray = false): string|array + { + $start = time(); + $output = $returnArray ? [] : ''; + while (!feof($this->fp)) { + $line = fgets($this->fp); + if (!$line) { + $this->fail("{$tag} failed"); + } + if ($returnArray) { + $output[] = $line; + } else { + $output .= $line; + } + if (str_contains($line, $tag)) { + if (preg_match('/(NO|BAD|failed|Failure)/i', $line)) { + $this->fail("{$tag} failed"); + } + return $output; + } + if (time() - $start > self::TIMEOUT) { + $this->fail("{$tag} timeout"); + } + } + $this->fail("{$tag} failed"); + } +} + +class MailBox +{ + private MailConnecter $mailConnecter; + private const PASSWORD = 'p4ssw0rd'; + public const USERS = [ + 'webmaster@example.com', + 'info@example.com', + 'admin@example.com', + 'foo@example.com', + ]; + + private const LOGIN = 'A00001'; + private const LOGOUT = 'A00002'; + private const SELECT_MAILBOX = 'A00003'; + private const SEARCH = 'A00004'; + private const FETCH_HEADERS = 'A00005'; + private const FETCH_BODY = 'A00006'; + private const DELETE = 'A00007'; + private const EXPUNGE = 'A00008'; + + private function __construct() + { + $this->mailConnecter = MailConnecter::getConnection(); + } + + public static function login(string $user): self + { + $self = new self(); + $self->mailConnecter->send(self::LOGIN, 'LOGIN '.$user.' '.self::PASSWORD); + if (!$self->mailConnecter->isSuccess(self::LOGIN)) { + $self->mailConnecter->fail('login failed'); + } + return $self; + } + + public function logout(): void + { + $this->mailConnecter->send(self::LOGOUT, 'LOGOUT'); + if (!$this->mailConnecter->isSuccess(self::LOGOUT)) { + $this->mailConnecter->fail('logout failed'); + } + } + + private function getUidsBySubject(string $subject): array + { + $this->mailConnecter->send(self::SELECT_MAILBOX, 'SELECT "INBOX"'); + if (!$this->mailConnecter->isSuccess(self::SELECT_MAILBOX)) { + $this->mailConnecter->fail('select mailbox failed'); + } + + $this->mailConnecter->send(self::SEARCH, "UID SEARCH SUBJECT \"{$subject}\""); + $res = $this->mailConnecter->getResponse(self::SEARCH); + preg_match('/SEARCH ([0-9 ]+)/is', $res, $matches); + return isset($matches[1]) ? explode(' ', trim($matches[1])) : []; + } + + public function getMailsBySubject(string $subject): MailCollection + { + return new MailCollection(array_map( + fn($uid) => $this->getHeaders($uid) + ['Body' => $this->getBody($uid)], + $this->getUidsBySubject($subject), + )); + } + + private function getHeaders(int $uid): array + { + $this->mailConnecter->send(self::FETCH_HEADERS, "UID FETCH {$uid} (BODY[HEADER])"); + $res = $this->mailConnecter->getResponse(self::FETCH_HEADERS, true); + + $headers = []; + foreach ($res as $line) { + $line = trim($line); + if (!$line) { + continue; + } + $items = explode(':', $line); + preg_match('/^(.+?):(.+?)$/', $line, $matches); + $key = trim($matches[1] ?? ''); + $val = trim($matches[2] ?? ''); + if (!$key || !$val || $val === self::FETCH_HEADERS.' OK UID completed' || $val === ')') { + continue; + } + + $headers[$key] = $val; + } + return $headers; + } + + private function getBody(int $uid): string + { + $this->mailConnecter->send(self::FETCH_BODY, "UID FETCH {$uid} (BODY[TEXT])"); + $body = $this->mailConnecter->getResponse(self::FETCH_BODY, true); + $count = count($body); + if ($count <= 3) { + return ''; + } + + return implode('', array_slice($body, 1, $count - 3)); + } + + public function deleteMailsBySubject(string $subject): void + { + array_map( + fn($uid) => $this->mailConnecter->send(self::DELETE, "UID STORE {$uid} +FLAGS (\\Deleted)"), + $this->getUidsBySubject($subject), + ); + $this->mailConnecter->send(self::EXPUNGE, 'EXPUNGE'); + } +} + +class MailCollection +{ + public function __construct(private ?array $mailData) {} + + public function isAsExpected(string $from, string $to, string $subject, string $message): bool + { + $result = true; + + if (!$this->mailData) { + $result = false; + echo "Email data does not exist.\n"; + } + if (!$this->count() > 1) { + $result = false; + echo "Multiple email data exist.\n"; + } + if ($this->getHeader('From', true) !== $from) { + $result = false; + echo "from does not match.\n"; + } + if ($this->getHeader('To', true) !== $to) { + $result = false; + echo "to does not match.\n"; + } + if ($this->getHeader('Subject', true) !== $subject) { + $result = false; + echo "subject does not match.\n"; + } + if (trim($this->getBody()) !== trim($message)) { + $result = false; + echo "body does not match.\n"; + } + + return $result; + } + + public function count(): int + { + return count($this->mailData); + } + + public function getHeader(string $field, bool $ignoreCases = false, int $offset = 0) + { + if ($ignoreCases) { + $mail = array_change_key_case($this->mailData[$offset] ?? []); + $field = strtolower($field); + } else { + $mail = $this->mailData[$offset] ?? []; + } + + return $mail[$field] ?? null; + } + + public function getBody(int $offset = 0): ?string + { + return $this->mailData[$offset]['Body'] ?? null; + } +} diff --git a/ext/standard/tests/mail/mail_variation_alt1-win32.phpt b/ext/standard/tests/mail/mail_variation_alt1-win32.phpt deleted file mode 100644 index 5551fb3a843fc..0000000000000 --- a/ext/standard/tests/mail/mail_variation_alt1-win32.phpt +++ /dev/null @@ -1,89 +0,0 @@ ---TEST-- -Test mail() function : basic functionality ---EXTENSIONS-- -imap ---CONFLICTS-- -imap ---SKIPIF-- - ---INI-- -max_execution_time = 120 ---FILE-- - 0) { - - // sleep for a while to allow msg to be delivered - sleep(1); - - $current_msg_count = imap_check($imap_stream)->Nmsgs; - - // Iterate over recent msgs to find the one we sent above - for ($i = 1; $i <= $current_msg_count; $i++) { - // get hdr details - $hdr = imap_headerinfo($imap_stream, $i); - - if (substr($hdr->Subject, 0 , strlen($subject_prefix)) == $subject_prefix) { - echo "Id of msg just sent is $i\n"; - echo ".. delete it\n"; - imap_delete($imap_stream, $i); - $found = true; - break; - } - } - - $repeat_count -= 1; -} - -if (!$found) { - echo "TEST FAILED: email not delivered\n"; -} else { - echo "TEST PASSED: Msgs sent and deleted OK\n"; -} - -imap_close($imap_stream, CL_EXPUNGE); -?> -===DONE=== ---EXPECTF-- -*** Testing mail() : basic functionality *** - -Warning: mail(): Failed to connect to mailserver at "localhost" port 2525, verify your "SMTP" and "smtp_port" setting in php.ini or use ini_set() in %s on line %d -TEST COMPLETED : Unable to send test email diff --git a/ext/standard/tests/mail/mail_variation_alt2-win32.phpt b/ext/standard/tests/mail/mail_variation_alt2-win32.phpt deleted file mode 100644 index 38c90fd037190..0000000000000 --- a/ext/standard/tests/mail/mail_variation_alt2-win32.phpt +++ /dev/null @@ -1,89 +0,0 @@ ---TEST-- -Test mail() function : basic functionality ---EXTENSIONS-- -imap ---CONFLICTS-- -imap ---SKIPIF-- - ---INI-- -max_execution_time = 120 ---FILE-- - 0) { - - // sleep for a while to allow msg to be delivered - sleep(1); - - $current_msg_count = imap_check($imap_stream)->Nmsgs; - - // Iterate over recent msgs to find the one we sent above - for ($i = 1; $i <= $current_msg_count; $i++) { - // get hdr details - $hdr = imap_headerinfo($imap_stream, $i); - - if (substr($hdr->Subject, 0 , strlen($subject_prefix)) == $subject_prefix) { - echo "Id of msg just sent is $i\n"; - echo ".. delete it\n"; - imap_delete($imap_stream, $i); - $found = true; - break; - } - } - - $repeat_count -= 1; -} - -if (!$found) { - echo "TEST FAILED: email not delivered\n"; -} else { - echo "TEST PASSED: Msgs sent and deleted OK\n"; -} - -imap_close($imap_stream, CL_EXPUNGE); -?> -===DONE=== ---EXPECTF-- -*** Testing mail() : basic functionality *** - -Warning: mail(): Failed to connect to mailserver at "localplace" port 25, verify your "SMTP" and "smtp_port" setting in php.ini or use ini_set() in %s on line %d -TEST COMPLETED : Unable to send test email diff --git a/ext/standard/tests/mail/mail_variation_alt3-win32.phpt b/ext/standard/tests/mail/mail_variation_alt3-win32.phpt deleted file mode 100644 index f4b2bf4a8e63a..0000000000000 --- a/ext/standard/tests/mail/mail_variation_alt3-win32.phpt +++ /dev/null @@ -1,88 +0,0 @@ ---TEST-- -Test mail() function : basic functionality ---EXTENSIONS-- -imap ---CONFLICTS-- -imap ---SKIPIF-- - ---INI-- -max_execution_time = 120 ---FILE-- - 0) { - - // sleep for a while to allow msg to be delivered - sleep(1); - - $current_msg_count = imap_check($imap_stream)->Nmsgs; - - // Iterate over recent msgs to find the one we sent above - for ($i = 1; $i <= $current_msg_count; $i++) { - // get hdr details - $hdr = imap_headerinfo($imap_stream, $i); - - if (substr($hdr->Subject, 0 , strlen($subject_prefix)) == $subject_prefix) { - echo "Id of msg just sent is $i\n"; - echo ".. delete it\n"; - imap_delete($imap_stream, $i); - $found = true; - break; - } - } - - $repeat_count -= 1; -} - -if (!$found) { - echo "TEST FAILED: email not delivered\n"; -} else { - echo "TEST PASSED: Msgs sent and deleted OK\n"; -} - -imap_close($imap_stream, CL_EXPUNGE); -?> -===DONE=== ---EXPECTF-- -*** Testing mail() : basic functionality *** - -Warning: mail(): Bad Message Return Path in %s on line %d -TEST COMPLETED : Unable to send test email diff --git a/ext/standard/tests/mail/mail_variation_win.phpt b/ext/standard/tests/mail/mail_variation_win.phpt new file mode 100644 index 0000000000000..8d8fdf348ee0b --- /dev/null +++ b/ext/standard/tests/mail/mail_variation_win.phpt @@ -0,0 +1,90 @@ +--TEST-- +Test mail() function : basic functionality (win) +--SKIPIF-- + +--INI-- +SMTP=localhost +smtp_port=25 +--FILE-- + 'From is not set', + 'premise' => function () { + ini_set('SMTP', 'localhost'); + ini_set('smtp_port', 25); + } + ], + [ + 'label' => 'Invalid port', + 'premise' => function () { + ini_set('SMTP', 'localhost'); + ini_set('smtp_port', 2525); + ini_set('sendmail_from', 'from@example.com'); + } + ], + [ + 'label' => 'Invalid host', + 'premise' => function () { + ini_set('SMTP', 'localplace'); + ini_set('smtp_port', 25); + ini_set('sendmail_from', 'from@example.com'); + } + ], +]; + +foreach ($cases as $index => ['label' => $label, 'premise' => $premise]) { + echo "========== {$label} ==========\n"; + + $premise(); + + $to = $users[$index]; + $subject = "{$index}: Basic PHPT test for mail() function"; + $message = <<getMailsBySubject($subject); + $mailBox->logout(); + + if ($mail->count() > 0) { + echo "Found the email.\n\n"; + } +} +?> +--CLEAN-- +deleteMailsBySubject($subject); + $mailBox->logout(); +} +?> +--EXPECTF-- +========== From is not set ========== + +Warning: mail(): Bad Message Return Path in %s on line %d +========== Invalid port ========== + +Warning: mail(): Failed to connect to mailserver at "localhost" port 2525, verify your "SMTP" and "smtp_port" setting in php.ini or use ini_set() in %s on line %d +========== Invalid host ========== + +Warning: mail(): Failed to connect to mailserver at "localplace" port 1025, verify your "SMTP" and "smtp_port" setting in php.ini or use ini_set() in %s on line %d + diff --git a/ext/standard/tests/mail/mail_windows_skipif.inc b/ext/standard/tests/mail/mail_windows_skipif.inc new file mode 100644 index 0000000000000..4a2bc9ba0be03 --- /dev/null +++ b/ext/standard/tests/mail/mail_windows_skipif.inc @@ -0,0 +1,15 @@ +