• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

processone / ejabberd / 603

17 Oct 2023 01:57PM UTC coverage: 32.654% (-0.4%) from 33.021%
603

push

github

badlop
Fixing minor typos in CHANGELOG

13497 of 41333 relevant lines covered (32.65%)

646.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

47.66
/src/mod_offline_sql.erl
1
%%%-------------------------------------------------------------------
2
%%% File    : mod_offline_sql.erl
3
%%% Author  : Evgeny Khramtsov <ekhramtsov@process-one.net>
4
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
5
%%%
6
%%%
7
%%% ejabberd, Copyright (C) 2002-2023   ProcessOne
8
%%%
9
%%% This program is free software; you can redistribute it and/or
10
%%% modify it under the terms of the GNU General Public License as
11
%%% published by the Free Software Foundation; either version 2 of the
12
%%% License, or (at your option) any later version.
13
%%%
14
%%% This program is distributed in the hope that it will be useful,
15
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
16
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
%%% General Public License for more details.
18
%%%
19
%%% You should have received a copy of the GNU General Public License along
20
%%% with this program; if not, write to the Free Software Foundation, Inc.,
21
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
%%%
23
%%%----------------------------------------------------------------------
24

25
-module(mod_offline_sql).
26

27

28
-behaviour(mod_offline).
29

30
-export([init/2, store_message/1, pop_messages/2, remove_expired_messages/1,
31
         remove_old_messages/2, remove_user/2, read_message_headers/2,
32
         read_message/3, remove_message/3, read_all_messages/2,
33
         remove_all_messages/2, count_messages/2, import/1, export/1, remove_old_messages_batch/3]).
34

35
-include_lib("xmpp/include/xmpp.hrl").
36
-include("mod_offline.hrl").
37
-include("logger.hrl").
38
-include("ejabberd_sql_pt.hrl").
39

40
%%%===================================================================
41
%%% API
42
%%%===================================================================
43
init(Host, _Opts) ->
44
    ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()),
3✔
45
    ok.
3✔
46

47
schemas() ->
48
    [#sql_schema{
3✔
49
        version = 1,
50
        tables =
51
            [#sql_table{
52
                name = <<"spool">>,
53
                columns =
54
                    [#sql_column{name = <<"username">>, type = text},
55
                     #sql_column{name = <<"server_host">>, type = text},
56
                     #sql_column{name = <<"xml">>, type = {text, big}},
57
                     #sql_column{name = <<"seq">>, type = bigserial},
58
                     #sql_column{name = <<"created_at">>, type = timestamp,
59
                                 default = true}],
60
                indices = [#sql_index{
61
                              columns = [<<"server_host">>, <<"username">>]},
62
                           #sql_index{
63
                              columns = [<<"created_at">>]}]}]}].
64

