在 ejabberd 中过滤消息包的正文
Posted
技术标签:
【中文标题】在 ejabberd 中过滤消息包的正文【英文标题】:Filtering message packet's body in ejabberd 【发布时间】:2016-07-17 00:21:44 【问题描述】:我正在尝试过滤 ejabberd 中不需要的消息。我从this 帖子中获得了一些指导。这是通过 filter_packet 钩子执行的函数 sn-p:
check_stanza(_From, _To, #xmlelname = 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 的任何建议?
【问题讨论】:
【参考方案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, ... ].
但也许就是这样,但请确保您正确记录它。
要修改正文,您需要执行以下步骤:
-
提取包含正文的
xmlcdata
修改正文
将其存储回主结构中
在继续之前,请将整个结构复制到 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.
<<"message">>
与第一个 '_'
匹配,然后第一个列表与第二个 '_'
匹配。然后将第二个列表绑定到L
:
6> L.
[xmlel,<<"active">>,
[<<"xmlns">>,<<"http://jabber.org/protocol/chatstates">>],
[],
xmlel,<<"body">>,[],
[xmlcdata,<<"MESSAGE BODY GOES HERE">>]]
您想要包含 <<"body">>
值的元组,例如:
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.
绑定的BL
是proplist,简单的获取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, <<"body">>, [], List
可能是一个 Erlang record xmlel
,如果您知道该记录的定义,您可以用语义上更正确的方式替换它,例如:
32> NewT = T#xmlelbody = NewBody
如果这确实是一条记录,那么它的定义必须在一个 Erlang .hrl
文件中,该文件位于 ejabberd
代码的某个位置,您可以将其包含在代码中并使用。如果该记录的定义发生更改,您只能重新编译您的代码,它应该仍然可以工作。使用setelement
时,如果元组的大小发生变化,代码可能会停止工作,因此请记住这一点。我将继续使用setelement
,因为此时这对我来说更简单(记录定义需要使用rr
导入到shell 中才能使用)。
现在剩下三个操作:替换主列表L
中的<<"body">>
元组,然后替换<<"message">>
元组中的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, <<"body">>).
是的!有效。我得到了 > 不过,我需要的是 HI。有什么建议吗?
binary_to_list(<<"HI">>).
:-)以上是关于在 ejabberd 中过滤消息包的正文的主要内容,如果未能解决你的问题,请参考以下文章