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

processone / ejabberd / 1195

14 Nov 2025 07:30AM UTC coverage: 14.534% (-19.2%) from 33.775%
1195

Pull #4493

github

web-flow
Merge 53c116bc1 into 026bd24a5
Pull Request #4493: Great invitations

0 of 521 new or added lines in 8 files covered. (0.0%)

2195 existing lines in 61 files now uncovered.

6752 of 46457 relevant lines covered (14.53%)

47.62 hits per line

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

0.0
/src/mod_invites_sql.erl
1
%%%----------------------------------------------------------------------
2
%%% File    : mod_invites_sql.erl
3
%%% Author  : Stefan Strigler <stefan@strigler.de>
4
%%% Created : Mon Sep 15 2025 by Stefan Strigler <stefan@strigler.de>
5
%%%
6
%%%
7
%%% ejabberd, Copyright (C) 2025 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
-module(mod_invites_sql).
25

26
-author('stefan@strigler.de').
27

28
-behaviour(mod_invites).
29

30
-export([cleanup_expired/1, create_invite/1, expire_tokens/2, get_invite/2, init/2, is_reserved/3,
31
         is_token_valid/3, list_invites/1, num_account_invites/2, remove_user/2, set_invitee/3]).
32

33
-include("mod_invites.hrl").
34
-include("ejabberd_sql_pt.hrl").
35

36
%% @format-begin
37

38
%%--------------------------------------------------------------------
39
%%| mod_invite callbacks
40
init(Host, _Opts) ->
NEW
41
    ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()).
×
42

43
sql_schemas() ->
NEW
44
    [#sql_schema{version = 1,
×
45
                 tables =
46
                     [#sql_table{name = <<"invite_token">>,
47
                                 columns =
48
                                     [#sql_column{name = <<"token">>, type = text},
49
                                      #sql_column{name = <<"username">>, type = text},
50
                                      #sql_column{name = <<"server_host">>, type = text},
51
                                      #sql_column{name = <<"invitee">>,
52
                                                  type = text,
53
                                                  default = true},
54
                                      #sql_column{name = <<"created_at">>,
55
                                                  type = timestamp,
56
                                                  default = true},
57
                                      #sql_column{name = <<"expires">>, type = timestamp},
58
                                      #sql_column{name = <<"type">>, type = text},
59
                                      #sql_column{name = <<"account_name">>, type = text}],
60
                                 indices =
61
                                     [#sql_index{columns = [<<"token">>], unique = true},
62
                                      #sql_index{columns =
63
                                                     [<<"username">>, <<"server_host">>]}]}]}].
64

65
cleanup_expired(Host) ->
NEW
66
    {updated, Count} =
×
67
        ejabberd_sql:sql_query(Host, "delete from invite_token where expires < now()"),
NEW
68
    Count.
×
69

70
create_invite(Invite) ->
NEW
71
    #invite_token{inviter = {User, Host},
×
72
                  token = Token,
73
                  account_name = AccountName0,
74
                  created_at = CreatedAt0,
75
                  expires = Expires0,
76
                  type = Type} =
77
        Invite,
NEW
78
    TypeBin = atom_to_binary(Type),
×
NEW
79
    AccountName =
×
80
        case AccountName0 of
81
            undefined ->
NEW
82
                <<>>;
×
83
            _ ->
NEW
84
                AccountName0
×
85
        end,
NEW
86
    CreatedAt = datetime_to_sql_timestamp(CreatedAt0),
×
NEW
87
    Expires = datetime_to_sql_timestamp(Expires0),
×
88

NEW
89
    Query =
×
NEW
90
        ?SQL_INSERT("invite_token",
×
91
                    ["token=%(Token)s",
92
                     "username=%(User)s",
93
                     "server_host=%(Host)s",
94
                     "type=%(TypeBin)s",
95
                     "created_at=%(CreatedAt)s",
96
                     "expires=%(Expires)s",
97
                     "account_name=%(AccountName)s"]),
98
    %    ejabberd_sql:sql_transaction(Host, Queries)
NEW
99
    {updated, 1} = ejabberd_sql:sql_query(Host, Query),
×
NEW
100
    Invite.
×
101

102
expire_tokens(User, Server) ->
NEW
103
    {updated, Count} =
×
104
        ejabberd_sql:sql_query(Server,
NEW
105
                               ?SQL("update invite_token set expires = '1970-01-01 00:00:01' where "
×
106
                                    "username = %(User)s and %(Server)H and expires > now() and "
107
                                    "type != 'roster_only'")),