65
store_message(#offline_msg{us = {LUser, LServer}} = M) ->
66
    From = M#offline_msg.from,
387✔
67
    To = M#offline_msg.to,
387✔
68
    Packet = xmpp:set_from_to(M#offline_msg.packet, From, To),
387✔
69
    NewPacket = misc:add_delay_info(
387✔
70
                  Packet, jid:make(LServer),
71
                  M#offline_msg.timestamp,
72
                  <<"Offline Storage">>),
73
    XML = fxml:element_to_binary(
387✔
74
            xmpp:encode(NewPacket)),
75
    case ejabberd_sql:sql_query(
387✔
76
           LServer,
77
           ?SQL_INSERT(
387✔
78
              "spool",
79
              ["username=%(LUser)s",
80
               "server_host=%(LServer)s",
81
               "xml=%(XML)s"])) of
82
        {updated, _} ->
83
            ok;
387✔
84
        _ ->
85
            {error, db_failure}
×
86
    end.
87

88
pop_messages(LUser, LServer) ->
89
    case get_and_del_spool_msg_t(LServer, LUser) of
84✔
90
        {atomic, {selected, Rs}} ->
91
            {ok, lists:flatmap(
84✔
92
                   fun({_, XML}) ->
93
                           case xml_to_offline_msg(XML) of
372✔
94
                               {ok, Msg} ->
95
                                   [Msg];
372✔
96
                               _Err ->
97
                                   []
×
98
                           end
99
                   end, Rs)};
100
        Err ->
101
            {error, Err}
×
102
    end.
103

104
remove_expired_messages(_LServer) ->
105
    %% TODO
106
    {atomic, ok}.
×
107

108
remove_old_messages(Days, LServer) ->
109
    case ejabberd_sql:sql_query(
×
110
           LServer,
111
           fun(pgsql, _) ->
112
                   ejabberd_sql:sql_query_t(
×
113
                     ?SQL("DELETE FROM spool"
×
114
                          " WHERE created_at <"
115
                          " NOW() - %(Days)d * INTERVAL '1 DAY'"));
116
              (sqlite, _) ->
117
                  ejabberd_sql:sql_query_t(
×
118
                      ?SQL("DELETE FROM spool"
×
119
                           " WHERE created_at <"
120
                           " DATETIME('now', '-%(Days)d days')"));
121
              (_, _) ->
122
                   ejabberd_sql:sql_query_t(
×
123
                     ?SQL("DELETE FROM spool"
×
124
                          " WHERE created_at < NOW() - INTERVAL %(Days)d DAY"))
125
              end)
126
        of
127
        {updated, N} ->
128
            ?INFO_MSG("~p message(s) deleted from offline spool", [N]);
×
129
        _Error ->
130
            ?ERROR_MSG("Cannot delete message in offline spool: ~p", [_Error])
×
131
    end,
132
    {atomic, ok}.
×
133

134
remove_old_messages_batch(LServer, Days, Batch) ->
135
    case ejabberd_sql:sql_query(
×
136
        LServer,
137
        fun(pgsql, _) ->
138
            ejabberd_sql:sql_query_t(
×
139
                ?SQL("DELETE FROM spool"
×
140
                     " WHERE created_at <"
141
                     " NOW() - %(Days)d * INTERVAL '1 DAY' LIMIT %(Batch)d"));
142
           (sqlite, _) ->
143
               ejabberd_sql:sql_query_t(
×
144
                   ?SQL("DELETE FROM spool"
×
145
                        " WHERE created_at <"
146
                        " DATETIME('now', '-%(Days)d days') LIMIT %(Batch)d"));
147
           (_, _) ->
148
               ejabberd_sql:sql_query_t(
×
149
                   ?SQL("DELETE FROM spool"
×
150
                        " WHERE created_at < NOW() - INTERVAL %(Days)d DAY LIMIT %(Batch)d"))
151
        end)
152
    of
153
        {updated, N} ->
154
            {ok, N};
×
155
        Error ->
156
            {error, Error}
×
157
    end.
158

159
remove_user(LUser, LServer) ->
160
    ejabberd_sql:sql_query(
18✔
161
      LServer,
162
      ?SQL("delete from spool where username=%(LUser)s and %(LServer)H")).
18✔
163

164
read_message_headers(LUser, LServer) ->
165
    case ejabberd_sql:sql_query(
12✔
166
           LServer,
167
           ?SQL("select @(xml)s, @(seq)d from spool"
12✔
168
                " where username=%(LUser)s and %(LServer)H order by seq")) of
169
        {selected, Rows} ->
170
            lists:flatmap(
12✔
171
              fun({XML, Seq}) ->
172
                      case xml_to_offline_msg(XML) of
30✔
173
                          {ok, #offline_msg{from = From,
174
                                            to = To,
175
                                            timestamp = TS,
176
                                            packet = El}} ->
177
                              [{Seq, From, To, TS, El}];
30✔
178
                          _ ->
179
                              []
×
180
                      end
181
              end, Rows);
182
        _Err ->
183
            error
×
184
    end.
185

186
read_message(LUser, LServer, Seq) ->
187
    case ejabberd_sql:sql_query(
18✔
188
           LServer,
189
           ?SQL("select @(xml)s from spool where username=%(LUser)s"
18✔
190
                " and %(LServer)H"
191
                " and seq=%(Seq)d")) of
192
        {selected, [{RawXML}|_]} ->
193
            case xml_to_offline_msg(RawXML) of
15✔
194
                {ok, Msg} ->
195
                    {ok, Msg};
15✔
196
                _ ->
197
                    error
×
198
            end;
199
        _ ->
200
            error
3✔
201
    end.
202

203
remove_message(LUser, LServer, Seq) ->
204
    ejabberd_sql:sql_query(
9✔
205
      LServer,
206
      ?SQL("delete from spool where username=%(LUser)s and %(LServer)H"
9✔
207
           " and seq=%(Seq)d")),
208
    ok.
9✔
209

210
read_all_messages(LUser, LServer) ->
211
    case ejabberd_sql:sql_query(
×
212
           LServer,
213
           ?SQL("select @(xml)s from spool where "
×
214
                "username=%(LUser)s and %(LServer)H order by seq")) of
215
        {selected, Rs} ->
216
            lists:flatmap(
×
217
              fun({XML}) ->
218
                      case xml_to_offline_msg(XML) of
×
219
                          {ok, Msg} -> [Msg];
×
220
                          _ -> []
×
221
                      end
222
              end, Rs);
223
        _ ->
224
            []
×
225
    end.
226

227
remove_all_messages(LUser, LServer) ->
228
    remove_user(LUser, LServer),
3✔
229
    {atomic, ok}.
3✔
230

231
count_messages(LUser, LServer) ->
232
    case catch ejabberd_sql:sql_query(
24✔
233
                 LServer,
234
                 ?SQL("select @(count(*))d from spool "
24✔
235
                      "where username=%(LUser)s and %(LServer)H")) of
236
        {selected, [{Res}]} ->
237
            {cache, Res};
24✔
238
        {selected, []} ->
239
            {cache, 0};
×
240
        _ ->
241
            {nocache, 0}
×
242
    end.
243

244
export(_Server) ->
245
    [{offline_msg,
×
246
      fun(Host, #offline_msg{us = {LUser, LServer}})
247
            when LServer == Host ->
248
                      [?SQL("delete from spool where username=%(LUser)s"
×
249
                            " and %(LServer)H;")];
250
         (_Host, _R) ->
251
              []
×
252
      end},
253
     {offline_msg,
254
      fun(Host, #offline_msg{us = {LUser, LServer},
255
                             timestamp = TimeStamp, from = From, to = To,
256
                             packet = El})
257
            when LServer == Host ->
258
              try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of
×
259
                  Packet ->
260
                      Packet1 = xmpp:set_from_to(Packet, From, To),
×
261
                      Packet2 = misc:add_delay_info(
×
262
                                  Packet1, jid:make(LServer),
263
                                  TimeStamp, <<"Offline Storage">>),
264
                      XML = fxml:element_to_binary(xmpp:encode(Packet2)),
×
265
                      [?SQL_INSERT(
×
266
                          "spool",
267
                          ["username=%(LUser)s",
268
                           "server_host=%(LServer)s",
269
                           "xml=%(XML)s"])]
270
              catch _:{xmpp_codec, Why} ->
271
                      ?ERROR_MSG("Failed to decode packet ~p of user ~ts@~ts: ~ts",
×
272
                                 [El, LUser, LServer, xmpp:format_error(Why)]),
×
273
                      []
×
274
              end;
275
         (_Host, _R) ->
276
              []
×
277
      end}].
278

279
import(_) ->
280
    ok.
×
281

282
%%%===================================================================
283
%%% Internal functions
284
%%%===================================================================
285
xml_to_offline_msg(XML) ->
286
    case fxml_stream:parse_element(XML) of
417✔
287
        #xmlel{} = El ->
288
            el_to_offline_msg(El);
417✔
289
        Err ->
290
            ?ERROR_MSG("Got ~p when parsing XML packet ~ts",
×
291
                       [Err, XML]),
×
292
            Err
×
293
    end.
294

295
el_to_offline_msg(El) ->
296
    To_s = fxml:get_tag_attr_s(<<"to">>, El),
417✔
297
    From_s = fxml:get_tag_attr_s(<<"from">>, El),
417✔
298
    try
417✔
299
        To = jid:decode(To_s),
417✔
300
        From = jid:decode(From_s),
417✔
301
        {ok, #offline_msg{us = {To#jid.luser, To#jid.lserver},
417✔
302
                          from = From,
303
                          to = To,
304
                          packet = El}}
