summaryrefslogtreecommitdiff
path: root/net-im
diff options
context:
space:
mode:
Diffstat (limited to 'net-im')
-rw-r--r--net-im/ejabberd/Manifest20
-rw-r--r--net-im/ejabberd/ejabberd-1.1.1-r1.ebuild28
-rw-r--r--net-im/ejabberd/files/digest-ejabberd-1.1.1-r16
-rw-r--r--net-im/ejabberd/files/mod_archive.erl695
4 files changed, 735 insertions, 14 deletions
diff --git a/net-im/ejabberd/Manifest b/net-im/ejabberd/Manifest
index 3c84618c..775f201a 100644
--- a/net-im/ejabberd/Manifest
+++ b/net-im/ejabberd/Manifest
@@ -1,20 +1,26 @@
+AUX mod_archive.erl 29560 RMD160 6c628ca4f1d82cd826c2a4d11c87d6b3344f3f24 SHA1 b934e9a9ed1ab7dc700b589177dc9425aa535a6f SHA256 b46976ca86a43cb13ed71009a5fadf7a2ca231e51da83150cdff0d646fbed00f
+MD5 c3c630a783edc22b0d81646f2c2eb829 files/mod_archive.erl 29560
+RMD160 6c628ca4f1d82cd826c2a4d11c87d6b3344f3f24 files/mod_archive.erl 29560
+SHA256 b46976ca86a43cb13ed71009a5fadf7a2ca231e51da83150cdff0d646fbed00f files/mod_archive.erl 29560
AUX mod_presence.diff.gz 46392 RMD160 2cac587223b53a07c4b17cd49047e2e68ee8122e SHA1 bfff32599160dd3a6bc340743bd55fedca2a64ea SHA256 8c3e39fd87b77fb928f801eb4241cdbacc28c8891097a4cee8ea1c962f20f1b2
MD5 13316805ef6031854a78272e9aeadd27 files/mod_presence.diff.gz 46392
RMD160 2cac587223b53a07c4b17cd49047e2e68ee8122e files/mod_presence.diff.gz 46392
SHA256 8c3e39fd87b77fb928f801eb4241cdbacc28c8891097a4cee8ea1c962f20f1b2 files/mod_presence.diff.gz 46392
+DIST check_pam.diff.gz 3865 RMD160 86d6ff263d216c63662ef1078f947f9f7a6c1fce SHA1 756d258a27d145c53b0bac7bb2a333102604127b SHA256 c474adefeefd6e2d18fff531dc4c2f3c149ba003dbd606655656a95ca72ee62a
DIST ejabberd-1.1.1.tar.gz 803278 RMD160 b9c0b7ab3fe1f1b2dce52e1460bba04b313ea534 SHA1 4f23d787afe75c7c866decdff6f539195449776e SHA256 52a97275537073066bd352f5718954f6994b272d1efa51187e17edf0c9b11082
DIST ejabberd-1.1.2.tar.gz 836240 RMD160 e763752e6c5fb46c51b71e265ab2ceda6d043a0d SHA1 9e94bdbc10fee5b781405daf43a0b4abc4dee6c1 SHA256 029129a6bcb5d15dbccc5aa756f61c52692eb6882ec7aad0193aa940b6a20bb6
-EBUILD ejabberd-1.1.1-r1.ebuild 3509 RMD160 05625dedb3492d9fc259cc645b4cc16e41431668 SHA1 464ff922daa36839743699453b7ec7775fd0269c SHA256 ba3bd17997b164cc384b0b2e520ffe2640215d2425dd8f53020f158c06ee4154
-MD5 37294f0a6178bbf3ebc050dc6f0381ae ejabberd-1.1.1-r1.ebuild 3509
-RMD160 05625dedb3492d9fc259cc645b4cc16e41431668 ejabberd-1.1.1-r1.ebuild 3509
-SHA256 ba3bd17997b164cc384b0b2e520ffe2640215d2425dd8f53020f158c06ee4154 ejabberd-1.1.1-r1.ebuild 3509
+DIST mod_presence-0.0.3.tar.gz 51392 RMD160 adb6444dcef2fead52c1510fc247f7af20aa62c7 SHA1 88afa3af72d61608a55f1605d58b6ffd1fc43cfa SHA256 5fc2a97576f33965d07bb1f803a8fe48647479da37cd58333aea6a7e48ebc4a1
+EBUILD ejabberd-1.1.1-r1.ebuild 3886 RMD160 688617ec68dd542758b695d18d57c6d123c5e2f7 SHA1 42051242370093ccff3ecd35fcfa4bda4140ddd3 SHA256 04236fb5a30476d6b05bbfc8be4e0eb4abad407194204fa8729f3481c0e4bd53
+MD5 151e7780af1a89705e11cdae74358821 ejabberd-1.1.1-r1.ebuild 3886
+RMD160 688617ec68dd542758b695d18d57c6d123c5e2f7 ejabberd-1.1.1-r1.ebuild 3886
+SHA256 04236fb5a30476d6b05bbfc8be4e0eb4abad407194204fa8729f3481c0e4bd53 ejabberd-1.1.1-r1.ebuild 3886
EBUILD ejabberd-1.1.2.ebuild 3530 RMD160 fb69bcbe66a3865e20f28e0300b9c58b6cf16a7d SHA1 f40af4462e3dd89739b040b2531836071db0b618 SHA256 1fd15fedb7568f728ef05d680ce44cc0312eaccc6239a08e3dde9248bf7b7260
MD5 a7f6a322b75ed777a19f04b42943379b ejabberd-1.1.2.ebuild 3530
RMD160 fb69bcbe66a3865e20f28e0300b9c58b6cf16a7d ejabberd-1.1.2.ebuild 3530
SHA256 1fd15fedb7568f728ef05d680ce44cc0312eaccc6239a08e3dde9248bf7b7260 ejabberd-1.1.2.ebuild 3530
-MD5 87c80474a3cbcdf51830a748a411f0f4 files/digest-ejabberd-1.1.1-r1 244
-RMD160 bdb61a1e0dd65fa91775a5f3b7c7516dc65bd8ff files/digest-ejabberd-1.1.1-r1 244
-SHA256 d3659c3dbc43cf807fb7309da84ba62393e9257a7a16a3305621ee60f97378a6 files/digest-ejabberd-1.1.1-r1 244
+MD5 6bf0894f239498e7a5bdbaac771cdc75 files/digest-ejabberd-1.1.1-r1 723
+RMD160 d2d272d4488b3eec4fc58eeb9449396f29d783c0 files/digest-ejabberd-1.1.1-r1 723
+SHA256 f57333e151bec1d584876aade6dc4fec76e8deffdd240d7aa2d2ec947d6dbba8 files/digest-ejabberd-1.1.1-r1 723
MD5 a38033cd51c7bb84bc09310952063222 files/digest-ejabberd-1.1.2 244
RMD160 d8117ebc19953eb40137b4ebca37d7577ff9e5b6 files/digest-ejabberd-1.1.2 244
SHA256 e1ff34ed81425e1b009e60d29d7f6b9bdeff7cdba4407372cfdb8e40ca00efcb files/digest-ejabberd-1.1.2 244
diff --git a/net-im/ejabberd/ejabberd-1.1.1-r1.ebuild b/net-im/ejabberd/ejabberd-1.1.1-r1.ebuild
index 3abf39c1..586f4fce 100644
--- a/net-im/ejabberd/ejabberd-1.1.1-r1.ebuild
+++ b/net-im/ejabberd/ejabberd-1.1.1-r1.ebuild
@@ -2,7 +2,7 @@
# Distributed under the terms of the GNU General Public License v2
# $Header: /var/cvsroot/gentoo-x86/net-im/ejabberd/ejabberd-1.1.1.ebuild,v 1.2 2006/08/01 04:26:09 tsunam Exp $
-inherit eutils multilib ssl-cert versionator
+inherit eutils multilib ssl-cert versionator autotools
JABBER_ETC="/etc/jabber"
JABBER_RUN="/var/run/jabber"
@@ -11,32 +11,45 @@ JABBER_LOG="/var/log/jabber"
DESCRIPTION="The Erlang Jabber Daemon"
HOMEPAGE="http://ejabberd.jabber.ru/"
-SRC_URI="http://process-one.net/en/projects/${PN}/download/${PV}/${P}.tar.gz"
+SRC_URI="http://process-one.net/en/projects/${PN}/download/${PV}/${P}.tar.gz
+ pam? ( http://beber.meleeweb.net/jabber/check_pam.diff.gz )
+ mod_presence? ( http://www.goryachev.org/jabber/mod_presence-0.0.3.tar.gz )"
LICENSE="GPL-2"
SLOT="0"
KEYWORDS="x86 ~amd64"
-IUSE="mod_irc mod_muc mod_pubsub mod_presence ldap odbc web"
+IUSE="mod_archive mod_irc mod_muc mod_pubsub mod_presence ldap odbc pam web"
DEPEND=">=net-im/jabber-base-0.01
>=dev-libs/expat-1.95
>=dev-lang/erlang-10.2.0
odbc? ( dev-db/unixODBC )
- ldap? ( =net-nds/openldap-2* )"
+ ldap? ( =net-nds/openldap-2* )
+ pam? ( sys-libs/pam)"
+RESTRICT="nomirror"
PROVIDE="virtual/jabber-server"
S=${WORKDIR}/${P}/src
src_unpack() {
unpack ${A}
+ cd ${S}
if use mod_presence; then
- cd ${S}/..
- gunzip ${FILESDIR}/mod_presence.diff.gz -c > mod_presence.diff
- epatch mod_presence.diff
+ epatch ${WORKDIR}/mod_presence/mod_presence.diff
+ cp -r ${WORKDIR}/mod_presence/pixmaps .
+ fi
+
+ if use pam; then
+ patch -p0 < ${WORKDIR}/check_pam.diff
+ fi
+
+ if use mod_archive; then
+ cp ${FILESDIR}/mod_archive.erl .
fi
}
src_compile() {
+ eautoconf
econf ${myconf} \
$(use_enable mod_irc) \
$(use_enable ldap eldap) \
@@ -45,6 +58,7 @@ src_compile() {
$(use_enable ssl tls) \
$(use_enable web) \
$(use_enable odbc) \
+ $(use_enable mod_presence) \
|| die "econf failed"
emake || die "compiling ejabberd core failed"
diff --git a/net-im/ejabberd/files/digest-ejabberd-1.1.1-r1 b/net-im/ejabberd/files/digest-ejabberd-1.1.1-r1
index fdeac323..b4b47cb1 100644
--- a/net-im/ejabberd/files/digest-ejabberd-1.1.1-r1
+++ b/net-im/ejabberd/files/digest-ejabberd-1.1.1-r1
@@ -1,3 +1,9 @@
+MD5 6b78cb824b35a7f651ab33790cf841c7 check_pam.diff.gz 3865
+RMD160 86d6ff263d216c63662ef1078f947f9f7a6c1fce check_pam.diff.gz 3865
+SHA256 c474adefeefd6e2d18fff531dc4c2f3c149ba003dbd606655656a95ca72ee62a check_pam.diff.gz 3865
MD5 ef6fae4a3f9c7f807f21e9cd3dae195b ejabberd-1.1.1.tar.gz 803278
RMD160 b9c0b7ab3fe1f1b2dce52e1460bba04b313ea534 ejabberd-1.1.1.tar.gz 803278
SHA256 52a97275537073066bd352f5718954f6994b272d1efa51187e17edf0c9b11082 ejabberd-1.1.1.tar.gz 803278
+MD5 fa9a979f5e3530a2b3143416934d989b mod_presence-0.0.3.tar.gz 51392
+RMD160 adb6444dcef2fead52c1510fc247f7af20aa62c7 mod_presence-0.0.3.tar.gz 51392
+SHA256 5fc2a97576f33965d07bb1f803a8fe48647479da37cd58333aea6a7e48ebc4a1 mod_presence-0.0.3.tar.gz 51392
diff --git a/net-im/ejabberd/files/mod_archive.erl b/net-im/ejabberd/files/mod_archive.erl
new file mode 100644
index 00000000..88f0c8a7
--- /dev/null
+++ b/net-im/ejabberd/files/mod_archive.erl
@@ -0,0 +1,695 @@
+%%%----------------------------------------------------------------------
+%%% File : mod_archive.erl
+%%% Author : Olivier Goffart <ogoffar@kde.org>
+%%% Purpose : Message Archiving (JEP-0136)
+%%% Created : 19 Aug 2006
+%%%----------------------------------------------------------------------
+
+%% Version 0.0.1 2006-08-19
+%% Version 0.0.2 2006-08-21
+%% Version 0.0.3 2006-08-22
+
+
+%% Options:
+%% save_default -> true | false if messages are stored by default or not
+%% session_duration -> time in secondes before the timeout of a session
+
+
+-module(mod_archive).
+-author('ogoffart@kde.org').
+
+-vsn('0.0.3.5').
+-behaviour(gen_mod).
+
+-export([start/2, stop/1, loop/3 ,
+ remove_user/2, send_packet/3, receive_packet/4,
+ process_iq/3, process_local_iq/3]).
+
+-include("ejabberd.hrl").
+-include("jlib.hrl").
+
+
+-define(PROCNAME, ejabberd_mod_archive).
+-define(NS_ARCHIVE, "http://jabber.org/protocol/archive").
+-define(INFINITY, calendar:datetime_to_gregorian_seconds({{2038,1,19},{0,0,0}}) ).
+
+-define(MYDEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n",
+ [calendar:local_time(),?MODULE,?LINE]++Args)).
+
+
+%NOTE i was not sure what format to adopt for archive_option. otr_list is unused
+-record(archive_options, {us, default=unset , save_list = [] , nosave_list =[] , otr_list = [] }).
+%-record(archive_options, {usj, us , jid , type , value}).
+-record(archive_message, { usjs , us , jid , start , message_list=[] , subject=""}).
+-record(msg , {dirrection , secs, body }).
+
+start(Host, Opts) ->
+ IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
+ Save_Default = gen_mod:get_opt(save_default, Opts, false),
+ Session_Duration = gen_mod:get_opt(session_duration, Opts, 1300),
+ mnesia:create_table(archive_options,[{disc_copies, [node()]}, {attributes, record_info(fields, archive_options)}]),
+ mnesia:create_table(archive_message,[{disc_copies, [node()]}, {attributes, record_info(fields, archive_message)}]),
+% mnesia:add_table_index(archive_options, us),
+ mnesia:add_table_index(archive_message, us),
+ ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50),
+ gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_ARCHIVE, ?MODULE, process_iq, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_ARCHIVE, ?MODULE, process_local_iq, IQDisc),
+ ejabberd_hooks:add(user_send_packet, Host, ?MODULE, send_packet, 90),
+ ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, receive_packet, 90),
+ catch mod_disco:register_feature(Host, ?NS_ARCHIVE ++ "#manage"),
+ catch mod_disco:register_feature(Host, ?NS_ARCHIVE ++ "#save"),
+ catch mod_disco:register_feature(Host, ?NS_ARCHIVE ++ "#manual"),
+ register(gen_mod:get_module_proc(Host, ?PROCNAME), spawn(?MODULE, loop, [Host, [], { Save_Default, Session_Duration }])).
+
+stop(Host) ->
+ ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_ARCHIVE),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_ARCHIVE),
+ ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, send_packet, 90),
+ ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, receive_packet, 90),
+ catch mod_disco:unregister_feature(Host, ?NS_ARCHIVE ++ "#manage"),
+ catch mod_disco:unregister_feature(Host, ?NS_ARCHIVE ++ "#save"),
+ catch mod_disco:unregister_feature(Host, ?NS_ARCHIVE ++ "#manual"),
+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
+ Proc ! stop,
+ {wait, Proc}.
+
+
+%Workarounf the fact that if the client send <iq type='get'> it end up like <iq type='get' from='u@h' to = 'u@h'>
+process_iq(From, To, IQ) ->
+ #iq{sub_el = SubEl} = IQ,
+ #jid{lserver = LServer, luser = LUser} = To,
+ #jid{luser = FromUser} = From,
+ case {LUser , LServer , lists:member(LServer, ?MYHOSTS)} of
+ { FromUser , _ , true } ->
+ process_local_iq(From, To, IQ);
+ { "" , _ , true } ->
+ process_local_iq(From, To, IQ);
+ { "" , "" , _ } ->
+ process_local_iq(From, To, IQ);
+ _ ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
+ end.
+
+process_local_iq(From, To, #iq{sub_el = SubEl} = IQ) ->
+ case lists:member(From#jid.lserver, ?MYHOSTS) of
+ false ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
+ true ->
+ {xmlelement, Name, _Attrs, _Els} = SubEl,
+ case Name of
+ "save" -> process_local_iq_save( From, To , IQ ) ;
+% "otr" -> process_local_iq_otr( From, To , IQ ) ;
+ "list" -> process_local_iq_list( From , To , IQ ) ;
+ "retrieve" -> process_local_iq_retrieve( From , To , IQ );
+ "store" -> process_local_iq_store( From , To , IQ );
+ "remove" -> process_local_iq_remove( From , To , IQ );
+ _ -> IQ#iq{type = error, sub_el = [SubEl, ?ERR_FEATURE_NOT_IMPLEMENTED]}
+ end
+ end.
+
+
+remove_user(User, Server) ->
+ LUser = jlib:nodeprep(User),
+ LServer = jlib:nameprep(Server),
+ US = {LUser, LServer},
+ F = fun() ->
+ lists:foreach(fun(R) ->
+ mnesia:delete_object(R)
+ end,
+ mnesia:index_read(archive_message, US, #archive_message.us))
+ end,
+ mnesia:transaction(F),
+ F1 = fun() ->
+ mnesia:delete({archive_options, US})
+ end,
+ mnesia:transaction(F1).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 3 Automated archiving
+%%
+
+send_packet(From, To, P) ->
+ Host = From#jid.lserver,
+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
+ Proc ! {addlog, {to, From#jid.luser, Host , To, P}}.
+
+receive_packet(_JID, From, To, P) ->
+ Host = To#jid.lserver,
+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
+ Proc ! {addlog, {from, To#jid.luser, Host , From, P}}.
+
+
+% storage loop. Storages is a list containing 'open' storage element
+% storage element are in the form: { Timestamp , { LUser , LServer , Jid , Start } }
+loop(Host , Storages, { Save_Default, Session_Duration } ) ->
+ receive
+ {addlog, { Dirrection , LUser , LServer , Jid , P }} ->
+ NStorages = case catch should_store_jid( LUser , LServer , Jid , Save_Default) of
+ false -> Storages;
+ true ->
+ case parse_message(P) of
+ ignore -> Storages;
+ Body -> catch do_log( Storages , LUser, LServer, Jid , Dirrection, Body , Session_Duration )
+ end
+ end,
+ loop(Host, NStorages, { Save_Default, Session_Duration });
+ {get_save_default, Pid} ->
+ Pid ! Save_Default,
+ loop(Host, Storages, { Save_Default, Session_Duration });
+ stop ->
+ ok;
+ _ ->
+ loop(Host, Storages, { Save_Default, Session_Duration })
+ end.
+
+
+
+% parce a message and return the body string if sicessfull, return ignore if the message should not be stored
+parse_message({xmlelement , "message" , _ , _ } = Packet) ->
+ case xml:get_subtag(Packet, "body") of
+ false -> ignore;
+ Body_xml ->
+ case xml:get_tag_attr_s("type", Packet) of
+ "groupchat" -> ignore ;
+ _ -> xml:get_tag_cdata(Body_xml)
+ end
+ end;
+parse_message(_) -> ignore.
+
+% archive the message Body return the list of new Storages
+% Storages: a list of open storages key (usjs)
+% LUser , LServer : the local user's information
+% Jid : the contact's jid
+% Body : the message body
+do_log( Storages, LUser, LServer, Jid , Dirrection, Body , Session_Duration ) ->
+ NStorages = smart_find_storage( LUser , LServer, Jid , Storages , get_timestamp() + Session_Duration ),
+ [{Tm , { _ , _ , _ , Start } = Key } | _ ] = NStorages,
+ Message = #msg{dirrection=Dirrection , secs=(Tm-Start), body = Body },
+ mnesia:transaction( fun() ->
+ NE = case mnesia:read({archive_message, Key }) of
+ [] ->
+ #archive_message{ usjs= Key ,
+ us = { LUser , LServer } ,
+ jid = jlib:jid_tolower(jlib:jid_remove_resource(Jid)) ,
+ start = Tm ,
+ message_list = [ Message ] };
+ [E] -> E#archive_message{ message_list=lists:append( E#archive_message.message_list , [ Message ] ) }
+ end,
+ mnesia:write( NE )
+ end),
+ NStorages.
+
+%find a storage for Jid and move it on the begin on the storage list, if none are found, a new storage is created, old storages element are removed
+smart_find_storage( LUser, LServer, Jid , [ C | Tail ] , TimeStampLimit ) ->
+ NGid=jlib:jid_remove_resource(jlib:jid_tolower(Jid)),
+ case C of
+ { _ , { LUser , LServer , NGid , _ } = St } ->
+ [ { get_timestamp() , St } | Tail ];
+ { Tm , _ } ->
+ if Tm > TimeStampLimit ->
+ smart_find_storage( LUser, LServer, Jid , [ ] , TimeStampLimit );
+ true ->
+ [ UJ | NT ] = smart_find_storage( LUser , LServer , Jid , Tail , TimeStampLimit ),
+ [ UJ | [ C | NT ] ]
+ end
+ end;
+
+
+smart_find_storage( LUser, LServer, Jid , [ ] , _Limit ) ->
+ Tm = get_timestamp() ,
+ [ { Tm , { LUser, LServer , jlib:jid_tolower(jlib:jid_remove_resource(Jid)) , Tm } } ].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 3.1 Save Mode
+%%
+
+process_local_iq_save(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
+ Result = case Type of
+ set ->
+ {xmlelement, _Name, _Attrs, Els} = SubEl,
+ process_save_set(From#jid.luser , From#jid.lserver, Els);
+ get ->
+ process_save_get(From#jid.luser , From#jid.lserver)
+ end,
+ case Result of
+ {result , R } ->
+ IQ#iq{type = result, sub_el = [R] };
+ ok ->
+ broadcast_iq(From, IQ#iq{type = set, sub_el=[SubEl]}),
+ IQ#iq{type = result, sub_el = [] };
+ {error , E } ->
+ IQ#iq{type = error, sub_el = [SubEl , E]};
+ _ ->
+ IQ#iq{type = error, sub_el = [SubEl , ?ERR_INTERNAL_SERVER_ERROR]}
+ end.
+
+
+
+
+% return {error , xmlelement} or {result , xmlelement }
+process_save_get(LUser, LServer) ->
+ case catch mnesia:dirty_read(archive_options, {LUser, LServer}) of
+ {'EXIT', _Reason} ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR};
+ [] ->
+ {result, {xmlelement , "save" , [{"xmlns", ?NS_ARCHIVE}] , [default_element(LServer)]}};
+ [#archive_options{default = Default, save_list = Save_list , nosave_list = Nosave_list}] ->
+ LItems = lists:append(
+ lists:map( fun(J) ->
+ {xmlelement, "item", [{"jid", jlib:jid_to_string(J)}, {"save","true"}] , []}
+ end, Save_list),
+ lists:map( fun(J) ->
+ {xmlelement, "item", [{"jid", jlib:jid_to_string(J)}, {"save","false"}] , []}
+ end, Nosave_list) ),
+ DItem = case Default of
+ true ->
+ {xmlelement, "default",[{"save", "true"} ], []};
+ false ->
+ {xmlelement, "default",[{"save", "false"} ], []};
+ _ ->
+ default_element(LServer)
+ end,
+ {result, {xmlelement , "save" , [{"xmlns", ?NS_ARCHIVE}] , [DItem | LItems] } }
+ end.
+
+%return the <default save='unset' service='...' /> element
+default_element(Host) ->
+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
+ Proc ! {get_save_default , self()},
+ receive
+ Service ->
+ {xmlelement, "default",[{"save", "unset"} , {"service" , atom_to_list(Service)}], []}
+ end.
+
+
+% return {error , xmlelement} or {result , xmlelement } or ok
+process_save_set(LUser, LServer, Elms) ->
+ F = fun() ->
+ NE = case mnesia:read({archive_options, {LUser, LServer}}) of
+ [] ->
+ #archive_options{ us = { LUser , LServer } };
+ [E] ->
+ E
+ end,
+ SNE=transaction_parse_save_elem(NE,Elms),
+ case SNE of
+ {error , _} -> SNE;
+ _ -> mnesia:write( SNE )
+ end
+ end,
+ case mnesia:transaction(F) of
+ {atomic, {error, _} = Error} ->
+ Error;
+ {atomic, _ } ->
+ ok;
+ _ -> {error, ?ERR_INTERNAL_SERVER_ERROR}
+ end.
+
+
+% transaction_parse_save_elem( archive_options , ListOfXmlElement ) -> #archive_options
+% parse the list of xml element, and modify the given archive_option
+transaction_parse_save_elem(Options, [ {xmlelement , "default" , Attrs , _ } | Tail]) ->
+ V = case xml:get_attr_s("save", Attrs) of
+ "true" -> true;
+ "false" -> false;
+ _ -> unset
+ end,
+ transaction_parse_save_elem( Options#archive_options{default = V} , Tail );
+
+transaction_parse_save_elem(Options, [ {xmlelement , "item" , Attrs , _} | Tail]) ->
+ case jlib:string_to_jid(xml:get_attr_s("jid", Attrs)) of
+ error -> { error , ?ERR_JID_MALFORMED };
+ #jid{luser=LUser,lserver=LServer,lresource=LResource} ->
+ Jid = {LUser, LServer, LResource },
+ case xml:get_attr_s("save", Attrs) of
+ "true" ->
+ transaction_parse_save_elem( Options#archive_options{save_list=[Jid | lists:delete(Jid,Options#archive_options.save_list) ] ,
+ nosave_list=lists:delete(Jid,Options#archive_options.nosave_list)
+ } , Tail );
+ "false" ->
+ transaction_parse_save_elem( Options#archive_options{save_list=lists:delete(Jid,Options#archive_options.save_list) ,
+ nosave_list=[Jid | lists:delete(Jid,Options#archive_options.nosave_list) ]
+ } , Tail );
+ _ ->
+ transaction_parse_save_elem( Options#archive_options{save_list=lists:delete(Jid,Options#archive_options.save_list ) ,
+ nosave_list=lists:delete(Jid,Options#archive_options.nosave_list)
+ } , Tail )
+ end
+ end;
+
+transaction_parse_save_elem(Options, [ ]) -> Options;
+transaction_parse_save_elem(Options, [ _ | Tail ]) -> transaction_parse_save_elem(Options, Tail).
+
+
+broadcast_iq(#jid{luser = User, lserver = Server}, IQ) ->
+ Fun = fun(Resource) ->
+ ejabberd_router:route(
+ jlib:make_jid("", Server, ""),
+ jlib:make_jid(User, Server, Resource),
+ jlib:iq_to_xml(IQ#iq{id="push"}))
+ end,
+ lists:foreach(Fun, ejabberd_sm:get_user_resources(User,Server)).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 3.1 Off-the-Record Mode
+%%
+
+%TODO
+
+
+
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Utility function
+
+% Return true if LUser@LServer should log message for the contact Jid
+should_store_jid( LUser , LServer , Jid , Service_Default) ->
+ case catch mnesia:dirty_read(archive_options, {LUser, LServer}) of
+ [#archive_options{default = Default, save_list = Save_list , nosave_list = Nosave_list}] ->
+ Jid_t = jlib:jid_tolower(Jid),
+ Jid_b = jlib:jid_remove_resource(Jid_t),
+ A = lists:member(Jid_t, Save_list),
+ B = lists:member(Jid_t, Nosave_list),
+ C = lists:member(Jid_b, Save_list),
+ D = lists:member(Jid_b, Nosave_list),
+ if A -> true;
+ B -> false;
+ C -> true;
+ D -> false;
+ Default == true -> true;
+ Default == false -> false;
+ true -> Service_Default
+ end;
+ _ -> Service_Default
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 4. Manual Archiving
+%%
+
+
+process_local_iq_store(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
+ #jid{luser = LUser, lserver = LServer} = From,
+ case Type of
+ get ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
+ set ->
+ case parse_store_element ( LUser, LServer, SubEl ) of
+ { error , E } -> IQ#iq{type = error, sub_el = [SubEl , E ]} ;
+ Collection ->
+ case mnesia:transaction(fun() -> mnesia:write( Collection ) end) of
+ {atomic, _ } ->
+ IQ#iq{type = result , sub_el=[]};
+ _ ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
+ end
+ end
+ end.
+
+
+
+% return a #archive_message StoreElem is an xmlelement , or return {error, E}
+parse_store_element(LUser, LServer, {xmlelement, "store" , Attrs , SubEls } ) ->
+ case index_from_argument(LUser, LServer, Attrs) of
+ {error , E } -> {error , E} ;
+ { LUser , LServer , Jid , Start } = Index ->
+ Messages = parse_store_element_sub( SubEls ) ,
+ #archive_message{ usjs = Index ,
+ us = { LUser, LServer} ,
+ jid = Jid ,
+ start = Start,
+ subject = xml:get_attr_s("subject" , Attrs ) ,
+ message_list = Messages }
+ end.
+
+parse_store_element_sub( [ {xmlelement , Dir , _ , _ } = E | Tail ] ) ->
+ [ #msg{ dirrection=list_to_atom(Dir) , secs= list_to_integer(xml:get_tag_attr_s("secs" , E)), body=xml:get_tag_cdata(xml:get_subtag(E,"body")) } |
+ parse_store_element_sub( Tail ) ];
+
+parse_store_element_sub( [ ] ) -> [];
+parse_store_element_sub( [ _ | Tail ] ) -> parse_store_element_sub( Tail ).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 5. Archive Management
+%%
+
+
+process_local_iq_list(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
+ #jid{luser = LUser, lserver = LServer} = From,
+ case Type of
+ set ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
+ get ->
+ {xmlelement, _ , Attrs, _} = SubEl,
+ MaxItems = case xml:get_attr("maxitems" , Attrs) of
+ false -> all;
+ {value , V } -> list_to_integer(V)
+ end,
+ Result = case parse_root_argument(Attrs) of
+ {error , E } -> {error , E};
+ {interval, Start , Stop } -> get_list(LUser, LServer , Start, Stop, '_' , MaxItems);
+ {interval, Start , Stop , Jid } -> get_list(LUser, LServer , Start, Stop, Jid , MaxItems);
+ _ -> { error , ?ERR_BAD_REQUEST }
+ end,
+
+ case Result of
+ {ok, Items , Partial} ->
+ Zero = calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}),
+ Fun = fun(A) ->
+ Seconds= A#archive_message.start-Zero,
+ Start2 = jlib:now_to_utc_string( {Seconds div 1000000, Seconds rem 1000000, 0} ) ,
+ Args0 = [{"with", jlib:jid_to_string(A#archive_message.jid)} , {"start" , Start2 } ] ,
+ Args = case A#archive_message.subject of
+ "" -> Args0;
+ Subject -> [ {"subject",Subject} | Args0 ]
+ end,
+ {xmlelement, "store", Args , [] }
+ end,
+ MIA = case Partial of
+ false -> [];
+ true -> [{"partial" , "true"}]
+ end,
+ IQ#iq{type = result, sub_el = [{xmlelement, "list", [{"xmlns", ?NS_ARCHIVE} | MIA ], lists:map(Fun, lists:keysort(3, Items)) }] };
+ {error, R} ->
+ IQ#iq{type = error, sub_el = [SubEl , R]}
+ end
+ end.
+
+
+
+
+process_local_iq_retrieve(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
+ #jid{luser = LUser, lserver = LServer} = From,
+ case Type of
+ set ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
+ get ->
+ {xmlelement, _ , Attrs , _ } = SubEl,
+ case index_from_argument(LUser, LServer, Attrs ) of
+ { error , E } -> IQ#iq{type = error, sub_el = [SubEl , E ]} ;
+ Index ->
+ case retrieve_collection( Index ) of
+ {error, Err } -> IQ#iq{type = error, sub_el = [SubEl , Err]};
+ Store -> IQ#iq{type = result, sub_el = [Store] }
+ end
+ end
+ end.
+
+
+retrieve_collection(Index) ->
+ case get_collection(Index) of
+ {error, E} ->
+ {error, E};
+ A ->
+ Zero = calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}),
+ Seconds = A#archive_message.start-Zero,
+ Start2 = jlib:now_to_utc_string( {Seconds div 1000000, Seconds rem 1000000, 0} ) ,
+ Args0 = [{"xmlns", ?NS_ARCHIVE} , {"with", jlib:jid_to_string(A#archive_message.jid)} , {"start" , Start2 } ] ,
+ Args = case A#archive_message.subject of
+ "" -> Args0;
+ Subject -> [ Subject | Args0 ]
+ end,
+ Format_Fun = fun(Elem) ->
+ {xmlelement, atom_to_list( Elem#msg.dirrection) ,
+ [ {"secs" , integer_to_list(Elem#msg.secs) } ] ,
+ [ {xmlelement , "body" , [] , [{xmlcdata, Elem#msg.body }] } ] }
+ end,
+ {xmlelement, "store", Args , lists:map(Format_Fun, A#archive_message.message_list ) }
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 5.3 Removing a Collection
+%%
+
+
+process_local_iq_remove(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
+ #jid{luser = LUser, lserver = LServer} = From,
+ case Type of
+ get ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
+ set ->
+ {xmlelement, _, Attrs, _} = SubEl,
+ Result = case parse_root_argument(Attrs) of
+ {error , E } -> IQ#iq{type = error, sub_el = [SubEl, E ] } ;
+ {interval, Start , Stop } -> process_remove_interval(LUser, LServer , Start, Stop, '_');
+ {interval, Start , Stop , Jid } -> process_remove_interval(LUser, LServer , Start, Stop, Jid);
+ {index , Jid, Start } -> process_remove_index({LUser, LServer , Jid, Start})
+ end,
+ case Result of
+ { error , Ee } -> IQ#iq{type = error, sub_el = [SubEl , Ee ]} ;
+ ok -> IQ#iq{type = result , sub_el=[]}
+ end
+ end.
+
+process_remove_index( Index ) ->
+ case mnesia:transaction(fun() -> mnesia:delete({archive_message, Index}) end) of
+ {atomic, _} ->
+ ok;
+ {aborted, _} ->
+ {error, ?ERR_ITEM_NOT_FOUND}
+ end.
+
+process_remove_interval(LUser, LServer , Start, End, With) ->
+ Fun = fun() ->
+ Pat = #archive_message{ usjs= '_' , us = { LUser, LServer } , jid= With ,
+ start='$1' , message_list='_' , subject = '_' },
+ Guard = [{'>=', '$1', Start},{'<', '$1', End}],
+
+ lists:foreach(fun(R) -> mnesia:delete_object(R) end,
+ mnesia:select(archive_message, [{Pat, Guard, ['$_']}]) )
+ end,
+
+ case mnesia:transaction( Fun ) of
+ {atomic, _} ->
+ ok;
+ {aborted, _} ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR}
+ end.
+
+
+
+% return { ok, [ { #archive_message } ] } or { error , xmlelement }
+% With is a tuple Jid, or '_'
+get_list(LUser, LServer , Start, End, With , MaxItems ) ->
+ case mnesia:transaction(fun() ->
+ Pat = #archive_message{ usjs= '_' , us = { LUser, LServer } , jid= With ,
+ start='$1' , message_list='_' , subject = '_' },
+ Guard = [{'>=', '$1', Start},{'<', '$1', End}],
+ case MaxItems of
+ all -> mnesia:select(archive_message, [{Pat, Guard, ['$_']}]) ;
+ _ -> mnesia:select(archive_message, [{Pat, Guard, ['$_']}],MaxItems , read)
+ end
+
+ end) of
+ {atomic, {Result, _ }} ->
+ {ok, Result , true}; %FIXME: how to know if it's partial ?
+ {atomic, Result} ->
+ {ok, Result , false};
+ {aborted, _} ->
+ {error, ?ERRT_INTERNAL_SERVER_ERROR("", "plop" )}
+ end.
+
+
+% Index is {LUser, LServer , With , Start}
+get_collection( Index ) ->
+ case catch mnesia:dirty_read(archive_message, Index) of
+ {'EXIT', _Reason} ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR};
+ [] ->
+ {error, ?ERR_ITEM_NOT_FOUND};
+ [C] -> C
+ end.
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Utility
+
+
+% return either {error , Err } or {LUser, LServer , Jid , Start}
+index_from_argument(LUser, LServer, Attrs ) ->
+ case parse_root_argument( Attrs ) of
+ { error , E } -> { error , E } ;
+ { index , Jid , Start } -> {LUser, LServer , Jid , Start};
+ _ -> { error, ?ERR_BAD_REQUEST }
+ end.
+
+%parse commons arguments of root elements
+parse_root_argument( Attrs ) ->
+ case parse_root_argument_aux(Attrs , { undefined , undefined , undefined} ) of
+ {error , E } -> {error , E};
+ {{ok,Jid } , {ok,Start} , undefined } -> { index , Jid , Start };
+ {{ok,Jid } , undefined , undefined } -> { interval , 0 , ?INFINITY , Jid };
+ {{ok,Jid } , {ok,Start} , {ok,Stop} } -> { interval , Start , Stop , Jid };
+ {undefined , {ok,Start} , {ok,Stop} } -> { interval , Start , Stop };
+ {undefined , undefined , undefined } -> { interval , 0 , ?INFINITY };
+ _ -> {error, ?ERR_BAD_REQUEST}
+ end.
+
+parse_root_argument_aux([{"with", JidStr} | Tail ] , { _ , AS , AE} ) ->
+ case jlib:string_to_jid( JidStr ) of
+ error -> { error , ?ERR_JID_MALFORMED };
+ JidS ->
+ Jid = jlib:jid_tolower(JidS),
+ parse_root_argument_aux( Tail , { {ok , Jid} , AS , AE } )
+ end;
+parse_root_argument_aux([{"start", Str} | Tail ] , { AW , _ , AE} ) ->
+ case jlib:datetime_string_to_timestamp( Str ) of
+ undefined -> { error , ?ERR_BAD_REQUEST };
+ No ->
+ Val = calendar:datetime_to_gregorian_seconds(calendar:now_to_datetime(No)),
+ parse_root_argument_aux( Tail , { AW , {ok , Val} , AE } )
+ end;
+parse_root_argument_aux([{"end", Str} | Tail ] , { AW , AS , _} ) ->
+ case jlib:datetime_string_to_timestamp( Str ) of
+ undefined -> { error , ?ERR_BAD_REQUEST };
+ No ->
+ Val = calendar:datetime_to_gregorian_seconds(calendar:now_to_datetime(No)),
+ parse_root_argument_aux( Tail , { AW , AS , {ok , Val} } )
+ end;
+parse_root_argument_aux([ _ | Tail ] , A ) ->
+ parse_root_argument_aux( Tail , A );
+parse_root_argument_aux([ ] , A ) -> A.
+
+
+
+get_timestamp() -> calendar:datetime_to_gregorian_seconds(calendar:local_time()).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% NOTE ABOUT THE JEP-0136
+% - errors
+% * when setting <save/> , how to revert to the default, for a contact or for the default
+% * when setting <save/> , is it possible to have several element (<default/> , <item/>) i assume yes
+% * It is STRONGLY RECOMMENDED to use a bare jid. but if a full jid is used, does the resource count when doing comparison ?
+% * the name attribute of the <form/> element is missing in the shema Example 42
+% * the secs argument is required, and the utc is not there in the sheme Example 25
+% * <store/> may have partial = "true" but how to get the tail ? and how to specify the mex number
+% and before example 35 s|<list/>|<store/>|
+% * in the schema , the <remove> element has required attribute, => example 40
+% * How many time should an otr session persist ?
+%
+% - opinions
+% * I don't like the format using secs='' and why also support utc ?
+% * There is very few things to sort, only the body ? not the message type and all others elements ?
+% * There is no syncronisation mechanisme ( a way to know all element modified (added, changed, or removed) after a given date)
+% * The client should be able to disable otr support of the server (if he want the message to be logged)
+% *
+% * comment juste avoir les derniers message du contact
+
+