【问题标题】:Regex to validate SMTP Responses正则表达式验证 SMTP 响应
【发布时间】:2011-02-24 11:16:38
【问题描述】:

我正在编写一个可以交互验证 SMTP 响应代码的正则表达式,一旦 SMTP dialog is completed 它应该通过以下正则表达式(添加一些括号以提高可读性):

^(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,并且错误匹配220250334334235250354250221354 响应代码之前至少需要 2 个250)。

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


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

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

【问题讨论】:

    标签: php regex validation smtp interactive


    【解决方案1】:

    我假设您正在构建一个包含您收到的所有响应代码的字符串,并删除消息的其余部分?

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

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

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

    【讨论】:

    • 我已经用一个例子更新了我的答案。将响应代码添加到队列并检查它们是否匹配正是我想要做的,添加更多特定于状态的正则表达式会使代码更长、更复杂,恕我直言。正则表达式在匹配结构化数据方面非常出色,并且 SMTP 对话框遵循您在前两个正则表达式中看到的非常清晰的结构,这种方法对我来说似乎比一堆 if 和 @987654323 更优雅@的。
    • 我添加了您对我的问题提出的方法的程序实现,但是,我认为它并不比正则表达式方法更容易或更易读。
    • 当然,当您复制/粘贴相同的内容时,它的可读性并不高。您仍然需要考虑如何构建它。
    【解决方案2】:

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

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

    可以简化为:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-19
      • 2021-02-24
      • 2014-02-25
      • 2019-06-21
      相关资源
      最近更新 更多