305
    catch _:{bad_jid, To_s} ->
306
            ?ERROR_MSG("Failed to get 'to' JID from offline XML ~p", [El]),
×
307
            {error, bad_jid_to};
×
308
          _:{bad_jid, From_s} ->
309
            ?ERROR_MSG("Failed to get 'from' JID from offline XML ~p", [El]),
×
310
            {error, bad_jid_from}
×
311
    end.
312

313
get_and_del_spool_msg_t(LServer, LUser) ->
314
    F = fun () ->
84✔
315
                Result =
84✔
316
                    ejabberd_sql:sql_query_t(
317
                      ?SQL("select @(username)s, @(xml)s from spool where "
84✔
318
                           "username=%(LUser)s and %(LServer)H order by seq;")),
319
                DResult =
84✔
320
                    ejabberd_sql:sql_query_t(
321
                      ?SQL("delete from spool where"
84✔
322
                           " username=%(LUser)s and %(LServer)H;")),
323
                case {Result, DResult} of
84✔
324
                    {{selected, Rs}, {updated, DC}} when length(Rs) /= DC ->
325
                        ejabberd_sql:restart(concurent_insert);
×
326
                    _ ->
327
                        Result
84✔
328
                end
329
        end,
330
    ejabberd_sql:sql_transaction(LServer, F).
84✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc