Skip to content

Commit b0f8214

Browse files
kzemekfenek
authored andcommitted
Add hex escaping of binaries to MySQL backend. (#1678)
1 parent b8326b8 commit b0f8214

File tree

7 files changed

+85
-84
lines changed

7 files changed

+85
-84
lines changed

src/bin_to_hex.erl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
-compile([native, {hipe, [o3]}, {inline, [hex/1]}]).
66

7-
-export([bin_to_hex/1]).
7+
-export([bin_to_hex/1, hex_to_bin/1]).
88

99
bin_to_hex(B) when is_binary(B) ->
1010
bin_to_hex(B, <<>>).
@@ -24,6 +24,14 @@ bin_to_hex_(<<A:8, B:8, C:8, D:8, E:8, F:8, G:8, H:8, Rest/binary>>, Acc) ->
2424
<<Acc/binary,
2525
?H(A), ?H(B), ?H(C), ?H(D), ?H(E), ?H(F), ?H(G), ?H(H)>>).
2626

27+
-spec hex_to_bin(binary()) -> <<_:_*1>>.
28+
hex_to_bin(Bin) when is_binary(Bin) ->
29+
<< <<(hex_to_int(X, Y))>> || <<X, Y>> <= Bin>>.
30+
31+
-spec hex_to_int(byte(), byte()) -> integer().
32+
hex_to_int(X, Y) when is_integer(X), is_integer(Y) ->
33+
list_to_integer([X, Y], 16).
34+
2735
hex(X) ->
2836
element(
2937
X+1, {16#3030, 16#3031, 16#3032, 16#3033, 16#3034, 16#3035, 16#3036,

src/mod_mam_muc_odbc_arch.erl

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,7 @@ archive_message_unsafe(Host, MessID, RoomID, FromNick, Packet) ->
147147
SRoomID = integer_to_list(RoomID),
148148
SFromNick = mongoose_rdbms:escape(FromNick),
149149
Data = packet_to_stored_binary(Host, Packet),
150-
EscFormat = mongoose_rdbms:escape_format(Host),
151-
SData = mongoose_rdbms:escape_binary(EscFormat, Data),
150+
SData = mongoose_rdbms:escape_binary(Host, Data),
152151
SMessID = integer_to_list(MessID),
153152
TextBody = mod_mam_utils:packet_to_search_body(mod_mam_muc, Host, Packet),
154153
STextBody = mongoose_rdbms:escape(TextBody),
@@ -165,7 +164,7 @@ write_message(Host, SMessID, SRoomID, SFromNick, SData, STextBody) ->
165164
["INSERT INTO ", "mam_muc_message", " ",
166165
"(id, room_id, nick_name, message, search_body) "
167166
"VALUES ('", SMessID, "', '", SRoomID, "', "
168-
"'", SFromNick, "', '", SData, "', '", STextBody, "')"]),
167+
"'", SFromNick, "', ", SData, ", '", STextBody, "')"]),
169168
ok.
170169

171170

@@ -390,18 +389,16 @@ before_id(ID, Filter) ->
390389
-spec rows_to_uniform_format([raw_row()], jid:server(), jid:jid()) ->
391390
[mod_mam_muc:row()].
392391
rows_to_uniform_format(MessageRows, Host, RoomJID) ->
393-
EscFormat = mongoose_rdbms:escape_format(Host),
394-
DbEngine = mongoose_rdbms:db_engine(Host),
395-
[row_to_uniform_format(Host, DbEngine, EscFormat, Row, RoomJID) || Row <- MessageRows].
392+
Pool = mongoose_rdbms_sup:pool(Host),
393+
[row_to_uniform_format(Host, Pool, Row, RoomJID) || Row <- MessageRows].
396394

397395

