| 1 | -module(mod_syslogbot). |
|---|
| 2 | -author('bombadil@bosqueviejo.net'). |
|---|
| 3 | |
|---|
| 4 | -behaviour(gen_server). |
|---|
| 5 | -behaviour(gen_mod). |
|---|
| 6 | |
|---|
| 7 | %% API |
|---|
| 8 | -export([start_link/2, start/2, stop/1]). |
|---|
| 9 | |
|---|
| 10 | %% gen_server callbacks |
|---|
| 11 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, |
|---|
| 12 | terminate/2, code_change/3]). |
|---|
| 13 | |
|---|
| 14 | -include("ejabberd.hrl"). |
|---|
| 15 | -include("jlib.hrl"). |
|---|
| 16 | |
|---|
| 17 | -record(state, {host, users, id, filters}). |
|---|
| 18 | |
|---|
| 19 | -define(PROCNAME, ejabberd_mod_syslogbot). |
|---|
| 20 | |
|---|
| 21 | %%==================================================================== |
|---|
| 22 | %% API |
|---|
| 23 | %%==================================================================== |
|---|
| 24 | %%-------------------------------------------------------------------- |
|---|
| 25 | %% Function: start_link() -> {ok,Pid} | ignore | {error,Error} |
|---|
| 26 | %% Description: Starts the server |
|---|
| 27 | %%-------------------------------------------------------------------- |
|---|
| 28 | start_link(Host, Opts) -> |
|---|
| 29 | %% Proc = gen_mod:get_module_proc(Host, ?PROCNAME), |
|---|
| 30 | gen_server:start_link({local, ?PROCNAME}, ?MODULE, [Host, Opts], []). |
|---|
| 31 | |
|---|
| 32 | start(Host, Opts) -> |
|---|
| 33 | application:start(inets), |
|---|
| 34 | Proc = gen_mod:get_module_proc(Host, ?PROCNAME), |
|---|
| 35 | ChildSpec = |
|---|
| 36 | {Proc, |
|---|
| 37 | {?MODULE, start_link, [Host, Opts]}, |
|---|
| 38 | temporary, |
|---|
| 39 | 1000, |
|---|
| 40 | worker, |
|---|
| 41 | [?MODULE]}, |
|---|
| 42 | supervisor:start_child(ejabberd_sup, ChildSpec). |
|---|
| 43 | |
|---|
| 44 | stop(Host) -> |
|---|
| 45 | Proc = gen_mod:get_module_proc(Host, ?PROCNAME), |
|---|
| 46 | gen_server:call(Proc, stop), |
|---|
| 47 | supervisor:terminate_child(ejabberd_sup, Proc), |
|---|
| 48 | supervisor:delete_child(ejabberd_sup, Proc). |
|---|
| 49 | |
|---|
| 50 | |
|---|
| 51 | %%==================================================================== |
|---|
| 52 | %% gen_server callbacks |
|---|
| 53 | %%==================================================================== |
|---|
| 54 | |
|---|
| 55 | %%-------------------------------------------------------------------- |
|---|
| 56 | %% Function: init(Args) -> {ok, State} | |
|---|
| 57 | %% {ok, State, Timeout} | |
|---|
| 58 | %% ignore | |
|---|
| 59 | %% {stop, Reason} |
|---|
| 60 | %% Description: Initiates the server |
|---|
| 61 | %%-------------------------------------------------------------------- |
|---|
| 62 | init([Host, Opts]) -> |
|---|
| 63 | MyHost = gen_mod:get_opt_host(Host, Opts, "syslogbot.@HOST@"), |
|---|
| 64 | MyOpts = gen_mod:get_opt(users, Opts), |
|---|
| 65 | ejabberd_router:register_route(MyHost), |
|---|
| 66 | {ok, #state{host = MyHost, users = MyOpts, id = "0001", filters=[]}}. |
|---|
| 67 | |
|---|
| 68 | %%-------------------------------------------------------------------- |
|---|
| 69 | %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | |
|---|
| 70 | %% {reply, Reply, State, Timeout} | |
|---|
| 71 | %% {noreply, State} | |
|---|
| 72 | %% {noreply, State, Timeout} | |
|---|
| 73 | %% {stop, Reason, Reply, State} | |
|---|
| 74 | %% {stop, Reason, State} |
|---|
| 75 | %% Description: Handling call messages |
|---|
| 76 | %%-------------------------------------------------------------------- |
|---|
| 77 | handle_call(stop, _From, State) -> |
|---|
| 78 | {stop, normal, ok, State}. |
|---|
| 79 | |
|---|
| 80 | %%-------------------------------------------------------------------- |
|---|
| 81 | %% Function: handle_cast(Msg, State) -> {noreply, State} | |
|---|
| 82 | %% {noreply, State, Timeout} | |
|---|
| 83 | %% {stop, Reason, State} |
|---|
| 84 | %% Description: Handling cast messages |
|---|
| 85 | %%-------------------------------------------------------------------- |
|---|
| 86 | handle_cast({filter, Id, Params}, State) -> |
|---|
| 87 | Filters = case proplists:get_value(Id, State#state.filters) of |
|---|
| 88 | undefined -> State#state.filters; |
|---|
| 89 | Value -> State#state.filters -- [{Id, Value}] |
|---|
| 90 | end, |
|---|
| 91 | error_logger:info_msg("Filtro fijado a: ~p~n", [Params]), |
|---|
| 92 | {noreply, State#state{filters=Filters ++ [{Id, Params}]}}; |
|---|
| 93 | |
|---|
| 94 | handle_cast({syslog, _Severity, _Facility, DateTime, Machine, Message} = _Msg, State) -> |
|---|
| 95 | MsgTxt = io_lib:format("~s ~s ~s", [DateTime, Machine, Message]), |
|---|
| 96 | error_logger:info_msg("Mensaje para ~p: [~s]~n", [State#state.users, MsgTxt]), |
|---|
| 97 | {noreply, send_msg_all(State#state.users, Machine, MsgTxt, State)}; |
|---|
| 98 | |
|---|
| 99 | handle_cast(Msg, State) -> |
|---|
| 100 | error_logger:info_msg("Mensaje no reconocido: ~p~n", [Msg]), |
|---|
| 101 | {noreply, State}. |
|---|
| 102 | |
|---|
| 103 | %%-------------------------------------------------------------------- |
|---|
| 104 | %% Function: handle_info(Info, State) -> {noreply, State} | |
|---|
| 105 | %% {noreply, State, Timeout} | |
|---|
| 106 | %% {stop, Reason, State} |
|---|
| 107 | %% Description: Handling all non call/cast messages |
|---|
| 108 | %%-------------------------------------------------------------------- |
|---|
| 109 | handle_info({route, From, To, Packet}, State) -> |
|---|
| 110 | {jid, Id, _, _, _, _, _} = From, |
|---|
| 111 | Packet2 = case Packet of |
|---|
| 112 | ["status "|Pin] -> |
|---|
| 113 | [Pin|" active"]; |
|---|
| 114 | {xmlelement,"message",Q1,[{xmlelement,"body",Subject,[{xmlcdata,Body}]}|Q2]} -> |
|---|
| 115 | {xmlelement,"message",Q1,[{xmlelement,"body",Subject,[{xmlcdata, get_body(Id, Body, State)}]}|Q2]}; |
|---|
| 116 | {xmlelement,"message",Q1,[_Active,{xmlelement,"body",Subject,[{xmlcdata,Body}]}|Q2]} -> |
|---|
| 117 | {xmlelement,"message",Q1,[{xmlelement,"body",Subject,[{xmlcdata, get_body(Id, Body, State)}]}|Q2]}; |
|---|
| 118 | P -> |
|---|
| 119 | error_logger:info_msg("Mensaje (diferente): ~p~n", [P]), |
|---|
| 120 | P |
|---|
| 121 | end, |
|---|
| 122 | ejabberd_router:route(To, From, Packet2), |
|---|
| 123 | {noreply, State}; |
|---|
| 124 | |
|---|
| 125 | handle_info(Info, State) -> |
|---|
| 126 | io:format("Recibida Info: ~p~n", [Info]), |
|---|
| 127 | {noreply, State}. |
|---|
| 128 | |
|---|
| 129 | %%-------------------------------------------------------------------- |
|---|
| 130 | %% Function: terminate(Reason, State) -> void() |
|---|
| 131 | %% Description: This function is called by a gen_server when it is about to |
|---|
| 132 | %% terminate. It should be the opposite of Module:init/1 and do any necessary |
|---|
| 133 | %% cleaning up. When it returns, the gen_server terminates with Reason. |
|---|
| 134 | %% The return value is ignored. |
|---|
| 135 | %%-------------------------------------------------------------------- |
|---|
| 136 | terminate(_Reason, State) -> |
|---|
| 137 | ejabberd_router:unregister_route(State#state.host), |
|---|
| 138 | ok. |
|---|
| 139 | |
|---|
| 140 | %%-------------------------------------------------------------------- |
|---|
| 141 | %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} |
|---|
| 142 | %% Description: Convert process state when code is changed |
|---|
| 143 | %%-------------------------------------------------------------------- |
|---|
| 144 | code_change(_OldVsn, State, _Extra) -> |
|---|
| 145 | {ok, State}. |
|---|
| 146 | |
|---|
| 147 | %===================================================================== |
|---|
| 148 | % Internal functions |
|---|
| 149 | %===================================================================== |
|---|
| 150 | |
|---|
| 151 | send_msg(Pin, Machine, MsgList, State) -> |
|---|
| 152 | Service = State#state.host, |
|---|
| 153 | Filter = proplists:get_value(Pin, State#state.filters), |
|---|
| 154 | error_logger:info_msg("Filter (~p): ~p~n", [Pin, Filter]), |
|---|
| 155 | if |
|---|
| 156 | (Filter =:= undefined) or (Filter =:= Machine) -> |
|---|
| 157 | [_,Host] = string:tokens(Service, "."), |
|---|
| 158 | case ejabberd_sm:get_user_info(Pin, Host, "Jet") of |
|---|
| 159 | offline -> State; %% no se hace nada para este caso. |
|---|
| 160 | _ -> |
|---|
| 161 | JId_src = {jid,[],Service,[],[],Service,[]}, |
|---|
| 162 | JId_dst = {jid,Pin,Host,"Jet",Pin,Host,"Jet"}, |
|---|
| 163 | IdNum = get_id(State#state.id), |
|---|
| 164 | Id = {"id", IdNum}, |
|---|
| 165 | From = {"from", Service}, |
|---|
| 166 | TypeMsg = {"type", "chat"}, |
|---|
| 167 | To = {"to", Pin ++ "@" ++ Host ++ "/Jet"}, |
|---|
| 168 | Msg = list_to_binary(MsgList), |
|---|
| 169 | ejabberd_router:route(JId_src, JId_dst, {xmlelement,"message",[Id, To, From, TypeMsg], [{xmlelement,"body",[],[{xmlcdata,Msg}]}, {xmlelement,"thread",[],[{xmlcdata,<<"QOR8C2">>}]}]}), |
|---|
| 170 | State#state{id=IdNum} |
|---|
| 171 | end; |
|---|
| 172 | true -> |
|---|
| 173 | error_logger:info_msg("Mensaje ignorado por filtro [~s]~n", [Filter]), |
|---|
| 174 | State |
|---|
| 175 | end. |
|---|
| 176 | |
|---|
| 177 | send_msg_all([Pin|Pines], Machine, MsgList, State) -> |
|---|
| 178 | send_msg_all(Pines, Machine, MsgList, send_msg(Pin, Machine, MsgList, State)); |
|---|
| 179 | |
|---|
| 180 | send_msg_all([], _, _, State) -> |
|---|
| 181 | State. |
|---|
| 182 | |
|---|
| 183 | %% genera el ID para el mensaje |
|---|
| 184 | get_id( [$9,$9,$9,$9] ) -> |
|---|
| 185 | "0000"; |
|---|
| 186 | get_id( [N1,$9,$9,$9] ) -> |
|---|
| 187 | [N1+1,$0,$0,$0]; |
|---|
| 188 | get_id( [N1,N2,$9,$9] ) -> |
|---|
| 189 | [N1,N2+1,$0,$0]; |
|---|
| 190 | get_id( [N1,N2,N3,$9] ) -> |
|---|
| 191 | [N1,N2,N3+1,$0]; |
|---|
| 192 | get_id( [N1,N2,N3,N4] ) -> |
|---|
| 193 | [N1,N2,N3,N4+1]. |
|---|
| 194 | |
|---|
| 195 | get_body(Id, Body, State) -> |
|---|
| 196 | case erlang:binary_to_list(Body) of |
|---|
| 197 | "help" -> |
|---|
| 198 | io_lib:format(" |
|---|
| 199 | esyslog - ejabberd module |
|---|
| 200 | |
|---|
| 201 | commands: |
|---|
| 202 | remove filters - removes all filters |
|---|
| 203 | show filters - show all filters |
|---|
| 204 | filter machine XXX - filter by machine XXX |
|---|
| 205 | |
|---|
| 206 | ", []); |
|---|
| 207 | "remove filter" ++ _ -> |
|---|
| 208 | gen_server:cast(?PROCNAME, {filter, Id, undefined}), |
|---|
| 209 | io_lib:format("Removed filters.", []); |
|---|
| 210 | "show filter" ++ _ -> |
|---|
| 211 | io_lib:format("Filters: ~p", [proplists:get_value(Id, State#state.filters)]); |
|---|
| 212 | "filter machine " ++ Params -> |
|---|
| 213 | gen_server:cast(?PROCNAME, {filter, Id, Params}), |
|---|
| 214 | io_lib:format("Filters: ~p", [Params]); |
|---|
| 215 | BodyBin -> |
|---|
| 216 | error_logger:info_msg("Mensaje: ~p~n", [BodyBin]), |
|---|
| 217 | BodyBin |
|---|
| 218 | end. |
|---|