NEW
108
    Count.
×
109

110
get_invite(Host, Token) ->
NEW
111
    case ejabberd_sql:sql_query(Host,
×
NEW
112
                                ?SQL("select @(token)s, @(username)s, @(type)s, @(account_name)s, "
×
113
                                     "@(expires)s, @(created_at)s from invite_token where token = "
114
                                     "%(Token)s and %(Host)H"))
115
    of
116
        {selected, [{Token, User, Type, AccountName0, Expires, CreatedAt}]} ->
NEW
117
            AccountName =
×
118
                case AccountName0 of
119
                    <<>> ->
NEW
120
                        undefined;
×
121
                    _ ->
NEW
122
                        AccountName0
×
123
                end,
NEW
124
            #invite_token{token = Token,
×
125
                          inviter = {User, Host},
126
                          type = binary_to_existing_atom(Type),
127
                          account_name = AccountName,
128
                          expires = Expires,
129
                          created_at = CreatedAt};
130
        {selected, []} ->
NEW
131
            {error, not_found}
×
132
    end.
133

134
is_reserved(Host, Token, User) ->
NEW
135
    {selected, [{Count}]} =
×
136
        ejabberd_sql:sql_query(Host,
NEW
137
                               ?SQL("SELECT @(COUNT(*))d from invite_token WHERE %(Host)H AND token != %(Token)s AND "
×
138
                                    "account_name = %(User)s AND invitee = '' AND expires > NOW()")),
NEW
139
    Count > 0.
×
140

141
is_token_valid(Host, Token, {User, Host}) ->
NEW
142
    {selected, Rows} =
×
143
        ejabberd_sql:sql_query(Host,
NEW
144
                               ?SQL("SELECT @(token)s FROM invite_token WHERE %(Host)H AND token = %(Token)s AND "
×
145
                                    "invitee = '' AND expires > now() AND (%(User)s = '' OR username = %(User)s)")),
NEW
146
    case Rows /= [] of
×
147
        true ->
NEW
148
            true;
×
149
        false ->
NEW
150
            case get_invite(Host, Token) of
×
151
                {error, not_found} ->
NEW
152
                    throw(not_found);
×
153
                _ ->
NEW
154
                    false
×
155
            end
156
    end.
157

158
list_invites(Host) ->
NEW
159
    {selected, Rows} =
×
160
        ejabberd_sql:sql_query(Host,
NEW
161
                               ?SQL("SELECT @(token)s, @(username)s, @(type)s, @(account_name)s, "
×
162
                                    "@(expires)s, @(created_at)s FROM invite_token WHERE %(Host)H")),
NEW
163
    lists:map(
×
164
      fun({Token, User, Type, AccountName0, Expires, CreatedAt}) ->
NEW
165
              AccountName =
×
166
                  case AccountName0 of
167
                      <<>> ->
NEW
168
                          undefined;
×
169
                      _ ->
NEW
170
                          AccountName0
×
171
                  end,
NEW
172
              #invite_token{token = Token,
×
173
                            inviter = {User, Host},
174
                            type = binary_to_existing_atom(Type),
175
                            account_name = AccountName,
176
                            expires = Expires,
177
                            created_at = CreatedAt}
178
      end, Rows).
179

180
num_account_invites(User, Server) ->
NEW
181
    {selected, [{Count}]} =
×
182
        ejabberd_sql:sql_query(Server,
NEW
183
                               ?SQL("select @(count(*))d from invite_token where username=%(User)s "
×
184
                                    "and %(Server)H and type != 'roster_only'")),
NEW
185
    Count.
×
186

187
remove_user(User, Server) ->
NEW
188
    ejabberd_sql:sql_query(Server,
×
NEW
189
                           ?SQL("delete from invite_token where username=%(User)s and %(Server)H")).
×
190

191
set_invitee(Host, Token, Invitee) ->
NEW
192
    {updated, 1} =
×
193
        ejabberd_sql:sql_query(Host,
NEW
194
                               ?SQL("UPDATE invite_token SET invitee=%(Invitee)s WHERE token=%(Token)s")),
×
NEW
195
    ok.
×
196

197
%%--------------------------------------------------------------------
198
%%| helpers
199

200
datetime_to_sql_timestamp({{Year, Month, Day}, {Hour, Minute, Second}}) ->
NEW
201
    list_to_binary(io_lib:format("~4..0B-~2..0B-~2..0B ~2..0B:~2..0B:~2..0B",
×
202
                                 [Year, Month, Day, Hour, Minute, Second])).
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