398-
-spec row_to_uniform_format(jid:server(), atom(), atom(),
399-
raw_row(), jid:jid()) -> mod_mam_muc:row().
400-
row_to_uniform_format(Host, DbEngine, EscFormat, {BMessID, BNick, SDataRaw}, RoomJID) ->
396+
-spec row_to_uniform_format(jid:server(), mongoose_rdbms:pool(), raw_row(), jid:jid()) ->
397+
mod_mam_muc:row().
398+
row_to_uniform_format(Host, Pool, {BMessID, BNick, SDataRaw}, RoomJID) ->
401399
MessID = mongoose_rdbms:result_to_integer(BMessID),
402400
SrcJID = jid:replace_resource(RoomJID, BNick),
403-
SData = mongoose_rdbms:unescape_odbc_binary(DbEngine, SDataRaw),
404-
Data = mongoose_rdbms:unescape_binary(EscFormat, SData),
401+
Data = mongoose_rdbms:unescape_binary(Pool, SDataRaw),
405402
Packet = stored_binary_to_packet(Host, Data),
406403
{MessID, SrcJID, Packet}.
407404

@@ -646,4 +643,3 @@ stored_binary_to_packet(Host, Bin) ->
646643
-spec db_message_codec(Host :: jid:server()) -> module().
647644
db_message_codec(Host) ->
648645
gen_mod:get_module_opt(Host, ?MODULE, db_message_format, mam_message_compressed_eterm).
649-

src/mod_mam_odbc_arch.erl

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,7 @@ do_archive_message(_Result, Host, MessID, UserID,
227227
Data = packet_to_stored_binary(Host, Packet),
228228
TextBody = mod_mam_utils:packet_to_search_body(mod_mam, Host, Packet),
229229
STextBody = mongoose_rdbms:escape(TextBody),
230-
EscFormat = mongoose_rdbms:escape_format(Host),
231-
SData = mongoose_rdbms:escape_binary(EscFormat, Data),
230+
SData = mongoose_rdbms:escape_binary(Host, Data),
232231
SMessID = integer_to_list(MessID),
233232
Table = "mam_message",
234233
write_message(Host, Table, SMessID, SUserID, SBareRemJID,
@@ -238,7 +237,7 @@ do_archive_message(_Result, Host, MessID, UserID,
238237
-spec write_message(Host :: jid:server(), Table :: string(),
239238
SMessID :: string(), SUserID :: string(), SBareRemJID :: string(),
240239
SRemLResource :: string(), SDir :: string(), SSrcJID :: string(),
241-
SData :: string(), TextBody :: string()) -> 'ok'.
240+
SData :: binary(), TextBody :: string()) -> 'ok'.
242241
write_message(Host, Table, SMessID, SUserID, SBareRemJID,
243242
SRemLResource, SDir, SSrcJID, SData, STextBody) ->
244243
{updated, 1} =
@@ -249,7 +248,7 @@ write_message(Host, Table, SMessID, SUserID, SBareRemJID,
249248
"from_jid, message, search_body) "
250249
"VALUES ('", SMessID, "', '", SUserID, "', '", SBareRemJID, "', "
251250
"'", SRemLResource, "', '", SDir, "', ",
252-
"'", SSrcJID, "', '", SData, "', '", STextBody, "');"]),
251+
"'", SSrcJID, "', ", SData, ", '", STextBody, "');"]),
253252
ok.
254253

