diff options
Diffstat (limited to 'src/mod_push_sql.erl')
-rw-r--r-- | src/mod_push_sql.erl | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/src/mod_push_sql.erl b/src/mod_push_sql.erl new file mode 100644 index 000000000..e4634a34e --- /dev/null +++ b/src/mod_push_sql.erl @@ -0,0 +1,221 @@ +%%%---------------------------------------------------------------------- +%%% ejabberd, Copyright (C) 2017 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-module(mod_push_sql). +-behaviour(mod_push). +-compile([{parse_transform, ejabberd_sql_pt}]). + +%% API +-export([init/2, store_session/6, lookup_session/4, lookup_session/3, + lookup_sessions/3, lookup_sessions/2, lookup_sessions/1, + delete_session/3, delete_old_sessions/2, export/1]). + +-include("xmpp.hrl"). +-include("logger.hrl"). +-include("ejabberd_sql_pt.hrl"). +-include("mod_push.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +init(_Host, _Opts) -> + ok. + +store_session(LUser, LServer, NowTS, PushJID, Node, XData) -> + XML = encode_xdata(XData), + TS = misc:now_to_usec(NowTS), + PushLJID = jid:tolower(PushJID), + Service = jid:encode(PushLJID), + case ?SQL_UPSERT(LServer, "push_session", + ["!username=%(LUser)s", + "!timestamp=%(TS)d", + "!service=%(Service)s", + "!node=%(Node)s", + "xml=%(XML)s"]) of + ok -> + {ok, {NowTS, PushLJID, Node, XData}}; + Err -> + ?ERROR_MSG("Failed to update 'push_session' table: ~p", [Err]), + {error, db_failure} + end. + +lookup_session(LUser, LServer, PushJID, Node) -> + PushLJID = jid:tolower(PushJID), + Service = jid:encode(PushLJID), + case ejabberd_sql:sql_query( + LServer, + ?SQL("select @(timestamp)d, @(xml)s from push_session " + "where username=%(LUser)s and service=%(Service)s " + "and node=%(Node)s")) of + {selected, [{TS, XML}]} -> + NowTS = misc:usec_to_now(TS), + XData = decode_xdata(XML, LUser, LServer), + {ok, {NowTS, PushLJID, Node, XData}}; + {selected, []} -> + {error, notfound}; + Err -> + ?ERROR_MSG("Failed to select from 'push_session' table: ~p", [Err]), + {error, db_failure} + end. + +lookup_session(LUser, LServer, NowTS) -> + TS = misc:now_to_usec(NowTS), + case ejabberd_sql:sql_query( + LServer, + ?SQL("select @(service)s, @(node)s, @(xml)s " + "from push_session where username=%(LUser)s " + "and timestamp=%(TS)d")) of + {selected, [{Service, Node, XML}]} -> + PushLJID = jid:tolower(jid:decode(Service)), + XData = decode_xdata(XML, LUser, LServer), + {ok, {NowTS, PushLJID, Node, XData}}; + {selected, []} -> + {error, notfound}; + Err -> + ?ERROR_MSG("Failed to select from 'push_session' table: ~p", [Err]), + {error, db_failure} + end. + +lookup_sessions(LUser, LServer, PushJID) -> + PushLJID = jid:tolower(PushJID), + Service = jid:encode(PushLJID), + case ejabberd_sql:sql_query( + LServer, + ?SQL("select @(timestamp)d, @(xml)s, @(node)s from push_session " + "where username=%(LUser)s and service=%(Service)s")) of + {selected, Rows} -> + {ok, lists:map( + fun({TS, XML, Node}) -> + NowTS = misc:usec_to_now(TS), + XData = decode_xdata(XML, LUser, LServer), + {NowTS, PushLJID, Node, XData} + end, Rows)}; + Err -> + ?ERROR_MSG("Failed to select from 'push_session' table: ~p", [Err]), + {error, db_failure} + end. + +lookup_sessions(LUser, LServer) -> + case ejabberd_sql:sql_query( + LServer, + ?SQL("select @(timestamp)d, @(xml)s, @(node)s, @(service)s " + "from push_session where username=%(LUser)s")) of + {selected, Rows} -> + {ok, lists:map( + fun({TS, XML, Node, Service}) -> + NowTS = misc:usec_to_now(TS), + XData = decode_xdata(XML, LUser, LServer), + PushLJID = jid:tolower(jid:decode(Service)), + {NowTS, PushLJID,Node, XData} + end, Rows)}; + Err -> + ?ERROR_MSG("Failed to select from 'push_session' table: ~p", [Err]), + {error, db_failure} + end. + +lookup_sessions(LServer) -> + case ejabberd_sql:sql_query( + LServer, + ?SQL("select @(username)s, @(timestamp)d, @(xml)s, " + "@(node)s, @(service)s from push_session")) of + {selected, Rows} -> + {ok, lists:map( + fun({LUser, TS, XML, Node, Service}) -> + NowTS = misc:usec_to_now(TS), + XData = decode_xdata(XML, LUser, LServer), + PushLJID = jid:tolower(jid:decode(Service)), + {NowTS, PushLJID, Node, XData} + end, Rows)}; + Err -> + ?ERROR_MSG("Failed to select from 'push_session' table: ~p", [Err]), + {error, db_failure} + end. + +delete_session(LUser, LServer, NowTS) -> + TS = misc:now_to_usec(NowTS), + case ejabberd_sql:sql_query( + LServer, + ?SQL("delete from push_session where " + "username=%(LUser)s and timestamp=%(TS)d")) of + {updated, _} -> + ok; + Err -> + ?ERROR_MSG("failed to delete from 'push_session' table: ~p", [Err]), + {error, db_failure} + end. + +delete_old_sessions(LServer, Time) -> + TS = misc:now_to_usec(Time), + case ejabberd_sql:sql_query( + LServer, + ?SQL("delete from push_session where timestamp<%(TS)d")) of + {updated, _} -> + ok; + Err -> + ?ERROR_MSG("failed to delete from 'push_session' table: ~p", [Err]), + {error, db_failure} + end. + +export(_Server) -> + [{push_session, + fun(Host, #push_session{us = {LUser, LServer}, + timestamp = NowTS, + service = PushLJID, + node = Node, + xml = XData}) + when LServer == Host -> + TS = misc:now_to_usec(NowTS), + Service = jid:encode(PushLJID), + XML = encode_xdata(XData), + [?SQL("delete from push_session where " + "username=%(LUser)s and timestamp=%(TS)d and " + "service=%(Service)s and node=%(Node)s and " + "xml=%(XML)s;"), + ?SQL("insert into push_session(username, timestamp, " + "service, node, xml) values (" + "%(LUser)s, %(TS)d, %(Service)s, %(Node)s, %(XML)s);")]; + (_Host, _R) -> + [] + end}]. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +decode_xdata(<<>>, _LUser, _LServer) -> + undefined; +decode_xdata(XML, LUser, LServer) -> + case fxml_stream:parse_element(XML) of + #xmlel{} = El -> + try xmpp:decode(El) + catch _:{xmpp_codec, Why} -> + ?ERROR_MSG("Failed to decode ~s for user ~s@~s " + "from table 'push_session': ~s", + [XML, LUser, LServer, xmpp:format_error(Why)]), + undefined + end; + Err -> + ?ERROR_MSG("Failed to decode ~s for user ~s@~s from " + "table 'push_session': ~p", + [XML, LUser, LServer, Err]), + undefined + end. + +encode_xdata(undefined) -> + <<>>; +encode_xdata(XData) -> + fxml:element_to_binary(xmpp:encode(XData)). |