首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用正则表达式从SIP消息中通过标头分支令牌提取

使用正则表达式从SIP消息中通过标头分支令牌提取
EN

Stack Overflow用户
提问于 2013-03-11 17:45:41
回答 5查看 1.3K关注 0票数 1

我正在尝试从SIP消息的Via:头中提取branch=z9hG4bKlmrltg10b801lgkf0681.1。下面是我尝试过的PHP代码:

代码语言:javascript
运行
复制
preg_match('/.branch=.* + From:/', $msg, $result)

下面是$msg的值

代码语言:javascript
运行
复制
"INVITE sip:3310094@mediastream.voip.cabletel.net:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.50.240:5060;branch=z9hG4bKlmrltg10b801lgkf0681.1
From: DEATON JEANETTE<sip:9123840782@mediastream.voip.cabletel.net:5060>;tag=SDg7j0c01-959bf958-d8f0f4ea-13c4-50029-140b-4d106390-140b"

我如何纠正我的正则表达式才能使其正常工作?

EN

回答 5

Stack Overflow用户

发布于 2013-03-12 01:18:19

请正确解析您的SIP消息。我发现你不太可能只想要分行ID,你几乎肯定想要关于交易的其他信息,而不是伪呼叫ID。SIP消息遵循几个其他协议(包括HTTP ;-)使用的标准化消息格式,并且有几个库设计用于解析这种消息格式。

为了演示这是多么的简单和强大,让我们首先看一下我前段时间编写的RFC822消息解析器类(尽管它们最近进行了改进和更新)。这些可以用来解析电子邮件,我也有一些简单的HTTP消息解析器类,它们是从以下扩展而来的:

代码语言:javascript
运行
复制
<?php

/**
 * Class representing the basic RFC822 message format
 *
 * @author  Chris Wright
 * @version 1.1
 */
class RFC822Message
{
    /**
     * @var array Collection of headers from the message
     */
    protected $headers = array();

    /**
     * @var string The message body
     */
    protected $body;

    /**
     * Constructor
     *
     * @param array  $headers Collection of headers from the message
     * @param string $body    The message body
     */
    public function __construct($headers, $body)
    {
        $this->headers = $headers;
        $this->body    = $body;
    }

    /**
     * Get the value of a header from the message
     *
     * @param string $name The name of the header
     *
     * @return array The value(s) of the header from the request
     */
    public function getHeader($name)
    {
        $name = strtolower(trim($name));

        return isset($this->headers[$name]) ? $this->headers[$name] : null;
    }

    /**
     * Get the message body
     *
     * @return string The message body
     */
    public function getBody()
    {
        return $this->body;
    }
}

/**
 * Factory which makes RFC822 message objects
 *
 * @author  Chris Wright
 * @version 1.1
 */
class RFC822MessageFactory
{
    /**
     * Create a new RFC822 message object
     *
     * @param array  $headers The request headers
     * @param string $body    The request body
     */
    public function create($headers, $body)
    {
        return new RFC822Message($headers, $body);
    }
}


/**
 * Parser which creates RFC822 message objects from strings
 *
 * @author  Chris Wright
 * @version 1.2
 */
class RFC822MessageParser
{
    /**
     * @var RFC822MessageFactory Factory which makes RFC822 message objects
     */
    protected $messageFactory;

    /**
     * Constructor
     *
     * @param RFC822MessageFactory $messageFactory Factory which makes RFC822 message objects
     */
    public function __construct(RFC822MessageFactory $messageFactory)
    {
        $this->messageFactory  = $messageFactory;
    }

    /**
     * Split a message into head and body sections
     *
     * @param string $message The message string
     *
     * @return array Head at index 0, body at index 1
     */
    protected function splitHeadFromBody($message)
    {
        $parts = preg_split('/\r?\n\r?\n/', ltrim($message), 2);

        return array(
            $parts[0],
            isset($parts[1]) ? $parts[1] : null
        );
    }