255254
prepare_message(Host, MessID, UserID, LocJID = #jid{}, RemJID = #jid{lresource = RemLResource},
@@ -483,15 +482,13 @@ before_id(ID, Filter) ->
483482
[Filter, " AND id < '", SID, "'"].
484483

485484
rows_to_uniform_format(Host, UserJID, MessageRows) ->
486-
EscFormat = mongoose_rdbms:escape_format(Host),
487-
DbEngine = mongoose_rdbms:db_engine(Host),
488-
[row_to_uniform_format(Host, DbEngine, UserJID, EscFormat, Row) || Row <- MessageRows].
485+
Pool = mongoose_rdbms_sup:pool(Host),
486+
[row_to_uniform_format(Host, Pool, UserJID, Row) || Row <- MessageRows].
489487

490-
row_to_uniform_format(Host, DbEngine, UserJID, EscFormat, {BMessID, BSrcJID, SDataRaw}) ->
488+
row_to_uniform_format(Host, Pool, UserJID, {BMessID, BSrcJID, SDataRaw}) ->
491489
MessID = mongoose_rdbms:result_to_integer(BMessID),
492490
SrcJID = stored_binary_to_jid(Host, UserJID, BSrcJID),
493-
SData = mongoose_rdbms:unescape_odbc_binary(DbEngine, SDataRaw),
494-
Data = mongoose_rdbms:unescape_binary(EscFormat, SData),
491+
Data = mongoose_rdbms:unescape_binary(Pool, SDataRaw),
495492
Packet = stored_binary_to_packet(Host, Data),
496493
{MessID, SrcJID, Packet}.
497494

@@ -758,4 +755,3 @@ db_jid_codec(Host) ->
758755
-spec db_message_codec(jid:server()) -> module().
759756
db_message_codec(Host) ->
760757
gen_mod:get_module_opt(Host, ?MODULE, db_message_format, mam_message_compressed_eterm).
761-

src/rdbms/mongoose_rdbms.erl

Lines changed: 14 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131

3232
-behaviour(gen_server).
3333

34-
-callback escape_format(Pool :: pool()) -> atom().
34+
-callback escape_binary(Pool :: pool(), binary()) -> iodata().
35+
-callback unescape_binary(Pool :: pool(), binary()) -> binary().
3536
-callback connect(Args :: any(), QueryTimeout :: non_neg_integer()) ->
3637
{ok, Connection :: term()} | {error, Reason :: any()}.
3738
-callback disconnect(Connection :: term()) -> any().
@@ -56,10 +57,8 @@
5657
print_state/1]).
5758

5859
%% BLOB escaping
59-
-export([escape_format/1,
60-
escape_binary/2,
61-
unescape_binary/2,
62-
unescape_odbc_binary/2]).
60+
-export([escape_binary/2,
61+
unescape_binary/2]).
6362

6463
%% count / integra types decoding
6564
-export([result_to_integer/1]).
@@ -213,45 +212,24 @@ escape_like(S) ->
213212
rdbms_queries:escape_like_string(S).
214213

215214

216-
-spec escape_format(server()) -> hex | simple_escape.
217-
escape_format(HostOrPool) ->
215+
-spec escape_binary(server(), binary()) -> iodata().
216+
escape_binary(HostOrPool, Bin) when is_binary(Bin) ->
218217
Pool = mongoose_rdbms_sup:pool(HostOrPool),
219-
mongoose_rdbms_backend:escape_format(Pool).
220-
221-
-spec escape_binary('hex' | 'simple_escape', binary()) -> binary() | string().
222-
escape_binary(hex, Bin) when is_binary(Bin) ->
223-
<<"\\\\x", (bin_to_hex:bin_to_hex(Bin))/binary>>;
224-
escape_binary(mssql_hex, Bin) when is_binary(Bin) ->
225-
bin_to_hex:bin_to_hex(Bin);
226-
escape_binary(simple_escape, Bin) when is_binary(Bin) ->
227-
escape(Bin).
228-
229-
-spec unescape_binary('hex' | 'simple_escape', binary()) -> binary().
230-
unescape_binary(hex, <<"\\x", Bin/binary>>) when is_binary(Bin) ->
231-
hex_to_bin(Bin);
232-
unescape_binary(_, Bin) ->
233-
Bin.
234-
235-
-spec unescape_odbc_binary(atom(), binary()) -> binary().
236-
unescape_odbc_binary(odbc, Bin) when is_binary(Bin)->
237-
hex_to_bin(Bin);
238-
unescape_odbc_binary(_, Bin) ->
239-
Bin.
218+
mongoose_rdbms_backend:escape_binary(Pool, Bin).
219+
220+
221+
-spec unescape_binary(server(), binary()) -> binary().
222+
unescape_binary(HostOrPool, Bin) when is_binary(Bin) ->
223+
Pool = mongoose_rdbms_sup:pool(HostOrPool),
224+
mongoose_rdbms_backend:unescape_binary(Pool, Bin).
225+
240226

241227
-spec result_to_integer(binary() | integer()) -> integer().
242228
result_to_integer(Int) when is_integer(Int) ->
243229
Int;
244230
result_to_integer(Bin) when is_binary(Bin) ->
245231
binary_to_integer(Bin).
246232

