diff options
Diffstat (limited to 'net-im')
-rw-r--r-- | net-im/ejabberd/Manifest | 20 | ||||
-rw-r--r-- | net-im/ejabberd/ejabberd-1.1.1-r1.ebuild | 28 | ||||
-rw-r--r-- | net-im/ejabberd/files/digest-ejabberd-1.1.1-r1 | 6 | ||||
-rw-r--r-- | net-im/ejabberd/files/mod_archive.erl | 695 |
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 + + |