    /**
     * Parse the header section into a normalized array
     *
     * @param string $head The message head section
     *
     * @return array The parsed headers
     */
    protected function parseHeaders($head)
    {
        $expr =
        '!
          ^
          ([^()<>@,;:\\"/[\]?={} \t]+)          # Header name
          [ \t]*:[ \t]*
          (
            (?:
              (?:                               # First line of value
                (?:"(?:[^"\\\\]|\\\\.)*"|\S+)   # Quoted string or unquoted token
                [ \t]*                          # LWS
              )*
              (?:                               # Folded lines
                \r?\n
                [ \t]+                          # ...must begin with LWS
                (?:
                  (?:"(?:[^"\\\\]|\\\\.)*"|\S+) # ...followed by quoted string or unquoted tokens
                  [ \t]*                        # ...and maybe some more LWS
                )*
              )*
            )?
          )
          \r?$
        !smx';
        preg_match_all($expr, $head, $matches);

        $headers = array();
        for ($i = 0; isset($matches[0][$i]); $i++) {
            $name = strtolower($matches[1][$i]);
            if (!isset($headers[$name])) {
                $headers[$name] = array();
            }

            $value = preg_replace('/\s+("(?:[^"\\\\]|\\\\.)*"|\S+)/s', ' $1', $matches[2][$i]);

            $headers[$name][] = $value;
        }

        return $headers;
    }

    /**
     * Create a message object from a string
     *
     * @param string $message The message string
     *
     * @return RFC822Message The parsed message object
     */
    public function parseMessage($message)
    {
        list($head, $body) = $this->splitHeadFromBody($message);
        $headers = $this->parseHeaders($head);

        return $this->requestFactory->create($headers, $body);
    }
}

没有什么特别可怕的,如果你忽略了可怕的正则表达式来解析标题:-P -认真的说,这些类可以不加修改地用于解析电子邮件的标题部分,这是RFC822格式消息的基础。

SIP在HTTP上建模,因此只需对HTTP消息解析类进行几个相当简单的修改,我们就可以很容易地使它们适应SIP。让我们来看看这些类--在这些类中,我(或多或少)搜索了HTTP,并将其替换为SIP

代码语言:javascript
运行
复制
<?php

/**
 * Abstract class representing a SIP message
 *
 * @author  Chris Wright
 * @version 1.0
 */
abstract class SIPMessage extends RFC822Message
{
    /**
     * @var string The message protocol version
     */
    protected $version;

    /**
     * Constructor
     *
     * @param array  $headers Collection of headers from the message
     * @param string $body    The message body
     * @param string $version The message protocol version
     */
    public function __construct($headers, $body, $version)
    {
        parent::__construct($headers, $body);
        $this->version = $version;
    }

    /**
     * Get the message protocol version
     *
     * @return string The message protocol version
     */
    public function getVersion()
    {
        return $this->version;
    }
}

/**
 * Class representing a SIP request message
 *
 * @author  Chris Wright
 * @version 1.0
 */
class SIPRequest extends SIPMessage
{
    /**
     * @var string The request method
     */
    private $method;

    /**
     * @var string The request URI
     */
    private $uri;

    /**
     * Constructor
     *
     * @param array  $headers The request headers
     * @param string $body    The request body
     * @param string $version The request protocol version
     * @param string $method  The request method
     * @param string $uri     The request URI
     */
    public function __construct($headers, $body, $version, $method, $uri)
    {
        parent::__construct($headers, $body, $version);
        $this->method  = $method;
        $this->uri     = $uri;
    }

    /**
     * Get the request method
     *
     * @return string The request method
     */
    public function getMethod()
    {
        return $this->method;
    }

    /**
     * Get the request URI
     *
     * @return string The request URI
     */
    public function getURI()
    {
        return $this->uri;
    }
}

/**
 * Class representing a SIP response message
 *
 * @author  Chris Wright
 * @version 1.0
 */
class SIPResponse extends SIPMessage
{
    /**
     * @var int The response code
     */
    private $code;

    /**
     * @var string The response message
     */
    private $message;