247-
-spec hex_to_bin(binary()) -> <<_:_*1>>.
248-
hex_to_bin(Bin) when is_binary(Bin) ->
249-
<< <<(hex_to_int(X, Y))>> || <<X, Y>> <= Bin>>.
250-
251-
-spec hex_to_int(byte(), byte()) -> integer().
252-
hex_to_int(X, Y) when is_integer(X), is_integer(Y) ->
253-
list_to_integer([X, Y], 16).
254-
255233
-spec to_bool(binary() | string() | atom() | integer() | any()) -> boolean().
256234
to_bool(B) when is_binary(B) ->
257235
to_bool(binary_to_list(B));

src/rdbms/mongoose_rdbms_mysql.erl

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,18 @@
2222

2323
-define(MYSQL_PORT, 3306).
2424

25-
-export([escape_format/1, connect/2, disconnect/1, query/3, prepare/6, execute/4]).
25+
-export([escape_binary/2, unescape_binary/2, connect/2, disconnect/1,
26+
query/3, prepare/6, execute/4]).
2627

2728
%% API
2829

29-
-spec escape_format(mongoose_rdbms:pool()) -> atom().
30-
escape_format(_Pool) ->
31-
simple_escape.
30+
-spec escape_binary(mongoose_rdbms:pool(), binary()) -> iodata().
31+
escape_binary(_Pool, Bin) when is_binary(Bin) ->
32+
[<<"X'">>, bin_to_hex:bin_to_hex(Bin), <<"'">>].
33+
34+
-spec unescape_binary(mongoose_rdbms:pool(), binary()) -> binary().
35+
unescape_binary(_Pool, Bin) when is_binary(Bin) ->
36+
Bin.
3237

3338
-spec connect(Args :: any(), QueryTimeout :: non_neg_integer()) ->
3439
{ok, Connection :: term()} | {error, Reason :: any()}.

src/rdbms/mongoose_rdbms_odbc.erl

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,18 @@
1818
-author('konrad.zemek@erlang-solutions.com').
1919
-behaviour(mongoose_rdbms).
2020

21-
-export([escape_format/1, connect/2, disconnect/1, query/3, prepare/6, execute/4]).
21+
-export([escape_binary/2, unescape_binary/2, connect/2, disconnect/1,
22+
query/3, prepare/6, execute/4]).
2223

2324
%% API
2425

25-
-spec escape_format(mongoose_rdbms:pool()) -> atom().
26-
escape_format(Pool) ->
27-
case mongoose_rdbms_sup:get_option(Pool, odbc_server_type) of
28-
pgsql ->
29-
hex;
30-
mssql ->
31-
mssql_hex;
32-
_ ->
33-
simple_escape
34-
end.
26+
-spec escape_binary(mongoose_rdbms:pool(), binary()) -> iodata().
27+
escape_binary(Pool, Bin) when is_binary(Bin) ->
28+
escape_binary(Pool, server_type(Pool), Bin).
29+
30+
-spec unescape_binary(mongoose_rdbms:pool(), binary()) -> binary().
31+
unescape_binary(_Pool, Bin) when is_binary(Bin) ->
32+
bin_to_hex:hex_to_bin(Bin).
3533

3634
-spec connect(Args :: any(), QueryTimeout :: non_neg_integer()) ->
3735
{ok, Connection :: term()} | {error, Reason :: any()}.
@@ -61,10 +59,10 @@ query(Connection, Query, Timeout) ->
6159
Fields :: [binary()], Statement :: iodata()) ->
6260
{ok, {[binary()], [fun((term()) -> tuple())]}}.
6361
prepare(Pool, Connection, _Name, Table, Fields, Statement) ->
64-
BinEscapeFormat = escape_format(Pool),
6562
{ok, TableDesc} = odbc:describe_table(Connection, unicode:characters_to_list(Table)),
6663
SplitQuery = binary:split(iolist_to_binary(Statement), <<"?">>, [global]),
67-
ParamMappers = [field_name_to_mapper(BinEscapeFormat, TableDesc, Field) || Field <- Fields],
64+
ServerType = server_type(Pool),
65+
ParamMappers = [field_name_to_mapper(Pool, ServerType, TableDesc, Field) || Field <- Fields],
6866
{ok, {SplitQuery, ParamMappers}}.
6967

