mirror of
https://github.com/processone/ejabberd.git
synced 2024-10-31 15:21:38 +01:00
380 lines
13 KiB
Erlang
380 lines
13 KiB
Erlang
%%%----------------------------------------------------------------------
|
|
%%% File : ejd2odbc.erl
|
|
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
|
%%% Purpose : Export some mnesia tables to SQL DB
|
|
%%% Created : 22 Aug 2005 by Alexey Shchepin <alexey@process-one.net>
|
|
%%%
|
|
%%%
|
|
%%% ejabberd, Copyright (C) 2002-2010 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., 59 Temple Place, Suite 330, Boston, MA
|
|
%%% 02111-1307 USA
|
|
%%%
|
|
%%%----------------------------------------------------------------------
|
|
|
|
-module(ejd2odbc).
|
|
-author('alexey@process-one.net').
|
|
|
|
%% External exports
|
|
-export([export_passwd/2,
|
|
export_roster/2,
|
|
export_roster_group/2,
|
|
export_offline/2,
|
|
export_last/2,
|
|
export_vcard/2,
|
|
export_vcard_search/2,
|
|
export_private_storage/2]).
|
|
|
|
-include_lib("exmpp/include/exmpp.hrl").
|
|
|
|
-include("ejabberd.hrl").
|
|
-include("mod_roster.hrl").
|
|
|
|
-record(offline_msg, {user_host, timestamp, expire, from, to, packet}).
|
|
-record(last_activity, {user_host, timestamp, status}).
|
|
-record(vcard, {user_host, vcard}).
|
|
-record(vcard_search, {user_host,
|
|
user, luser,
|
|
fn, lfn,
|
|
family, lfamily,
|
|
given, lgiven,
|
|
middle, lmiddle,
|
|
nickname, lnickname,
|
|
bday, lbday,
|
|
ctry, lctry,
|
|
locality, llocality,
|
|
email, lemail,
|
|
orgname, lorgname,
|
|
orgunit, lorgunit
|
|
}).
|
|
-record(private_storage, {user_host_ns, xml}).
|
|
|
|
-define(MAX_RECORDS_PER_TRANSACTION, 1000).
|
|
|
|
%%%----------------------------------------------------------------------
|
|
%%% API
|
|
%%%----------------------------------------------------------------------
|
|
%%% How to use:
|
|
%%% A table can be converted from Mnesia to an ODBC database by calling
|
|
%%% one of the API function with the following parameters:
|
|
%%% - Server is the server domain you want to convert
|
|
%%% - Output can be either odbc to export to the configured relational
|
|
%%% database or "Filename" to export to text file.
|
|
|
|
export_passwd(Server, Output) ->
|
|
export_common(
|
|
Server, passwd, Output,
|
|
fun(Host, {passwd, {LUser, LServer}, Password} = _R)
|
|
when LServer == Host ->
|
|
Username = ejabberd_odbc:escape(LUser),
|
|
Pass = ejabberd_odbc:escape(Password),
|
|
["delete from users where host='", Server, "' and username='", Username ,"';\n"
|
|
"insert into users(host, username, password) "
|
|
"values ('", Server, "', '", Username, "', '", Pass, "');\n"];
|
|
(_Host, _R) ->
|
|
[]
|
|
end).
|
|
|
|
export_roster(ServerS, Output) ->
|
|
Server = list_to_binary(ServerS),
|
|
export_common(
|
|
Server, rosteritem, Output,
|
|
fun(Host, #rosteritem{user_host_jid = {LUser, LServer, {N, D, Res} = _LJID}} = R)
|
|
when LServer == Host ->
|
|
Username = ejabberd_odbc:escape(LUser),
|
|
SJID = ejabberd_odbc:escape(exmpp_jid:to_list(N, D, Res)),
|
|
ItemVals = record_to_string(R, ServerS),
|
|
["delete from rosterusers "
|
|
" where username='", Username, "' "
|
|
" and jid='", SJID, "';\n"
|
|
"insert into rosterusers("
|
|
" host, username, jid, nick, "
|
|
" subscription, ask, askmessage, "
|
|
" server, subscribe, type) "
|
|
" values ", ItemVals, ";\n"];
|
|
(_Host, _R) ->
|
|
[]
|
|
end).
|
|
|
|
export_roster_group(ServerS, Output) ->
|
|
Server = list_to_binary(ServerS),
|
|
export_common(
|
|
Server, rostergroup, Output,
|
|
fun(Host, #rostergroup{user_host_jid = {_LUser, LServer, _LJID}} = R)
|
|
when LServer == Host ->
|
|
ItemGroup = group_to_string(R, ServerS),
|
|
[%%"delete from rostergroups"
|
|
%%" where username='", Username, "'"
|
|
%%" and host='", ServerS, "'"
|
|
%%" and jid='", SJID, "';"
|
|
"insert into rostergroups(host, username, jid, grp)"
|
|
" values ", ItemGroup, ";\n"];
|
|
(_Host, _R) ->
|
|
[]
|
|
end).
|
|
|
|
export_offline(Server, Output) ->
|
|
export_common(
|
|
Server, offline_msg, Output,
|
|
fun(Host, #offline_msg{user_host = {LUser, LServer},
|
|
timestamp = TimeStamp,
|
|
from = From,
|
|
to = To,
|
|
packet = PacketString})
|
|
when LServer == Host ->
|
|
Username = ejabberd_odbc:escape(LUser),
|
|
[Packet] = exmpp_xml:parse_document(PacketString, [names_as_atom]),
|
|
Packet0 = exmpp_stanza:set_jids(Packet,
|
|
exmpp_jid:to_list(From),
|
|
exmpp_jid:to_list(To)),
|
|
Packet0b = exmpp_xml:append_child(Packet0,
|
|
jlib:timestamp_to_xml(
|
|
calendar:gregorian_seconds_to_datetime(TimeStamp),
|
|
utc,
|
|
exmpp_jid:make("", Server, ""),
|
|
"Offline Storage")),
|
|
%% TODO: Delete the next three lines once XEP-0091 is Obsolete
|
|
Packet1 = exmpp_xml:append_child(Packet0b,
|
|
jlib:timestamp_to_xml(
|
|
calendar:gregorian_seconds_to_datetime(TimeStamp))),
|
|
XML =
|
|
ejabberd_odbc:escape(
|
|
exmpp_xml:document_to_list(Packet1)),
|
|
["insert into spool(host, username, xml) "
|
|
"values ('", Server, "', '", Username, "', '",
|
|
XML,
|
|
"');\n"];
|
|
(_Host, _R) ->
|
|
[]
|
|
end).
|
|
|
|
export_last(ServerS, Output) ->
|
|
Server = list_to_binary(ServerS),
|
|
export_common(
|
|
Server, last_activity, Output,
|
|
fun(Host, #last_activity{user_host = {LUser, LServer},
|
|
timestamp = TimeStamp,
|
|
status = Status})
|
|
when LServer == Host ->
|
|
Username = ejabberd_odbc:escape(LUser),
|
|
Seconds = ejabberd_odbc:escape(integer_to_list(TimeStamp)),
|
|
State = ejabberd_odbc:escape(Status),
|
|
["delete from last where host='", ServerS, "' and username='", Username, "';\n"
|
|
"insert into last(username, seconds, state) "
|
|
"values ('", Username, "', '", Seconds, "', '", State, "');\n"];
|
|
(_Host, _R) ->
|
|
[]
|
|
end).
|
|
|
|
export_vcard(Server, Output) ->
|
|
export_common(
|
|
Server, vcard, Output,
|
|
fun(Host, #vcard{user_host = {LUser, LServer},
|
|
vcard = VCARD})
|
|
when LServer == Host ->
|
|
Username = ejabberd_odbc:escape(LUser),
|
|
SVCARD = ejabberd_odbc:escape(
|
|
exmpp_xml:document_to_list(VCARD)),
|
|
["delete from vcard where host='", Server,"' and username='", Username, "';\n"
|
|
"insert into vcard(host, username, vcard) "
|
|
"values ('", Server, "', '", Username, "', '", SVCARD, "');\n"];
|
|
(_Host, _R) ->
|
|
[]
|
|
end).
|
|
|
|
export_vcard_search(Server, Output) ->
|
|
export_common(
|
|
Server, vcard_search, Output,
|
|
fun(Host, #vcard_search{user = {User, LServer},
|
|
luser = LUser,
|
|
fn = FN, lfn = LFN,
|
|
family = Family, lfamily = LFamily,
|
|
given = Given, lgiven = LGiven,
|
|
middle = Middle, lmiddle = LMiddle,
|
|
nickname = Nickname, lnickname = LNickname,
|
|
bday = BDay, lbday = LBDay,
|
|
ctry = CTRY, lctry = LCTRY,
|
|
locality = Locality, llocality = LLocality,
|
|
email = EMail, lemail = LEMail,
|
|
orgname = OrgName, lorgname = LOrgName,
|
|
orgunit = OrgUnit, lorgunit = LOrgUnit
|
|
})
|
|
when LServer == Host ->
|
|
Username = ejabberd_odbc:escape(User),
|
|
LUsername = ejabberd_odbc:escape(LUser),
|
|
|
|
SFN = ejabberd_odbc:escape(FN),
|
|
SLFN = ejabberd_odbc:escape(LFN),
|
|
SFamily = ejabberd_odbc:escape(Family),
|
|
SLFamily = ejabberd_odbc:escape(LFamily),
|
|
SGiven = ejabberd_odbc:escape(Given),
|
|
SLGiven = ejabberd_odbc:escape(LGiven),
|
|
SMiddle = ejabberd_odbc:escape(Middle),
|
|
SLMiddle = ejabberd_odbc:escape(LMiddle),
|
|
SNickname = ejabberd_odbc:escape(Nickname),
|
|
SLNickname = ejabberd_odbc:escape(LNickname),
|
|
SBDay = ejabberd_odbc:escape(BDay),
|
|
SLBDay = ejabberd_odbc:escape(LBDay),
|
|
SCTRY = ejabberd_odbc:escape(CTRY),
|
|
SLCTRY = ejabberd_odbc:escape(LCTRY),
|
|
SLocality = ejabberd_odbc:escape(Locality),
|
|
SLLocality = ejabberd_odbc:escape(LLocality),
|
|
SEMail = ejabberd_odbc:escape(EMail),
|
|
SLEMail = ejabberd_odbc:escape(LEMail),
|
|
SOrgName = ejabberd_odbc:escape(OrgName),
|
|
SLOrgName = ejabberd_odbc:escape(LOrgName),
|
|
SOrgUnit = ejabberd_odbc:escape(OrgUnit),
|
|
SLOrgUnit = ejabberd_odbc:escape(LOrgUnit),
|
|
|
|
["delete from vcard_search where host='", Server, "' and lusername='", LUsername, "';\n"
|
|
"insert into vcard_search("
|
|
" username, lusername, fn, lfn, family, lfamily,"
|
|
" given, lgiven, middle, lmiddle, nickname, lnickname,"
|
|
" bday, lbday, ctry, lctry, locality, llocality,"
|
|
" email, lemail, orgname, lorgname, orgunit, lorgunit)"
|
|
"values (",
|
|
" '", Username, "', '", LUsername, "',"
|
|
" '", SFN, "', '", SLFN, "',"
|
|
" '", SFamily, "', '", SLFamily, "',"
|
|
" '", SGiven, "', '", SLGiven, "',"
|
|
" '", SMiddle, "', '", SLMiddle, "',"
|
|
" '", SNickname, "', '", SLNickname, "',"
|
|
" '", SBDay, "', '", SLBDay, "',"
|
|
" '", SCTRY, "', '", SLCTRY, "',"
|
|
" '", SLocality, "', '", SLLocality, "',"
|
|
" '", SEMail, "', '", SLEMail, "',"
|
|
" '", SOrgName, "', '", SLOrgName, "',"
|
|
" '", SOrgUnit, "', '", SLOrgUnit, "');\n"];
|
|
(_Host, _R) ->
|
|
[]
|
|
end).
|
|
|
|
export_private_storage(ServerS, Output) ->
|
|
Server = list_to_binary(ServerS),
|
|
export_common(
|
|
Server, private_storage, Output,
|
|
fun(Host, #private_storage{user_host_ns = {LUser, LServer, XMLNS},
|
|
xml = Data})
|
|
when LServer == Host ->
|
|
Username = ejabberd_odbc:escape(LUser),
|
|
LXMLNS = ejabberd_odbc:escape(atom_to_list(XMLNS)),
|
|
SData = ejabberd_odbc:escape(
|
|
exmpp_xml:document_to_list(Data)),
|
|
odbc_queries:set_private_data_sql(LServer, Username, LXMLNS, SData);
|
|
(_Host, _R) ->
|
|
[]
|
|
end).
|
|
|
|
%%%----------------------------------------------------------------------
|
|
%%% Internal functions
|
|
%%%----------------------------------------------------------------------
|
|
|
|
export_common(Server, Table, Output, ConvertFun) ->
|
|
IO = case Output of
|
|
odbc ->
|
|
odbc;
|
|
_ ->
|
|
{ok, IODevice} = file:open(Output, [write, raw]),
|
|
IODevice
|
|
end,
|
|
mnesia:transaction(
|
|
fun() ->
|
|
mnesia:read_lock_table(Table),
|
|
LServer = exmpp_stringprep:nameprep(Server),
|
|
{_N, SQLs} =
|
|
mnesia:foldl(
|
|
fun(R, {N, SQLs} = Acc) ->
|
|
case ConvertFun(LServer, R) of
|
|
[] ->
|
|
Acc;
|
|
SQL ->
|
|
if
|
|
N < ?MAX_RECORDS_PER_TRANSACTION - 1 ->
|
|
{N + 1, [SQL | SQLs]};
|
|
true ->
|
|
%% Execute full SQL transaction
|
|
output(LServer, IO,
|
|
["begin;\n",
|
|
lists:reverse([SQL | SQLs]),
|
|
"commit"]),
|
|
{0, []}
|
|
end
|
|
end
|
|
end, {0, []}, Table),
|
|
%% Execute SQL transaction with remaining records
|
|
output(LServer, IO,
|
|
["begin;\n",
|
|
lists:reverse(SQLs),
|
|
"commit"])
|
|
end).
|
|
|
|
output(LServer, IO, SQL) ->
|
|
case IO of
|
|
odbc ->
|
|
catch ejabberd_odbc:sql_query(LServer, SQL);
|
|
_ ->
|
|
file:write(IO, [SQL, $;, $\n])
|
|
end.
|
|
|
|
record_to_string(#rosteritem{user_host_jid = {User, _Server, {N, D, R} = _JID},
|
|
name = Name,
|
|
subscription = Subscription,
|
|
ask = Ask,
|
|
askmessage = AskMessage}, ServerS) ->
|
|
Username = ejabberd_odbc:escape(User),
|
|
SJID = ejabberd_odbc:escape(exmpp_jid:to_list(N, D, R)),
|
|
Nick = ejabberd_odbc:escape(Name),
|
|
SSubscription = case Subscription of
|
|
both -> "B";
|
|
to -> "T";
|
|
from -> "F";
|
|
none -> "N"
|
|
end,
|
|
SAsk = case Ask of
|
|
subscribe -> "S";
|
|
unsubscribe -> "U";
|
|
both -> "B";
|
|
out -> "O";
|
|
in -> "I";
|
|
none -> "N"
|
|
end,
|
|
SAskMessage =
|
|
case catch ejabberd_odbc:escape(
|
|
binary_to_list(list_to_binary([AskMessage]))) of
|
|
{'EXIT', _Reason} ->
|
|
[];
|
|
SAM ->
|
|
SAM
|
|
end,
|
|
["("
|
|
"'", ServerS, "',"
|
|
"'", Username, "',"
|
|
"'", SJID, "',"
|
|
"'", Nick, "',"
|
|
"'", SSubscription, "',"
|
|
"'", SAsk, "',"
|
|
"'", SAskMessage, "',"
|
|
"'N', '', 'item')"].
|
|
|
|
group_to_string(#rostergroup{user_host_jid = {User, _Server, {N, D, R} = _JID},
|
|
grp = Group}, ServerS) ->
|
|
Username = ejabberd_odbc:escape(User),
|
|
SJID = ejabberd_odbc:escape(exmpp_jid:to_list(N, D, R)),
|
|
["("
|
|
"'", ServerS, "',"
|
|
"'", Username, "',"
|
|
"'", SJID, "',"
|
|
"'", ejabberd_odbc:escape(Group), "')"].
|