    /**
     * Constructor
     *
     * @param array  $headers The request headers
     * @param string $body    The request body
     * @param string $version The request protocol version
     * @param int    $code    The response code
     * @param string $message The response message
     */
    public function __construct($headers, $body, $version, $code, $message)
    {
        parent::__construct($headers, $body, $version);
        $this->code    = $code;
        $this->message = $message;
    }

    /**
     * Get the response code
     *
     * @return int The response code
     */
    public function getCode()
    {
        return $this->code;
    }

    /**
     * Get the response message
     *
     * @return string The response message
     */
    public function getMessage()
    {
        return $this->message;
    }
}

/**
 * Factory which makes SIP request objects
 *
 * @author  Chris Wright
 * @version 1.0
 */
class SIPRequestFactory extends RFC822MessageFactory
{
    /**
     * Create a new SIP request object
     *
     * The last 3 arguments of this method are only optional  to prevent PHP from triggering
     * an E_STRICT at compile time. IMO this particular error is itself an error on the part
     * of the PHP designers,  and I don't feel bad  about about this workaround,  even if it
     * does mean the signature is technically wrong. It is the lesser of two evils.
     *
     * @param array  $headers The request headers
     * @param string $body    The request body
     * @param string $version The request protocol version
     * @param string $method  The request method
     * @param string $uri     The request URI
     */
    public function create($headers, $body, $version = null, $method = null, $uri = null)
    {
        return new SIPRequest($headers, $body, $version, $method, $uri);
    }
}

/**
 * Factory which makes SIP response objects
 *
 * @author  Chris Wright
 * @version 1.0
 */
class SIPResponseFactory extends RFC822MessageFactory
{
    /**
     * Create a new SIP response object
     *
     * The last 3 arguments of this method are only optional  to prevent PHP from triggering
     * an E_STRICT at compile time. IMO this particular error is itself an error on the part
     * of the PHP designers,  and I don't feel bad  about about this workaround,  even if it
     * does mean the signature is technically wrong. It is the lesser of two evils.
     *
     * @param array  $headers The response headers
     * @param string $body    The response body
     * @param string $version The response protocol version
     * @param int    $code    The response code
     * @param string $message The response message
     */
    public function create($headers, $body, $version = null, $code = null, $message = null)
    {
        return new SIPResponse($headers, $body, $version, $code, $message);
    }
}

/**
 * Parser which creates SIP message objects from strings
 *
 * @author  Chris Wright
 * @version 1.0
 */
class SIPMessageParser extends RFC822MessageParser
{
    /**
     * @var SIPRequestFactory Factory which makes SIP request objects
     */
    private $requestFactory;

    /**
     * @var SIPResponseFactory Factory which makes SIP response objects
     */
    private $responseFactory;

    /**
     * Constructor
     *
     * @param SIPRequestFactory  $requestFactory  Factory which makes SIP request objects
     * @param SIPResponseFactory $responseFactory Factory which makes SIP response objects
     */
    public function __construct(SIPRequestFactory $requestFactory, SIPResponseFactory $responseFactory)
    {
        $this->requestFactory  = $requestFactory;
        $this->responseFactory = $responseFactory;
    }

    /**
     * Remove the request line from the message and parse into tokens
     *
     * @param string $head The message head section
     *
     * @return array The parsed request line at index 0, the remainder of the message at index 1
     *
     * @throws \DomainException When the request line of the message is invalid
     */
    private function removeAndParseRequestLine($head)
    {
        // Note: this method  forgives a couple of minor standards violations, mostly for benefit
        // of some older  Polycom phones and for Voispeed,  who seem to make  stuff up as they go
        // along.  It also  treats the  whole line as  case-insensitive  even though  methods are
        // officially case-sensitive,  because having two different casings of the same verb mean
        // different things makes no sense semantically or implementationally.
        // Side note, from RFC3261:
        // > The SIP-Version string is case-insensitive, but implementations MUST send upper-case
        // Wat. Go home Rosenberg, et. al., you're drunk.

        $parts = preg_split('/\r?\n/', $head, 2);

        $expr =
          '@^
            (?:
              ([^\r\n \t]+) [ \t]+ ([^\r\n \t]+) [ \t]+ SIP/(\d+\.\d+) # request
             |
              SIP/(\d+\.\d+) [ \t]+ (\d+) [ \t]+ ([^\r\n]+)            # response
            )
           $@ix';
        if (!preg_match($expr, $parts[0], $match)) {
            throw new \DomainException('Request-Line of the message is invalid');
        }

        if (empty($match[4])) { // request
            $requestLine = array(
                'method'  => strtoupper($match[1]),
                'uri'     => $match[2],
                'version' => $match[3]
            );
        } else { // response
            $requestLine = array(
                'version' => $match[4],
                'code'    => (int) $match[5],
                'message' => $match[6]
            );
        }

        return array(
            $requestLine,
            isset($parts[1]) ? $parts[1] : ''
        );
    }

