【问题标题】:Filtering message packet's body in ejabberd在 ejabberd 中过滤消息包的正文
【发布时间】:2016-07-17 00:21:44
【问题描述】:

我正在尝试过滤 ejabberd 中不需要的消息。我从this 帖子中获得了一些指导。这是通过 filter_packet 钩子执行的函数 sn-p:

check_stanza({_From, _To, #xmlel{name = StanzaType}} = Input) ->
    AccessRule = case StanzaType of
             <<"message">> ->
           ?DEBUG("filtering packet...~nFrom: ~p~nTo: ~p~nPacket: ~p~nResult: ",
             [_From, _To, Input]),
           Input
           %check_stanza_type(AccessRule, Input)
         end.

日志中打印的数据包:

{{jid,<<"test25">>,<<"localhost">>,<<"Administrators-MacBook-Pro-6">>,
<<"test25">>,<<"localhost">>,<<"Administrators-MacBook-Pro-6">>},{jid,
<<"test24">>,<<"localhost">>,<<"Administrators-MacBook-Pro-6">>,
<<"test24">>,<<"localhost">>,<<"Administrators-MacBook-Pro-6">>},{xmlel,
<<"message">>,[{<<"type">>,<<"chat">>},{<<"id">>,<<"purpleaed2ec77">>},
{<<"to">>,<<"test24@localhost/Administrators-MacBook-Pro-6">>}],[{xmlel,
<<"active">>,[{<<"xmlns">>,<<"http://jabber.org/protocol/chatstates">>}],
[]},{xmlel,<<"body">>,[],[{xmlcdata,<<"MESSAGE BODY GOES HERE">>}]}]}}

我的要求:提取邮件正文并过滤掉辱骂性词语。例如,如果用户正在发送“消息正文到这里”,则应该发生以下序列:

  • 发送方的数据包被模块拦截,通过钩子(完成)
  • 正文的消息被提取出来,单词通过一组数据进行过滤。数据可以在 Mnesia 或 MySQL 中(待定)
  • 改变的数据包(过滤的主体)被允许通过接收客户端

如果“here”是一个不受欢迎的词,接收者将收到“message body goes ****”。

我是 Erlang 的新手,它是一个小型社区,那里很少有好文章,所以需要一些关于实现上述目标的最佳方法的建议。有一个很棒的 post 关于如何使用 elixir 支持做同样的事情,但我想坚持使用 Erlang。任何帮助将不胜感激。

更新

感谢 Amiramix。这是替换特定单词的代码:

{xmlel,Syntax,Type,OuterBody} = Xmlel.   


case Syntax ->
    "<<message>>" ,
        XmlelBody = lists:keyfind(<<"body">>, 2, OuterBody),  %{xmlel,<<"body">>,[],[{xmlcdata,<<"HI">>}]}
        {xmlel,BodySyntax,_,Innerbody} = XmlelBody,      % [{xmlcdata,<<"HI">>}]    
        Body = proplists:get_value(xmlcdata, Innerbody),   %<<"HI">>


        TmpList = re:replace(Body,<<"HI$">>,<<"**">>),
        NewBody = binary:list_to_bin(TmpList),      %<<"**">>
        NewInnerBody = lists:keyreplace(xmlcdata, 1, Innerbody, {xmlcdata, NewBody}).   %[{xmlcdata,<<"**">>}]
        NewXmlelBody = setelement(4,XmlelBody,NewInnerBody),   %{xmlel,<<"body">>,[],[{xmlcdata,<<"**">>}]}


        NewOuterBody  = lists:keyreplace(<<"body">>, 2, OuterBody, NewXmlelBody),
        NewXmlel = setelement(4, Xmlel, NewOuterBody)

由于很难对正文中的每个单词进行多次阻塞的单词的迭代,因此我想将提取的正文发送到为我执行此操作的 python 脚本。关于如何从 > 中提取 MESSAGE BODY GOES HERE 的任何建议?

【问题讨论】:

    标签: erlang ejabberd


    【解决方案1】:

    日志与代码不匹配,即输出中没有“过滤数据包...”,所以我不能给你一个确切的代码来输入check_stanza 函数。而且我不太了解ejabberd 来验证。不过,我想为您提供一些指导,帮助您在 Erlang 中处理此类结构,以便您更轻松地做自己想做的事情。

    首先重新格式化结构,以便清楚数据是如何嵌套的:

    {
      {jid,
       <<"test25">>,
       <<"localhost">>,
       <<"Administrators-MacBook-Pro-6">>,
       <<"test25">>,
       <<"localhost">>,
       <<"Administrators-MacBook-Pro-6">>
      },
      {jid,
       <<"test24">>,
       <<"localhost">>,
       <<"Administrators-MacBook-Pro-6">>,
       <<"test24">>,
       <<"localhost">>,<<"Administrators-MacBook-Pro-6">>
      },
      {xmlel, <<"message">>,
       [
        {<<"type">>, <<"chat">>},
        {<<"id">>, <<"purpleaed2ec77">>},
        {<<"to">>, <<"test24@localhost/Administrators-MacBook-Pro-6">>}
       ],
       [
        {xmlel, <<"active">>,
         [{<<"xmlns">>, <<"http://jabber.org/protocol/chatstates">>}], []
        },
        {xmlel, <<"body">>, [],
         [{xmlcdata, <<"MESSAGE BODY GOES HERE">>}]
        }
       ]
      }
    }.
    

    你有一个外部元组,里面有三个元组:

    { {jid, ...}, {jid, ...}, {xmlel, ...} }.
    

    外部数据是一个元组看起来不太正确,我希望它是一个列表,例如:

    [ {jid, ...}, {jid, ...}, {xmlel, ...} ].
    

    但也许就是这样,但请确保您正确记录它。

    要修改正文,您需要执行以下步骤:

    1. 提取包含正文的xmlcdata
    2. 修改正文
    3. 将其存储回主结构中

    在继续之前,请将整个结构复制到 Erlang shell 中并将其存储为变量,以便您可以在自己的 shell 中进行操作。不要忘记在开头添加变量名,在末尾添加'.'

    Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
    
    Eshell V7.2.1  (abort with ^G)
    1> M =
    1> {
    1>   {jid,
    1>    <<"test25">>,
    1>    <<"localhost">>,
    1>    <<"Administrators-MacBook-Pro-6">>,
    1>    <<"test25">>,
    1>    <<"localhost">>,
    1>    <<"Administrators-MacBook-Pro-6">>
    1>   },
    1>   {jid,
    1>    <<"test24">>,
    1>    <<"localhost">>,
    1>    <<"Administrators-MacBook-Pro-6">>,
    1>    <<"test24">>,
    1>    <<"localhost">>,<<"Administrators-MacBook-Pro-6">>
    1>   },
    1>   {xmlel, <<"message">>,
    1>    [
    1>     {<<"type">>, <<"chat">>},
    1>     {<<"id">>, <<"purpleaed2ec77">>},
    1>     {<<"to">>, <<"test24@localhost/Administrators-MacBook-Pro-6">>}
    1>    ],
    1>    [
    1>     {xmlel, <<"active">>,
    1>      [{<<"xmlns">>, <<"http://jabber.org/protocol/chatstates">>}], []
    1>     },
    1>     {xmlel, <<"body">>, [],
    1>      [{xmlcdata, <<"MESSAGE BODY GOES HERE">>}]
    1>     }
    1>    ]
    1>   }
    1> }.
    {{jid,<<"test25">>,<<"localhost">>,
          <<"Administrators-MacBook-Pro-6">>,<<"test25">>,
          <<"localhost">>,<<"Administrators-MacBook-Pro-6">>},
     {jid,<<"test24">>,<<"localhost">>,
          <<"Administrators-MacBook-Pro-6">>,<<"test24">>,
          <<"localhost">>,<<"Administrators-MacBook-Pro-6">>},
     {xmlel,<<"message">>,
            [{<<"type">>,<<"chat">>},
             {<<"id">>,<<"purpleaed2ec77">>},
             {<<"to">>,
              <<"test24@localhost/Administrators-MacBook-Pro-6">>}],
            [{xmlel,<<"active">>,
                    [{<<"xmlns">>,<<"http://jabber.org/protocol/chatstates">>}],
                    []},
             {xmlel,<<"body">>,[],
                    [{xmlcdata,<<"MESSAGE BODY GOES HERE">>}]}]}}
    2>
    

    现在只需在 shell 中输入 M. 即可打印出整个结构(为简洁起见):

    2> M.
    {{jid,<<"test25">>,<<"localhost">>,
          <<"Administrators-MacBook-Pro-6">>,<<"test25">>,
    (...)
             {xmlel,<<"body">>,[],
                    [{xmlcdata,<<"MESSAGE BODY GOES HERE">>}]}]}}
    

    如果数据确实是一个元组,您可以使用以下代码获取最后一个子元组:

    3> {_, _, Xmlel} = M.
    

    同样,在 shell 中输入 Xmlel. 将打印出该变量的内容('_' 表示 不关心anonymous variable)。现在提取最后一个列表,xmlel 本身就是一个元组:

    4> {xmlel, _, _, L} = Xmlel.
    

    &lt;&lt;"message"&gt;&gt; 与第一个 '_' 匹配,然后第一个列表与第二个 '_' 匹配。然后将第二个列表绑定到L

    6> L.
    [{xmlel,<<"active">>,
            [{<<"xmlns">>,<<"http://jabber.org/protocol/chatstates">>}],
            []},
     {xmlel,<<"body">>,[],
            [{xmlcdata,<<"MESSAGE BODY GOES HERE">>}]}]
    

    您想要包含 &lt;&lt;"body"&gt;&gt; 值的元组,例如:

    7> T = lists:keyfind(<<"body">>, 2, L).
    {xmlel,<<"body">>,[], [{xmlcdata,<<"MESSAGE BODY GOES HERE">>}]}
    

    请查看lists:keyfind/3 文档以获取有关该函数参数的信息。如果您需要解释这些功能的作用,请查看Erlang documentation for particular modules

    最后,我们想要包含 body 元素的列表:

    8> {xmlel, _, _, BL} = T.
    

    绑定的BLproplist,简单的获取body:

    16> Body = proplists:get_value(xmlcdata, BL).
    <<"MESSAGE BODY GOES HERE">>
    

    让我们替换字符串并重建结构:

    21> TmpList = re:replace(Body, <<"HERE$">>, <<"*****">>).
    [<<"MESSAGE BODY GOES ">>,<<"*****">>]
    
    23> binary:list_to_bin(TmpList).
    <<"MESSAGE BODY GOES *****">>
    
    24> NewBody = binary:list_to_bin(TmpList).
    <<"MESSAGE BODY GOES *****">>
    

    现在新的主体是NewBody 变量。我们将列表中的元组替换为lists:keyreplace/4

    28> NewBL = lists:keyreplace(xmlcdata, 1, BL, {xmlcdata, NewBody}).
    [{xmlcdata,<<"MESSAGE BODY GOES *****">>}]
    

    我们用setelement/3替换元组中的元素:

    31> NewT = setelement(4, T, NewBL).
    {xmlel,<<"body">>,[], [{xmlcdata,<<"MESSAGE BODY GOES *****">>}]}
    

    公平地说,元组 {xmlel, &lt;&lt;"body"&gt;&gt;, [], List} 可能是一个 Erlang record xmlel,如果您知道该记录的定义,您可以用语义上更正确的方式替换它,例如:

    32> NewT = T#xmlel{body = NewBody}
    

    如果这确实是一条记录,那么它的定义必须在一个 Erlang .hrl 文件中,该文件位于 ejabberd 代码的某个位置,您可以将其包含在代码中并使用。如果该记录的定义发生更改,您只能重新编译您的代码,它应该仍然可以工作。使用setelement 时,如果元组的大小发生变化,代码可能会停止工作,因此请记住这一点。我将继续使用setelement,因为此时这对我来说更简单(记录定义需要使用rr 导入到shell 中才能使用)。

    现在剩下三个操作:替换主列表L中的&lt;&lt;"body"&gt;&gt;元组,然后替换&lt;&lt;"message"&gt;&gt;元组中的L,最后替换主结构中的那个元组:

    35> NewL = lists:keyreplace(<<"body">>, 2, L, NewT).
    [{xmlel,<<"active">>,
            [{<<"xmlns">>,<<"http://jabber.org/protocol/chatstates">>}],
            []},
     {xmlel,<<"body">>,[],
            [{xmlcdata,<<"MESSAGE BODY GOES *****">>}]}]
    
    41> NewXmlel = setelement(4, Xmlel, NewL).
    {xmlel,<<"message">>,
           [{<<"type">>,<<"chat">>},
            {<<"id">>,<<"purpleaed2ec77">>},
            {<<"to">>,
             <<"test24@localhost/Administrators-MacBook-Pro-6">>}],
           [{xmlel,<<"active">>,
                   [{<<"xmlns">>,<<"http://jabber.org/protocol/chatstates">>}],
                   []},
            {xmlel,<<"body">>,[],
                   [{xmlcdata,<<"MESSAGE BODY GOES *****">>}]}]}
    
    42> NewM = setelement(3, M, NewXmlel).
    {{jid,<<"test25">>,<<"localhost">>,
          <<"Administrators-MacBook-Pro-6">>,<<"test25">>,
          <<"localhost">>,<<"Administrators-MacBook-Pro-6">>},
     {jid,<<"test24">>,<<"localhost">>,
          <<"Administrators-MacBook-Pro-6">>,<<"test24">>,
          <<"localhost">>,<<"Administrators-MacBook-Pro-6">>},
     {xmlel,<<"message">>,
            [{<<"type">>,<<"chat">>},
             {<<"id">>,<<"purpleaed2ec77">>},
             {<<"to">>,
              <<"test24@localhost/Administrators-MacBook-Pro-6">>}],
            [{xmlel,<<"active">>,
                    [{<<"xmlns">>,<<"http://jabber.org/protocol/chatstates">>}],
                    []},
             {xmlel,<<"body">>,[],
                    [{xmlcdata,<<"MESSAGE BODY GOES *****">>}]}]}}
    

    现在NewM 包含与M 相同的消息,但根据需要替换了正文。

    这相当长,因为为了清楚起见,我分别对每个步骤进行了编码。实际上,在您的代码中使用它时,您将能够缩短这些步骤,特别是如果您可以包含和使用适当的记录定义。

    【讨论】:

    • 感谢 Amiramix 的详细解释,这很有帮助。我将在 shell 中运行此代码。我正在尝试 xml:get_subtag_cdata(XML, "body") ,其中 XML 是我们示例中的外部元组,但结果始终为空。是因为元组不是 xml 吗?是否有一个模块可以从元组中以类似的方式提供主体。我可以迭代身体,但正如你所说,总是有改变结构的风险
    • 试试xml:get_subtag_cdata(XML, &lt;&lt;"body"&gt;&gt;).
    • 是的!有效。我得到了 > 不过,我需要的是 HI。有什么建议吗?
    • binary_to_list(&lt;&lt;"HI"&gt;&gt;). :-)
    猜你喜欢
    • 2010-12-28
    • 2011-08-26
    • 2013-09-16
    • 2015-08-07
    • 2016-01-08
    • 2015-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多