7068
-spec execute(Connection :: term(), Statement :: {[binary()], [fun((term()) -> tuple())]},
@@ -89,13 +87,14 @@ parse({error, Reason}) ->
8987
parse(Other) ->
9088
Other.
9189

92-
-spec field_name_to_mapper(BinEscFormat :: atom(), TableDesc :: proplists:proplist(),
90+
-spec field_name_to_mapper(Pool :: mongoose_rdbms:pool(), ServerType :: atom(),
91+
TableDesc :: proplists:proplist(),
9392
FieldName :: binary()) -> fun((term()) -> tuple()).
94-
field_name_to_mapper(BinEscFormat, TableDesc, FieldName) ->
93+
field_name_to_mapper(Pool, ServerType, TableDesc, FieldName) ->
9594
{_, ODBCType} = lists:keyfind(unicode:characters_to_list(FieldName), 1, TableDesc),
9695
case ODBCType of
9796
T when T =:= 'SQL_BINARY'; T =:= 'SQL_VARBINARY'; T =:= 'SQL_LONGVARBINARY' ->
98-
fun(P) -> {[<<"'">>, mongoose_rdbms:escape_binary(BinEscFormat, P), <<"'">>], []} end;
97+
fun(P) -> {[escape_binary(Pool, ServerType, P)], []} end;
9998
'SQL_LONGVARCHAR' ->
10099
fun(P) -> {[<<"'">>, mongoose_rdbms:escape(P), <<"'">>], []} end;
101100
'SQL_BIGINT' ->
@@ -123,3 +122,15 @@ unsplit_query([QueryHead | QueryRest], ParamMappers, [Param | Params], QueryAcc,
123122
NewQueryAcc = [InlineQuery, QueryHead | QueryAcc],
124123
NewParamsAcc = ODBCParam ++ ParamsAcc,
125124
unsplit_query(QueryRest, NextParamMappers, Params, NewQueryAcc, NewParamsAcc).
125+
126+
-spec server_type(mongoose_rdbms:pool()) -> atom().
127+
server_type(Pool) ->
128+
mongoose_rdbms_sup:get_option(Pool, odbc_server_type).
129+
130+
-spec escape_binary(mongoose_rdbms:pool(), ServerType :: atom(), binary()) -> iodata().
131+
escape_binary(Pool, pgsql, Bin) ->
132+
mongoose_rdbms_pgsql:escape_binary(Pool, Bin);
133+
escape_binary(Pool, mysql, Bin) ->
134+
mongoose_rdbms_mysql:escape_binary(Pool, Bin);
135+
escape_binary(_Pool, _ServerType, Bin) ->
136+
[$', bin_to_hex:bin_to_hex(Bin), $'].

src/rdbms/mongoose_rdbms_pgsql.erl

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,20 @@
2222

2323
-define(PGSQL_PORT, 5432).
2424

25-
-export([escape_format/1, connect/2, disconnect/1, query/3, prepare/6, execute/4]).
25+
-export([escape_binary/2, unescape_binary/2, connect/2, disconnect/1,
26+
query/3, prepare/6, execute/4]).
2627

2728
%% API
2829

29-
-spec escape_format(mongoose_rdbms:pool()) -> atom().
30-
escape_format(_Pool) ->
31-
hex.
30+
-spec escape_binary(mongoose_rdbms:pool(), binary()) -> iodata().
31+
escape_binary(_Pool, Bin) when is_binary(Bin) ->
32+
[<<"E'\\\\x">>, bin_to_hex:bin_to_hex(Bin), <<"'::bytea">>].
33+
34+
-spec unescape_binary(mongoose_rdbms:pool(), binary()) -> binary().
35+
unescape_binary(_Pool, <<"\\x", Bin/binary>>) ->
36+
bin_to_hex:hex_to_bin(Bin);
37+
unescape_binary(_Pool, Bin) when is_binary(Bin) ->
38+
Bin.
3239

3340
-spec connect(Args :: any(), QueryTimeout :: non_neg_integer()) ->
3441
{ok, Connection :: term()} | {error, Reason :: any()}.

0 commit comments

Comments
 (0)