1

我正在编写一个可以交互验证 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至少需要 2 个)。250354

有人可以帮我解决这个问题吗?提前致谢。


我正在尝试做的一个例子:

$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);
}
4

2 回答 2

4

我想你正在用你收到的所有响应代码构建一个字符串,去掉其余的消息?

这可能不是您想要的答案,但我不禁觉得正则表达式在这里不是正确的工具。正则表达式擅长将文本解析为标记或从较大的字符串中提取有趣的子字符串。但是您已经有了令牌(SMTP 响应代码),并且您正试图确保它们以预期的顺序到达。我只需将响应代码添加到队列中,并在每次添加后检查队列的开头是否与您所处状态的预期模式之一匹配。如果匹配,请从队列中删除该部分并转到下一个状态。只有几个状态,所以我只是编写特定于这些状态的代码,而不是尝试将其抽象为某种状态机。

如果您确实采用 Regex 方式,您可能希望在字符串中保留空格作为分隔符 - 它不仅使匹配代码更容易,而且也更容易阅读程序。

编辑:感谢您发布代码。这几乎是我的假设。您基本上是在尝试为这个问题创建一个抽象的解决方案,因此您能够发送给定的命令数组并期望返回给定的响应模式。你真的不需要把它抽象化——增加的复杂性是巨大的,而且不太可能在重用中得到回报。只需编写以下代码:发送 X,如果收到 Y,则继续,否则 QUIT。它将变得更容易和更具可读性。

于 2010-05-27T01:58:49.127 回答
1

令人惊讶的是,在睡了一夜之后,正则表达式变得如此简单,这里是:

(?>220(?>250(?>(?>334){1,2}(?>235)?)?(?>(?>250){1,}(?>354(?>250(?>221)?)?)?)?)?)?

可以简化为:

^220(?>250(?>(?>334){1,2}(?>235)?)?(?>(?>250){1,}(?>354(?>250)?)?)?)?$

由于第一个响应代码 (220) 不是可选的,我们将始终发送最后一个QUIT命令。

于 2010-05-27T16:52:10.153 回答