Search code examples
erlangejabberd

How to extract nested ejabberd message element from MucSub event in Erlang


I want to find message element in ejabberd packet. The packet itself is a message element but sometimes (delayed messages or other cases) the actual message is nested inside the packet:

Normal messages:

<message from="[email protected]"
         to="[email protected]"
         type="groupchat">
  <body>Test</body>
</message>

Other structures example:

<message from="[email protected]"
         to="[email protected]/pda">
  <event xmlns="http://jabber.org/protocol/pubsub#event">
    <items node="urn:xmpp:mucsub:nodes:messages">
      <item id="18277869892147515942">
        <message from="[email protected]/secondwitch"
                 to="[email protected]/pda"
                 type="groupchat"
                 xmlns="jabber:client">
          <archived xmlns="urn:xmpp:mam:tmp"
                    by="muc.shakespeare.example"
                    id="1467896732929849" />
          <stanza-id xmlns="urn:xmpp:sid:0"
                     by="muc.shakespeare.example"
                     id="1467896732929849" />
          <body>Hello from the MUC room !</body>
        </message>
      </item>
    </items>
  </event>
</message>

Which in second example I would like to find the inner message element. The second case structure is not always the same. So I need to traverse trough the packet and try to find any sub element with name message. It cannot be two message subelements so if I found the first I don't need to continue anymore. If there were no sub elements with name message I would like to return the original packet.

This is the code I have till now:

get_message(Packet) ->
    Els = xmpp:get_els(Packet),

    Found =
        case Els of
            [] ->
                <<>>;
            _ ->
                El = find_file(Els, fun(El) ->
                ElementName = io_lib:format("~s",[xmpp:get_name(El)]),
                string:equal(ElementName,"message") end, <<>>),

                Fe = 
                    case El of
                        <<>> -> 
                            Elements = xmpp:get_els(El),
                            lists:foreach(fun(Element) ->
                                FoundElement = get_message(Element),
                                case FoundElement of
                                    <<>> ->
                                        ok;
                                    _ -> 
                                        % stop foreach and return FoundElement
                                        FoundElement
                                end
                            end, Elements);
                        _ ->
                            El
                    end,
                Fe
        end,
    Found.


    find_file(L, Condition, Default) ->
      case lists:dropwhile(fun(E) -> not Condition(E) end, L) of
        [] -> Default;
        [F | _] -> F
      end.

Solution

  • Turned out I don't need to do all these calculations. these is a method called unwrap_mucsub_message which do exactly what I needed.

    get_message(Packet) ->
        case misc:unwrap_mucsub_message(Packet) of
            #message{} = Msg ->
                Msg;
            _ ->
                Packet
        end.