    /**
     * Create the appropriate message object from a string
     *
     * @param string $message The message string
     *
     * @return SIPRequest|SIPResponse The parsed message object
     *
     * @throws \DomainException When the message string is not valid SIP message
     */
    public function parseMessage($message)
    {
        list($head, $body) = $this->splitHeadFromBody($message);
        list($requestLine, $head) = $this->removeAndParseRequestLine($head);
        $headers = $this->parseHeaders($head);

        if (isset($requestLine['uri'])) {
            return $this->requestFactory->create(
                $headers,
                $body,
                $requestLine['version'],
                $requestLine['method'],
                $requestLine['uri']
            );
        } else {
            return $this->responseFactory->create(
                $headers,
                $body,
                $requestLine['version'],
                $requestLine['code'],
                $requestLine['message']
            );
        }
    }
}

仅仅为了提取一个头值,似乎有很多代码,不是吗?是的,确实如此。但这不仅仅是它的作用。它将整个消息解析成一个数据结构,该结构提供对任意数量的信息的轻松访问,允许(或多或少)标准向您抛出的任何东西。

那么,让我们来看看你将如何实际使用它:

代码语言:javascript
运行
复制
// First we create a parser object
$messageParser = new SIPMessageParser(
  new SIPRequestFactory,
  new SIPResponseFactory
);

// Parse the message into an object
try {
  $message = $messageParser->parseMessage($msg);
} catch (Exception $e) {
  // The message parsing failed, handle the error here
}

// Get the value of the Via: header
$via = $message->getHeader('Via');

// SIP is irritatingly non-specific about the format of branch IDs. This
// expression matches either a quoted string or an unquoted token, which is
// about all that you can say for sure about arbitrary implementations.
$expr = '/branch=(?:"((?:[^"\\\\]|\\\\.)*)"|(.+?)(?:\s|;|$))/i';

// NB: this assumes the message has a single Via: header and a single branch ID.
// In reality this is rarely the case for messages that are received, although
// it is usually the case for messages before they are sent.
if (!preg_match($expr, $via[0], $matches)) {
  // The Via: header does not contain a branch ID, handle this error
}

$branchId = !empty($matches[2]) ? $matches[2] : $matches[1];

var_dump($branchId);

See it working

对于手头的问题,这个答案几乎肯定是夸大其词了。然而,我认为这是解决这个问题的正确方法。

票数 2
EN

Stack Overflow用户

发布于 2013-03-11 17:52:58

尝尝这个

代码语言:javascript
运行
复制
$str = "INVITE sip:3310094@mediastream.voip.cabletel.net:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.50.240:5060;branch=z9hG4bKlmrltg10b801lgkf0681.1
From: DEATON JEANETTE<sip:9123840782@mediastream.voip.cabletel.net:5060>;tag=SDg7j0c01-959bf958-d8f0f4ea-13c4-50029-140b-4d106390-140b";

preg_match('/branch=(.*)From:/i', $str, $output);
print_r( $output );
票数 0
EN

Stack Overflow用户

发布于 2013-03-11 17:57:47

代码语言:javascript
运行
复制
preg_match('/branch=.*/i', $msg, $result);
print_r($result);

将产生类似如下的结果:

代码语言:javascript
运行
复制
Array
(
    [0] => branch=z9hG4bKlmrltg10b801lgkf0681.1
)
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15335389

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档