Я пишу регулярное выражение, которое может интерактивно проверять коды ответов SMTP, как только диалог SMTP будет завершен, он должен передать следующее регулярное выражение (некоторые скобки добавлены для лучшей читаемости):
^(220)(250){3,}(354)(250)(221)$
Или с ( out ) аутентификацией:
^(220)(250)((334){2}(235))?(250){2,}(354)(250)(221)$
Я пытаюсь переписать вышеперечисленные выражения, чтобы я мог интерактивно проверять, идет ли диалог, как ожидалось, иначе вежливо отправить команду QUIT
и закрыть соединение, сохраняя пропускную способность и время, но мне сложно писать оптимальные регулярное выражение. До сих пор мне удалось придумать:
^(220(250(334(235(250(354(250(221)?)?)?){0,})?){0,2})?)?$
Который, помимо только сопоставления аутентифицированных подключений, имеет некоторые ошибки … Например, он соответствует:
220250334235250354250221 220250334334235250354250221
Я также пробовал следующую модификацию:
^(220(250)?)?((334(235)?){2})?(250(354(250(221)?)?)?){0,}$
Этот принимает не аутентифицированные ответы, но не соответствует 220250334
и ошибочно соответствует 220250334334235250354250221
( 220250334334235250354250221
не менее 2 250
до кода ответа 354
).
Может ли кто-нибудь помочь мне с этим? Заранее спасибо.
Пример того, что я пытаюсь сделать:
$smtp = fsockopen('mail.example.com', 25); $result = null; $commands = array('HELO', 'AUTH LOGIN', 'user', 'pass', 'MAIL FROM', 'RCPT TO', 'RCPT TO', 'DATA', "\r\n.", 'QUIT'); foreach ($commands as $command) { $result .= substr(fgets($smtp), 0, 3); if (preg_match('~^(220(250)?)?((334){1,2}(235)?)?(250(354(250(221)?)?)?){0,}$~S', $result) > 0) { fwrite($smtp, $command . "\r\n"); } else { fwrite($smtp, "QUIT\r\n"); fclose($smtp); break; } }
Который должен действовать в качестве замены для следующего процедурного кода:
$smtp = fsockopen('mail.example.com', 25); $result = substr(fgets($smtp), 0, 3); // 220 if ($result == '220') { fwrite($smtp, 'HELO' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 220 if ($result == '250') { fwrite($smtp, 'AUTH LOGIN' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 334 if ($result == '334') { fwrite($smtp, 'user' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 334 if ($result == '334') { fwrite($smtp, 'pass' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 235 if ($result == '235') { fwrite($smtp, 'MAIL FROM' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 250 if ($result == '250') { foreach ($to as $mail) { fwrite($smtp, 'RCPT TO' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 250 if ($result != '250') { fwrite($smtp, 'QUIT' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 221 fclose($smtp); break; } } if ($result == '250') { fwrite($smtp, 'DATA' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 354 if ($result == '354') { fwrite($smtp, "\r\n.\r\n"); $result = substr(fgets($smtp), 0, 3); // 250 if ($result == '250') { fwrite($smtp, 'QUIT' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 221 fclose($smtp); if ($result == '221') { echo 'SUCESS!'; } } else { fwrite($smtp, 'QUIT' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 221 fclose($smtp); } } else { fwrite($smtp, 'QUIT' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 221 fclose($smtp); } } } else { fwrite($smtp, 'QUIT' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 221 fclose($smtp); } } else { fwrite($smtp, 'QUIT' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 221 fclose($smtp); } } else { fwrite($smtp, 'QUIT' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 221 fclose($smtp); } } else { fwrite($smtp, 'QUIT' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 221 fclose($smtp); } } else { fwrite($smtp, 'QUIT' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 221 fclose($smtp); } } else { fwrite($smtp, 'QUIT' . "\r\n"); $result = substr(fgets($smtp), 0, 3); // 221 fclose($smtp); }
Я полагаю, вы строите строку со всеми кодами ответов, которые вы получаете, удаляя остальную часть сообщения?
Вероятно, это не тот ответ, который вам нужен, но я не могу не понять, что регулярное выражение просто не подходит для этого. Регулярные выражения хороши при анализе текста в токенах или извлечении интересных подстрок из большей строки. Но у вас уже есть маркеры (коды ответов SMTP), и вы пытаетесь обеспечить их прибытие в ожидаемом порядке. Я просто добавляю коды ответов в очередь и после каждого добавления проверяю, соответствует ли начало очереди одному из ожидаемого шаблона для состояния, в котором вы находитесь. Если это так, удалите эту часть из очереди и перейдите к следующее состояние. Есть только несколько состояний, поэтому я просто пишу код, специфичный для них, вместо того, чтобы пытаться абстрагировать его на какой-то конечный автомат.
Если вы перейдете по пути Regex, вы можете захотеть сохранить место в строке как разделители – это не только упростит соответствие кодов, но и будет легче читать программу.
Редактировать : Спасибо, что разместили код. Это в значительной степени то, что я предполагал. Вы в основном пытаетесь создать абстрактное решение этой проблемы, так что у вас есть возможность отправить заданный массив команд и ожидать возврата заданного шаблона ответов. Вам действительно не нужно делать его абстрактным – добавленная сложность огромна и вряд ли окупится при повторном использовании. Просто напишите код, который говорит: отправьте X, если вы получите Y continue, иначе QUIT . Это будет намного проще и понятнее.
Удивительно, как регулярные выражения становятся намного легче после хорошей ночи сна, вот оно:
(?>220(?>250(?>(?>334){1,2}(?>235)?)?(?>(?>250){1,}(?>354(?>250(?>221)?)?)?)?)?)?
Что может быть упрощено:
^220(?>250(?>(?>334){1,2}(?>235)?)?(?>(?>250){1,}(?>354(?>250)?)?)?)?$
Поскольку первый код ответа (220) не является необязательным, и мы всегда будем отправлять последнюю команду QUIT
.