public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Jeffrey C. Ollie <jcollie@fedoraproject.org>
To: git-commits@fedoraproject.org
Subject: [rpms/ejabberd] f45: auto-import ejabberd-1.1.1-7.fc5 on branch devel from
Date: Wed, 23 Sep 2026 19:06:29 GMT [thread overview]
Message-ID: <179019038904.1.3746773836589880712.rpms-ejabberd-73b15bb9533c@fedoraproject.org> (raw)
A new commit has been pushed.
Repo : rpms/ejabberd
Branch : f45
Commit : 73b15bb9533c40510176c67b8d29d61755e6aee7
Author : Jeffrey C. Ollie <jcollie@fedoraproject.org>
Date : 2006-06-23T12:40:03+00:00
Stats : +1984/-0 in 10 file(s)
URL : https://src.fedoraproject.org/rpms/ejabberd/c/73b15bb9533c40510176c67b8d29d61755e6aee7?branch=f45
Log:
auto-import ejabberd-1.1.1-7.fc5 on branch devel from
ejabberd-1.1.1-7.fc5.src.rpm
---
diff --git a/.cvsignore b/.cvsignore
index e69de29..d206494 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -0,0 +1 @@
+ejabberd-1.1.1.tar.gz
diff --git a/ejabberd.init b/ejabberd.init
new file mode 100644
index 0000000..6384a6d
--- /dev/null
+++ b/ejabberd.init
@@ -0,0 +1,69 @@
+#!/bin/bash
+#
+# ejabberd Starts, Stops and Reloads ejabberd.
+#
+# chkconfig: - 40 60
+# description: ejabberd
+# processname: ejabberd
+# pidfile: /var/run/ejabberd.pid
+
+. /etc/rc.d/init.d/functions
+
+start() {
+ echo -n $"Starting ejabberd: "
+ daemon --user=ejabberd --check=ejabberd \
+ "erl" "-pa @libdir@/ejabberd-@version@/ebin \
+ -sname ejabberd \
+ -s ejabberd \
+ -ejabberd config \\\"/etc/ejabberd/ejabberd.cfg\\\" \
+ log_path \\\"/var/log/ejabberd/ejabberd.log\\\" \
+ -sasl sasl_error_logger \\{file,\\\"/var/log/ejabberd/sasl.log\\\"\\} \
+ -mnesia dir \\\"/var/lib/ejabberd/spool\\\" \
+ -kernel inetrc \\\"/etc/ejabberd/inetrc\\\" \
+ -detached"
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/ejabberd
+ echo
+ return $RETVAL
+}
+
+stop() {
+ # Stop daemons.
+ echo -n "Shutting down ejabberd: "
+ runuser -s /bin/bash - ejabberd -c "erl -pa @libdir@/ejabberd-@version@/ebin -noinput -sname ejabberdctl -s ejabberd_ctl -extra ejabberd@`hostname -s` stop" && success || failure
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/ejabberd
+ echo
+ return $RETVAL
+}
+
+restart() {
+ stop
+ start
+}
+
+# See how we were called.
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ restart
+ ;;
+ condrestart)
+ [ -f /var/lock/subsys/ejabberd ] && restart || :
+ ;;
+ status)
+ runuser -s /bin/bash - ejabberd -c "erl -pa @libdir@/ejabberd-@version@/ebin -noinput -sname ejabberdctl -s ejabberd_ctl -extra ejabberd@`hostname -s` status"
+ ;;
+ *)
+ echo "Usage: ejabberd {start|stop|restart|reload|condrestart|status}"
+ exit 1
+esac
+
+exit $?
+
+
diff --git a/ejabberd.logrotate b/ejabberd.logrotate
new file mode 100644
index 0000000..37319d8
--- /dev/null
+++ b/ejabberd.logrotate
@@ -0,0 +1,9 @@
+/var/log/ejabberd/ejabberd.log /var/log/ejabberd/sasl.log {
+ missingok
+ notifempty
+ create 0640 ejabberd ejabberd
+ sharedscripts
+ postrotate
+ runuser -s /bin/bash - ejabberd -c "erl -pa @libdir@/ejabberd-@version@/ebin -noinput -sname ejabberdctl -s ejabberd_ctl -extra ejabberd@`hostname -s` reopen-log >/dev/null 2>/dev/null || true
+ endscript
+}
diff --git a/ejabberd.spec b/ejabberd.spec
new file mode 100644
index 0000000..2137a77
--- /dev/null
+++ b/ejabberd.spec
@@ -0,0 +1,175 @@
+Name: ejabberd
+Version: 1.1.1
+Release: 7%{?dist}
+Summary: A distributed, fault-tolerant Jabber/XMPP server
+
+Group: Applications/Internet
+License: GPL
+URL: http://ejabberd.jabber.ru/
+Source0: http://www.process-one.net/en/projects/ejabberd/download/%{version}/ejabberd-%{version}.tar.gz
+Source1: ejabberd.init
+Source2: ejabberd.logrotate
+Source3: inetrc
+
+# http://ejabberd.jabber.ru/ejabberdctl-extra
+Source4: http://ejabberd.jabber.ru/files/efiles/mod_ctlextra.erl
+
+# The following were extracted from a patch found on http://realloc.spb.ru/share/ejabberdad.html
+Source5: ejabberd_auth_ad.erl
+Source6: mod_shared_roster_ad.erl
+Source7: mod_vcard_ad.erl
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
+BuildRequires: expat-devel
+BuildRequires: openssl-devel
+BuildRequires: erlang
+BuildRequires: hevea
+
+Requires: erlang
+
+Requires(pre): fedora-usermgmt
+Requires(post): /sbin/chkconfig
+Requires(preun): /sbin/chkconfig
+Requires(preun): /sbin/service
+Requires(postun): /sbin/service
+
+%description
+ejabberd is a Free and Open Source distributed fault-tolerant
+Jabber/XMPP server. It is mostly written in Erlang, and runs on many
+platforms (tested on Linux, FreeBSD, NetBSD, Solaris, Mac OS X and
+Windows NT/2000/XP).
+
+%package doc
+Summary: Documentation for ejabberd
+Group: Documentation
+
+%description doc
+Documentation for ejabberd.
+
+%pre
+/usr/sbin/fedora-groupadd 27 -r ejabberd &>/dev/null || :
+/usr/sbin/fedora-useradd 27 -r -s /sbin/nologin -d /var/lib/ejabberd -M \
+ -c 'ejabberd' -g ejabberd ejabberd &>/dev/null || :
+
+%post
+/sbin/chkconfig --add ejabberd
+
+%preun
+if [ $1 = 0 ]; then
+ /sbin/service ejabberd stop >/dev/null 2>&1
+ /sbin/chkconfig --del ejabberd
+fi
+
+%postun
+if [ "$1" -ge "1" ]; then
+ /sbin/service ejabberd condrestart >/dev/null 2>&1
+fi
+
+%prep
+%setup -q
+
+%{__perl} -pi -e "s!/var/lib/ejabberd!%{_libdir}/ejabberd-%{version}!g" src/Makefile.in
+%{__perl} -pi -e "s!/etc!%{_sysconfdir}!g" src/Makefile.in
+%{__perl} -pi -e "s!\@prefix\@!!g" src/Makefile.in
+
+cp %{S:4} src
+cp %{S:5} src
+cp %{S:6} src
+cp %{S:7} src
+
+%build
+pushd src
+%configure --enable-odbc
+make %{?_smp_mflags}
+popd
+pushd doc
+make html pdf
+popd
+
+%install
+rm -rf %{buildroot}
+
+pushd src
+make install DESTDIR=%{buildroot}
+popd
+
+chmod a+x %{buildroot}%{_libdir}/ejabberd-%{version}/priv/lib/*.so
+
+%{__perl} -pi -e 's!./ssl.pem!/etc/ejabberd/ejabberd.pem!g' %{buildroot}/etc/ejabberd/ejabberd.cfg
+
+mkdir -p %{buildroot}/var/log/ejabberd
+mkdir -p %{buildroot}/var/lib/ejabberd/spool
+
+mkdir -p %{buildroot}%{_initrddir}
+cp %{S:1} %{buildroot}%{_initrddir}/ejabberd
+chmod a+x %{buildroot}%{_initrddir}/ejabberd
+
+mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d
+cp %{S:2} %{buildroot}%{_sysconfdir}/logrotate.d/ejabberd
+
+%{__perl} -pi -e 's!\@libdir\@!%{_libdir}!g' %{buildroot}%{_initrddir}/ejabberd %{buildroot}%{_sysconfdir}/logrotate.d/ejabberd
+%{__perl} -pi -e 's!\@version\@!%{version}!g' %{buildroot}%{_initrddir}/ejabberd %{buildroot}%{_sysconfdir}/logrotate.d/ejabberd
+
+cp %{S:3} %{buildroot}%{_sysconfdir}/ejabberd/inetrc
+
+%clean
+rm -rf %{buildroot}
+
+%files
+%defattr(-,root,root,-)
+%doc COPYING
+
+%attr(750,ejabberd,ejabberd) %dir %{_sysconfdir}/ejabberd
+%attr(640,ejabberd,ejabberd) %config(noreplace) %{_sysconfdir}/ejabberd/ejabberd.cfg
+%attr(640,ejabberd,ejabberd) %config(noreplace) %{_sysconfdir}/ejabberd/inetrc
+
+%{_initrddir}/ejabberd
+%config(noreplace) %{_sysconfdir}/logrotate.d/ejabberd
+
+%dir %{_libdir}/ejabberd-%{version}
+%dir %{_libdir}/ejabberd-%{version}/ebin
+%{_libdir}/ejabberd-%{version}/ebin/*.app
+%{_libdir}/ejabberd-%{version}/ebin/*.beam
+
+%dir %{_libdir}/ejabberd-%{version}/priv
+
+%dir %{_libdir}/ejabberd-%{version}/priv/lib
+%{_libdir}/ejabberd-%{version}/priv/lib/*.so
+
+%dir %{_libdir}/ejabberd-%{version}/priv/msgs
+%{_libdir}/ejabberd-%{version}/priv/msgs/*.msg
+
+%attr(750,ejabberd,ejabberd) %dir /var/lib/ejabberd
+%attr(750,ejabberd,ejabberd) %dir /var/lib/ejabberd/spool
+
+%attr(750,ejabberd,ejabberd) %dir /var/log/ejabberd
+
+%files doc
+%defattr(-,root,root,-)
+%doc ChangeLog COPYING TODO doc/*.pdf doc/*.html doc/*.png doc/release_notes_*
+
+%changelog
+* Thu Jun 22 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-7
+- Oops drop bad patch.
+
+* Thu Jun 22 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-6
+- Split documentation off to a subpackage.
+- Own %{_libdir}/ejabberd-%{version}
+- Mark %{_sysconfdir}/logrotate.d/ejabberd as %config
+
+* Thu Jun 8 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-5
+- Patch the makefile so that it adds a soname to shared libs.
+
+* Fri May 26 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-4
+- Modify AD modules not to check for group membership.
+
+* Thu May 25 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-3
+- Add some extra modules
+
+* Wed May 24 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-2
+- Munge Makefile.in a bit more...
+- Change ownership/permissions - not *everything* needs to be owned by ejabberd
+
+* Wed May 24 2006 Jeffrey C. Ollie <jeff@ocjtech.us> - 1.1.1-1
+- First version for Fedora Extras
diff --git a/ejabberd_auth_ad.erl b/ejabberd_auth_ad.erl
new file mode 100644
index 0000000..5e9e452
--- /dev/null
+++ b/ejabberd_auth_ad.erl
@@ -0,0 +1,167 @@
+%%%----------------------------------------------------------------------
+%%% File : ejabberd_auth_ad.erl
+%%% Author : Alexey Shchepin <alexey@sevcom.net>
+%%% Author : Alex Gorbachenko <agent_007@immo.ru>
+%%% Author : Stanislav Bogatyrev <realloc@realloc.spb.ru>
+%%% Purpose : Authentification via Active Directory
+%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id : $Id: ejabberd_auth_ad.erl 386 2005-12-20 10:06:37Z agent_007 $
+%%%----------------------------------------------------------------------
+
+-module(ejabberd_auth_ad).
+-author('alexey@sevcom.net').
+-author('agent_007@immo.ru').
+-author('realloc@realloc.spb.ru').
+-vsn('$Revision: 386 $ ').
+
+%% External exports
+-export([start/1,
+ set_password/3,
+ check_password/3,
+ check_password/5,
+ try_register/3,
+ dirty_get_registered_users/0,
+ get_vh_registered_users/1,
+ get_password/2,
+ get_password_s/2,
+ is_user_exists/2,
+ remove_user/2,
+ remove_user/3,
+ plain_password_required/0
+ ]).
+
+-include("ejabberd.hrl").
+-include("eldap/eldap.hrl").
+
+%%%----------------------------------------------------------------------
+%%% API
+%%%----------------------------------------------------------------------
+start(Host) ->
+ LDAPServers = ejabberd_config:get_local_option({ad_servers, Host}),
+ RootDN = ejabberd_config:get_local_option({ad_rootdn, Host}),
+ Password = ejabberd_config:get_local_option({ad_password, Host}),
+ eldap:start_link(get_eldap_id(Host, ejabberd),
+ LDAPServers, 389, RootDN, Password),
+ eldap:start_link(get_eldap_id(Host, ejabberd_bind),
+ LDAPServers, 389, RootDN, Password),
+ ok.
+
+plain_password_required() ->
+ true.
+
+check_password(User, Server, Password) ->
+ case find_user_dn(User, Server) of
+ false ->
+ false;
+ DN ->
+ LServer = jlib:nameprep(Server),
+ case eldap:bind(get_eldap_id(LServer, ejabberd_bind),
+ DN, Password) of
+ ok ->
+ true;
+ _ ->
+ false
+ end
+ end.
+
+check_password(User, Server, Password, _StreamID, _Digest) ->
+ check_password(User, Server, Password).
+
+set_password(_User, _Server, _Password) ->
+ {error, not_allowed}.
+
+try_register(_User, _Server, _Password) ->
+ {error, not_allowed}.
+
+dirty_get_registered_users() ->
+ get_vh_registered_users(?MYNAME).
+
+get_vh_registered_users(Server) ->
+ LServer = jlib:nameprep(Server),
+ Attr = ejabberd_config:get_local_option({ad_uidattr, LServer}),
+% AdGroup = ejabberd_config:get_local_option({ad_group, LServer}),
+ FilterPerson = eldap:equalityMatch("objectCategory", "person"),
+ FilterComp = eldap:equalityMatch("objectClass", "computer"),
+ FilterHidden = eldap:equalityMatch("description", "hidden"),
+% FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
+ FilterLive = eldap:equalityMatch("userAccountControl", "66050"),
+ FilterDef = eldap:present(Attr),
+ Filter = eldap:'and'([
+ FilterDef,
+ FilterPerson,
+% FilterGroup,
+ eldap:'not'(FilterComp),
+ eldap:'not'(FilterHidden),
+ eldap:'not'(FilterLive)]),
+ Base = ejabberd_config:get_local_option({ad_base, LServer}),
+ case eldap:search(get_eldap_id(LServer, ejabberd),
+ [{base, Base},
+ {filter, Filter},
+ {attributes, [Attr]}]) of
+ #eldap_search_result{entries = Es} ->
+ lists:flatmap(
+ fun(E) ->
+ case lists:keysearch(Attr, 1, E#eldap_entry.attributes) of
+ {value, {_, [U]}} ->
+ case jlib:nodeprep(U) of
+ error ->
+ [];
+ LU ->
+ [{LU, LServer}]
+ end;
+ _ ->
+ []
+ end
+ end, Es);
+ _ ->
+ []
+ end.
+
+get_password(_User, _Server) ->
+ false.
+
+get_password_s(_User, _Server) ->
+ "".
+
+is_user_exists(User, Server) ->
+ case find_user_dn(User, Server) of
+ false ->
+ false;
+ _DN ->
+ true
+ end.
+
+remove_user(_User, _Server) ->
+ {error, not_allowed}.
+
+remove_user(_User, _Server, _Password) ->
+ not_allowed.
+
+
+%%%----------------------------------------------------------------------
+%%% Internal functions
+%%%----------------------------------------------------------------------
+
+find_user_dn(User, Server) ->
+ LServer = jlib:nameprep(Server),
+ AdGroup = ejabberd_config:get_local_option({ad_group, LServer}),
+ Attr = ejabberd_config:get_local_option({ad_uidattr, LServer}),
+ FilterAttr = eldap:equalityMatch(Attr, User),
+ FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
+ Filter = eldap:'and'([
+ FilterAttr,
+ FilterGroup
+ ]),
+ Base = ejabberd_config:get_local_option({ad_base, LServer}),
+ case eldap:search(get_eldap_id(LServer, ejabberd),
+ [{base, Base},
+ {filter, Filter},
+ {attributes, []}]) of
+ #eldap_search_result{entries = [E | _]} ->
+ E#eldap_entry.object_name;
+ _ ->
+ false
+ end.
+
+get_eldap_id(Host, Name) ->
+ atom_to_list(gen_mod:get_module_proc(Host, Name)).
diff --git a/inetrc b/inetrc
new file mode 100644
index 0000000..2d2f635
--- /dev/null
+++ b/inetrc
@@ -0,0 +1 @@
+{file, resolv, "/etc/resolv.conf"}.
diff --git a/mod_ctlextra.erl b/mod_ctlextra.erl
new file mode 100644
index 0000000..dfff6ce
--- /dev/null
+++ b/mod_ctlextra.erl
@@ -0,0 +1,502 @@
+%%%----------------------------------------------------------------------
+%%% File : mod_ctlextra.erl
+%%% Author :
+%%% Purpose : Adds more options for ejabberd_ctl
+%%% Created :
+%%% Id :
+%%%----------------------------------------------------------------------
+
+%%% mod_ctlextra v0.2.2 for ejabberd 1.1.1 (18/May/2006)
+%%%
+%%% INSTALL:
+%%% 1. Copy mod_ctlextra.erl to your ejabberd/src
+%%% 2. Add to your ejabberd.cfg, on the modules section:
+%%% {mod_ctlextra, []},
+%%% 3. Recompile and restart ejabberd
+%%%
+%%% USAGE:
+%%% - Now you have several new options for ejabberd-ctl
+%%% - Example for vcard: ejabberdctl eja@host vcard-get joe myjab.net email
+%%% - The file used by 'pushroster' and 'pushroster-all' must be placed on
+%%% the same directory where the .beam files are. Example content:
+%%% [{"bob", "myserver", "workers", "Bob"},
+%%% {"mart", "myserver", "workers", "Mart"},
+%%% {"Rich", "myserver", "bosses", "Rich"}].
+
+-module(mod_ctlextra).
+-author('').
+-vsn('Version 0.2.1').
+
+-behaviour(gen_mod).
+
+-export([
+ start/2,
+ stop/1,
+ ctl_process/2,
+ ctl_process/3
+ ]).
+
+-include("ejabberd_ctl.hrl").
+-include("jlib.hrl").
+
+start(Host, _Opts) ->
+ ejabberd_ctl:register_commands([
+ {"compile file", "recompile and reload file"},
+ {"load-config file", "load config from file"},
+ {"remove-node nodename", "remove an ejabberd node from the database"},
+
+ % ejabberd_auth
+ {"delete-older-users days", "delete users that have not logged in the last 'days'"},
+ {"set-password user server password", "set password to user@server"},
+
+ % ejd2odbc
+ {"export2odbc server output", "export all possible tables on server to output"},
+
+ % mod_offline
+ {"delete-older-messages days", "delete offline messages older than 'days'"},
+
+ % mod_shared_roster
+ {"srg-create group host name description display", "create the group with options"},
+ {"srg-delete group host", "delete the group"},
+ {"srg-user-add user server group host", "add user@server to group on host"},
+ {"srg-user-del user server group host", "delete user@server from group on host"},
+
+ % mod_vcard
+ {"vcard-get user host data [data2]", "get data from the vCard of the user"},
+ {"vcard-set user host data [data2] content", "set data to content on the vCard"},
+
+ % mod_announce
+ % announce_send_online host message
+ % announce_send_all host, message
+
+ % mod_muc
+ % muc-add room opts
+ % muc-del room
+ % muc-del-older 90 : delete rooms older than X days (with no activity (chat, presence, logins) in 6 months)
+
+ % mod_roster
+ {"add-rosteritem user1 server1 user2 server2 nick group subs", "Add user2@server2 to user1@server1"},
+ %{"", "subs= none, from, to or both"},
+ %{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
+ %{"", "will add mike@server.com to peter@localhost roster"},
+ {"pushroster file user server", "push template roster in file to user@server"},
+ {"pushroster-all file", "push template roster in file to all those users"},
+ {"push-alltoall server group", "adds all the users to all the users in Group"},
+
+ {"stats registeredusers", "number of registered users"},
+ {"stats onlineusers", "number of logged users"},
+
+ % misc
+ {"killsession user server resource", "kill a user session"}
+ ], ?MODULE, ctl_process),
+ ejabberd_ctl:register_commands(Host, [
+ % mod_last
+ {"num-active-users days", "number of users active in the last 'days'"},
+ {"stats registeredusers", "number of registered users"},
+ {"stats onlineusers", "number of logged users"}
+ ], ?MODULE, ctl_process),
+ ok.
+
+stop(_Host) ->
+ ok.
+
+
+ctl_process(_Val, ["blo"]) ->
+ FResources = "eeeaaa aaa",
+ io:format("~s", [FResources]),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["delete-older-messages", Days]) ->
+ mod_offline:remove_old_messages(list_to_integer(Days)),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["delete-older-users", Days]) ->
+ {removed, N, UR} = delete_older_users(list_to_integer(Days)),
+ io:format("Deleted ~p users: ~p~n", [N, UR]),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["export2odbc", Server, Output1]) ->
+ Output = list_to_atom(Output1),
+ ejd2odbc:export_passwd(Server, Output),
+ ejd2odbc:export_roster(Server, Output),
+ ejd2odbc:export_offline(Server, Output),
+ ejd2odbc:export_last(Server, Output),
+ ejd2odbc:export_vcard(Server, Output),
+ ejd2odbc:export_vcard_search(Server, Output),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["set-password", User, Server, Password]) ->
+ ejabberd_auth:set_password(User, Server, Password),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["vcard-get", User, Server, Data]) ->
+ {ok, Res} = vcard_get(User, Server, {Data}),
+ io:format("~s~n", [Res]),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["vcard-get", User, Server, Data1, Data2]) ->
+ {ok, Res} = vcard_get(User, Server, {Data1, Data2}),
+ io:format("~s~n", [Res]),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["vcard-set", User, Server, Data, Content]) ->
+ {ok, Res} = vcard_set(User, Server, Data, Content),
+ io:format("~s~n", [Res]),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["vcard-set", User, Server, Data1, Data2, Content]) ->
+ {ok, Res} = vcard_set(User, Server, Data1, Data2, Content),
+ io:format("~s~n", [Res]),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["compile", Module]) ->
+ compile:file(Module),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["remove-node", Node]) ->
+ mnesia:del_table_copy(schema, list_to_atom(Node)),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["srg-create", Group, Host, Name, Description, Display]) ->
+ Opts = [{name, Name}, {displayed_groups, [Display]}, {description, Description}],
+ {atomic, ok} = mod_shared_roster:create_group(Host, Group, Opts),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["srg-delete", Group, Host]) ->
+ {atomic, ok} = mod_shared_roster:delete_group(Host, Group),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["srg-user-add", User, Server, Group, Host]) ->
+ {atomic, ok} = mod_shared_roster:add_user_to_group(Host, {User, Server}, Group),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["srg-user-del", User, Server, Group, Host]) ->
+ {atomic, ok} = mod_shared_roster:remove_user_from_group(Host, {User, Server}, Group),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["add-rosteritem", LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subs]) ->
+ case add_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, list_to_atom(Subs), []) of
+ {atomic, ok} ->
+ ?STATUS_SUCCESS;
+ {error, Reason} ->
+ io:format("Can't add ~p@~p to ~p@~p: ~p~n",
+ [RemoteUser, RemoteServer, LocalUser, LocalServer, Reason]),
+ ?STATUS_ERROR;
+ {badrpc, Reason} ->
+ io:format("Can't add roster item to user ~p: ~p~n",
+ [LocalUser, Reason]),
+ ?STATUS_BADRPC
+ end;
+
+ctl_process(_Val, ["pushroster", File, User, Server]) ->
+ case pushroster(File, User, Server) of
+ ok ->
+ ?STATUS_SUCCESS;
+ {error, Reason} ->
+ io:format("Can't push roster ~p to ~p@~p: ~p~n",
+ [File, User, Server, Reason]),
+ ?STATUS_ERROR;
+ {badrpc, Reason} ->
+ io:format("Can't push roster ~p: ~p~n",
+ [File, Reason]),
+ ?STATUS_BADRPC
+ end;
+
+ctl_process(_Val, ["pushroster-all", File]) ->
+ case pushroster_all([File]) of
+ ok ->
+ ?STATUS_SUCCESS;
+ {error, Reason} ->
+ io:format("Can't push roster ~p: ~p~n",
+ [File, Reason]),
+ ?STATUS_ERROR;
+ {badrpc, Reason} ->
+ io:format("Can't push roster ~p: ~p~n",
+ [File, Reason]),
+ ?STATUS_BADRPC
+ end;
+
+ctl_process(_Val, ["push-alltoall", Server, Group]) ->
+ case push_alltoall(Server, Group) of
+ ok ->
+ ?STATUS_SUCCESS;
+ {error, Reason} ->
+ io:format("Can't push all to all: ~p~n",
+ [Reason]),
+ ?STATUS_ERROR;
+ {badrpc, Reason} ->
+ io:format("Can't push all to all: ~p~n",
+ [Reason]),
+ ?STATUS_BADRPC
+ end;
+
+ctl_process(_Val, ["load-config", Path]) ->
+ case ejabberd_config:load_file(Path) of
+ {atomic, ok} ->
+ ?STATUS_SUCCESS;
+ {error, Reason} ->
+ io:format("Can't load config file ~p: ~p~n",
+ [filename:absname(Path), Reason]),
+ ?STATUS_ERROR;
+ {badrpc, Reason} ->
+ io:format("Can't load config file ~p: ~p~n",
+ [filename:absname(Path), Reason]),
+ ?STATUS_BADRPC
+ end;
+
+ctl_process(_Val, ["stats", Stat]) ->
+ Res = case Stat of
+ "registeredusers" -> mnesia:table_info(passwd, size);
+ "onlineusers" -> mnesia:table_info(session, size)
+ end,
+ io:format("~p~n", [Res]),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, ["killsession", User, Server, Resource]) ->
+ ejabberd_router:route(
+ jlib:make_jid("", "", ""),
+ jlib:make_jid(User, Server, Resource),
+ {xmlelement, "broadcast", [], [{exit, "killed"}]}),
+ ?STATUS_SUCCESS;
+
+ctl_process(Val, _Args) ->
+ Val.
+
+
+ctl_process(_Val, Host, ["num-active-users", Days]) ->
+ Number = num_active_users(Host, list_to_integer(Days)),
+ io:format("~p~n", [Number]),
+ ?STATUS_SUCCESS;
+
+ctl_process(_Val, Host, ["stats", Stat]) ->
+ Res = case Stat of
+ "registeredusers" -> length(ejabberd_auth:get_vh_registered_users(Host));
+ "onlineusers" -> length(ejabberd_sm:get_vh_session_list(Host))
+ end,
+ io:format("~p~n", [Res]),
+ ?STATUS_SUCCESS;
+
+ctl_process(Val, _Host, _Args) ->
+ Val.
+
+
+%%-------------
+%% UTILS
+%%-------------
+
+add_rosteritem(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs) ->
+ subscribe(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs),
+ % TODO: if the server is not local and Subs=to or both: send subscription request
+ % TODO: check if the 'remote server' is a virtual host here, else do nothing
+ %add_rosteritem2(RU, RS, LU, LS, LU, "", invert_subs(Subscription), Xattrs, Host).
+ subscribe(RU, RS, LU, LS, LU, "", invert_subs(Subscription), Xattrs).
+
+invert_subs(none) -> none;
+invert_subs(to) -> none;
+invert_subs(from) -> to;
+invert_subs(both) -> both.
+
+subscribe(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subscription, Xattrs) ->
+ mnesia:transaction(
+ fun() ->
+ mnesia:write({
+ roster,
+ {LocalUser,LocalServer,{RemoteUser,RemoteServer,[]}}, % usj
+ {LocalUser,LocalServer}, % us
+ {RemoteUser,RemoteServer,[]}, % jid
+ Nick, % name: "Mom", []
+ Subscription, % subscription: none, to=you see him, from=he sees you, both
+ none, % ask: out=send request, in=somebody requests you, none
+ [Group], % groups: ["Family"]
+ Xattrs, % xattrs: [{"category","conference"}]
+ [] % xs: []
+ })
+ end).
+
+pushroster(File, User, Server) ->
+ {ok, [Roster]} = file:consult(File),
+ subscribe_roster({User, Server, "", User}, Roster).
+
+pushroster_all(File) ->
+ {ok, [Roster]} = file:consult(File),
+ subscribe_all(Roster).
+
+subscribe_all(Roster) ->
+ subscribe_all(Roster, Roster).
+subscribe_all([], _) ->
+ ok;
+subscribe_all([User1 | Users], Roster) ->
+ subscribe_roster(User1, Roster),
+ subscribe_all(Users, Roster).
+
+subscribe_roster(_, []) ->
+ ok;
+% Do not subscribe a user to itself
+subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
+ subscribe_roster({Name, Server, Group, Nick}, Roster);
+% Subscribe Name2 to Name1
+subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
+ subscribe(Name1, Server1, Name2, Server2, Nick2, Group2, both, []),
+ subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
+
+push_alltoall(S, G) ->
+ Users = ejabberd_auth:get_vh_registered_users(S),
+ Users2 = build_list_users(G, Users, []),
+ subscribe_all(Users2).
+
+build_list_users(_Group, [], Res) ->
+ Res;
+build_list_users(Group, [{User, Server}|Users], Res) ->
+ build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
+
+vcard_get(User, Server, DataX) ->
+ [{_, _, A1}] = mnesia:dirty_read(vcard, {User, Server}),
+ Elem = vcard_get(DataX, A1),
+ {ok, xml:get_tag_cdata(Elem)}.
+
+vcard_get({Data1, Data2}, A1) ->
+ A2 = xml:get_subtag(A1, Data1),
+ A3 = xml:get_subtag(A2, Data2),
+ case A3 of
+ "" -> A2;
+ _ -> A3
+ end;
+
+vcard_get({Data}, A1) ->
+ xml:get_subtag(A1, Data).
+
+vcard_set(User, Server, Data1, Data2, Content) ->
+ Content2 = {xmlelement, Data2, [], [{xmlcdata,Content}]},
+ R = {xmlelement, Data1, [], [Content2]},
+ vcard_set2(User, Server, R, Data1).
+
+vcard_set(User, Server, Data, Content) ->
+ R = {xmlelement, Data, [], [{xmlcdata,Content}]},
+ vcard_set2(User, Server, R, Data).
+
+vcard_set2(User, Server, R, Data) ->
+ % Get old vcard
+ [{_, _, A1}] = mnesia:dirty_read(vcard, {User, Server}),
+ {_, _, _, A2} = A1,
+
+ A3 = lists:keydelete(Data, 2, A2),
+ A4 = [R | A3],
+
+ % Build new vcard
+ SubEl = {xmlelement, "vCard", [{"xmlns","vcard-temp"}], A4},
+ IQ = #iq{type=set, sub_el = SubEl},
+ JID = jlib:make_jid(User, Server, ""),
+
+ mod_vcard:process_sm_iq(JID, JID, IQ),
+ {ok, "done"}.
+
+
+-record(last_activity, {us, timestamp, status}).
+
+delete_older_users(Days) ->
+ % Convert older time
+ SecOlder = Days*24*60*60,
+
+ % Get current time
+ {MegaSecs, Secs, _MicroSecs} = now(),
+ TimeStamp_now = MegaSecs * 1000000 + Secs,
+
+ % Get the list of registered users
+ Users = ejabberd_auth:dirty_get_registered_users(),
+
+ % For a user, remove if required and answer true
+ F = fun({LUser, LServer}) ->
+ % Check if the user is logged
+ case ejabberd_sm:get_user_resources(LUser, LServer) of
+ % If it isn't
+ [] ->
+ % Look for his last_activity
+ case mnesia:dirty_read(last_activity, {LUser, LServer}) of
+ % If it is
+ % existent:
+ [#last_activity{timestamp = TimeStamp}] ->
+ % get his age
+ Sec = TimeStamp_now - TimeStamp,
+ % If he is
+ if
+ % younger than SecOlder:
+ Sec < SecOlder ->
+ % do nothing
+ false;
+ % older:
+ true ->
+ % remove the user
+ ejabberd_auth:remove_user(LUser, LServer),
+ true
+ end;
+ % nonexistent:
+ [] ->
+ % remove the user
+ ejabberd_auth:remove_user(LUser, LServer),
+ true
+ end;
+ % Else
+ _ ->
+ % do nothing
+ false
+ end
+ end,
+ % Apply the function to every user in the list
+ Users_removed = lists:filter(F, Users),
+ {removed, length(Users_removed), Users_removed}.
+
+num_active_users(Host, Days) ->
+ list_last_activity(Host, true, Days).
+
+% Code based on ejabberd/src/web/ejabberd_web_admin.erl
+list_last_activity(Host, Integral, Days) ->
+ {MegaSecs, Secs, _MicroSecs} = now(),
+ TimeStamp = MegaSecs * 1000000 + Secs,
+ TS = TimeStamp - Days * 86400,
+ case catch mnesia:dirty_select(
+ last_activity, [{{last_activity, {'_', Host}, '$1', '_'},
+ [{'>', '$1', TS}],
+ [{'trunc', {'/',
+ {'-', TimeStamp, '$1'},
+ 86400}}]}]) of
+ {'EXIT', _Reason} ->
+ [];
+ Vals ->
+ Hist = histogram(Vals, Integral),
+ if
+ Hist == [] ->
+ 0;
+ true ->
+ Left = if
+ Days == infinity ->
+ 0;
+ true ->
+ Days - length(Hist)
+ end,
+ Tail = if
+ Integral ->
+ lists:duplicate(Left, lists:last(Hist));
+ true ->
+ lists:duplicate(Left, 0)
+ end,
+ lists:nth(Days, Hist ++ Tail)
+ end
+ end.
+histogram(Values, Integral) ->
+ histogram(lists:sort(Values), Integral, 0, 0, []).
+histogram([H | T], Integral, Current, Count, Hist) when Current == H ->
+ histogram(T, Integral, Current, Count + 1, Hist);
+histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H ->
+ if
+ Integral ->
+ histogram(Values, Integral, Current + 1, Count, [Count | Hist]);
+ true ->
+ histogram(Values, Integral, Current + 1, 0, [Count | Hist])
+ end;
+histogram([], _Integral, _Current, Count, Hist) ->
+ if
+ Count > 0 ->
+ lists:reverse([Count | Hist]);
+ true ->
+ lists:reverse(Hist)
+ end.
diff --git a/mod_shared_roster_ad.erl b/mod_shared_roster_ad.erl
new file mode 100644
index 0000000..8dc09f1
--- /dev/null
+++ b/mod_shared_roster_ad.erl
@@ -0,0 +1,428 @@
+%%%----------------------------------------------------------------------
+%%% File : mod_shared_roster.erl
+%%% Author : Alexey Shchepin <alexey@sevcom.net>
+%%% Author : Stanislav Bogatyrev <realloc@realloc.spb.ru>
+%%% Purpose : Shared roster management
+%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id : $Id: mod_shared_roster_ad.erl,v 1.1 2006/06/23 12:40:03 jcollie Exp $
+%%%----------------------------------------------------------------------
+
+-module(mod_shared_roster_ad).
+-author('alexey@sevcom.net').
+-author('realloc@realloc.spb.ru').
+-vsn('$Revision: 1.1 $ ').
+
+-behaviour(gen_mod).
+
+-export([start/2, stop/1,
+ get_user_roster/2,
+ get_subscription_lists/3,
+ get_jid_info/4,
+ in_subscription/5,
+ out_subscription/4,
+ list_groups/1,
+ create_group/2,
+ create_group/3,
+ delete_group/2,
+ get_group_opts/2,
+ set_group_opts/3,
+ get_group_users/2,
+ get_group_explicit_users/2,
+ add_user_to_group/3,
+ remove_user_from_group/3]).
+
+-include("ejabberd.hrl").
+-include("jlib.hrl").
+-include("mod_roster.hrl").
+-include("eldap/eldap.hrl").
+
+-record(sr_group, {group_host, opts}).
+-record(sr_user, {us, group_host}).
+
+start(Host, _Opts) ->
+ mnesia:create_table(sr_group,
+ [{disc_copies, [node()]},
+ {attributes, record_info(fields, sr_group)}]),
+ mnesia:create_table(sr_user,
+ [{disc_copies, [node()]},
+ {type, bag},
+ {attributes, record_info(fields, sr_user)}]),
+ mnesia:add_table_index(sr_user, group_host),
+ ejabberd_hooks:add(roster_get, Host,
+ ?MODULE, get_user_roster, 70),
+ ejabberd_hooks:add(roster_in_subscription, Host,
+ ?MODULE, in_subscription, 30),
+ ejabberd_hooks:add(roster_out_subscription, Host,
+ ?MODULE, out_subscription, 30),
+ ejabberd_hooks:add(roster_get_subscription_lists, Host,
+ ?MODULE, get_subscription_lists, 70),
+ ejabberd_hooks:add(roster_get_jid_info, Host,
+ ?MODULE, get_jid_info, 70),
+
+ %ejabberd_hooks:add(remove_user, Host,
+ % ?MODULE, remove_user, 50),
+ LDAPServers = ejabberd_config:get_local_option({ad_servers, Host}),
+ RootDN = ejabberd_config:get_local_option({ad_rootdn, Host}),
+ Password = ejabberd_config:get_local_option({ad_password, Host}),
+ eldap:start_link("mod_shared_roster_ad", LDAPServers, 389, RootDN, Password).
+
+
+
+stop(Host) ->
+ ejabberd_hooks:delete(roster_get, Host,
+ ?MODULE, get_user_roster, 70),
+ ejabberd_hooks:delete(roster_in_subscription, Host,
+ ?MODULE, in_subscription, 30),
+ ejabberd_hooks:delete(roster_out_subscription, Host,
+ ?MODULE, out_subscription, 30),
+ ejabberd_hooks:delete(roster_get_subscription_lists, Host,
+ ?MODULE, get_subscription_lists, 70),
+ ejabberd_hooks:delete(roster_get_jid_info, Host,
+ ?MODULE, get_jid_info, 70).
+ %ejabberd_hooks:delete(remove_user, Host,
+ % ?MODULE, remove_user, 50),
+
+
+get_user_roster(Items, US) ->
+ {U, S} = US,
+ DisplayedGroups = get_user_displayed_groups_ad(US),
+ SRUsers =
+ lists:foldl(
+ fun(Group, Acc1) ->
+ lists:foldl(
+ fun(User, Acc2) ->
+ dict:append(User, Group, Acc2)
+ end, Acc1, get_group_users_ad(S, Group))
+ end, dict:new(), DisplayedGroups),
+ {NewItems1, SRUsersRest} =
+ lists:mapfoldl(
+ fun(Item, SRUsers1) ->
+ {_, _, {U1, S1, _}} = Item#roster.usj,
+ US1 = {U1, S1},
+ case dict:find(US1, SRUsers1) of
+ {ok, _GroupNames} ->
+ {Item#roster{subscription = both, ask = none},
+ dict:erase(US1, SRUsers1)};
+ error ->
+ {Item, SRUsers1}
+ end
+ end, SRUsers, Items),
+ SRItems = [#roster{usj = {U, S, {U1, S1, ""}},
+ us = US,
+ jid = {U1, S1, ""},
+ name = get_user_fn(U1,S1),
+ subscription = both,
+ ask = none,
+ groups = GroupNames} ||
+ {{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)],
+ SRItems ++ NewItems1.
+
+get_subscription_lists({F, T}, User, Server) ->
+ LUser = jlib:nodeprep(User),
+ LServer = jlib:nameprep(Server),
+ US = {LUser, LServer},
+ DisplayedGroups = get_user_displayed_groups_ad(US),
+ SRUsers =
+ lists:usort(
+ lists:flatmap(
+ fun(Group) ->
+ get_group_users_ad(LServer, Group)
+ end, DisplayedGroups)),
+ SRJIDs = [{U1, S1, ""} || {U1, S1} <- SRUsers],
+ {lists:usort(SRJIDs ++ F), lists:usort(SRJIDs ++ T)}.
+
+get_jid_info({Subscription, Groups}, User, Server, JID) ->
+ LUser = jlib:nodeprep(User),
+ LServer = jlib:nameprep(Server),
+ US = {LUser, LServer},
+ {U1, S1, _} = jlib:jid_tolower(JID),
+ US1 = {U1, S1},
+ DisplayedGroups = get_user_displayed_groups_ad(US),
+ SRUsers =
+ lists:foldl(
+ fun(Group, Acc1) ->
+ lists:foldl(
+ fun(User1, Acc2) ->
+ dict:append(
+ User1, Group, Acc2)
+ end, Acc1, get_group_users_ad(LServer, Group))
+ end, dict:new(), DisplayedGroups),
+ case dict:find(US1, SRUsers) of
+ {ok, GroupNames} ->
+ NewGroups = if
+ Groups == [] -> GroupNames;
+ true -> Groups
+ end,
+ {both, NewGroups};
+ error ->
+ {Subscription, Groups}
+ end.
+
+in_subscription(Acc, User, Server, JID, Type) ->
+ process_subscription(in, User, Server, JID, Type, Acc).
+
+out_subscription(User, Server, JID, Type) ->
+ process_subscription(out, User, Server, JID, Type, false).
+
+process_subscription(Direction, User, Server, JID, _Type, Acc) ->
+ LUser = jlib:nodeprep(User),
+ LServer = jlib:nameprep(Server),
+ US = {LUser, LServer},
+ {U1, S1, _} = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
+ US1 = {U1, S1},
+ DisplayedGroups = get_user_displayed_groups_ad(US),
+ SRUsers =
+ lists:usort(
+ lists:flatmap(
+ fun(Group) ->
+ get_group_users_ad(LServer, Group)
+ end, DisplayedGroups)),
+ case lists:member(US1, SRUsers) of
+ true ->
+ case Direction of
+ in ->
+ {stop, false};
+ out ->
+ stop
+ end;
+ false ->
+ Acc
+ end.
+
+list_groups(Host) ->
+ get_user_displayed_groups_ad({"",Host}).
+
+
+create_group(Host, Group) ->
+ create_group(Host, Group, []).
+
+create_group(Host, Group, Opts) ->
+ R = #sr_group{group_host = {Group, Host}, opts = Opts},
+ F = fun() ->
+ mnesia:write(R)
+ end,
+ mnesia:transaction(F).
+
+delete_group(Host, Group) ->
+ F = fun() ->
+ mnesia:delete({sr_group, {Group, Host}})
+ end,
+ mnesia:transaction(F).
+
+get_group_opts(Host, Group) ->
+ case catch mnesia:dirty_read(sr_group, {Group, Host}) of
+ [#sr_group{opts = Opts}] ->
+ Opts;
+ _ ->
+ error
+ end.
+
+set_group_opts(Host, Group, Opts) ->
+ R = #sr_group{group_host = {Group, Host}, opts = Opts},
+ F = fun() ->
+ mnesia:write(R)
+ end,
+ mnesia:transaction(F).
+
+
+
+get_user_groups(US) ->
+ Host = element(2, US),
+ case catch mnesia:dirty_read(sr_user, US) of
+ Rs when is_list(Rs) ->
+ [Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host];
+ _ ->
+ []
+ end ++ get_all_users_groups(Host).
+
+is_group_enabled(Host, Group) ->
+ case catch mnesia:dirty_read(sr_group, {Group, Host}) of
+ [#sr_group{opts = Opts}] ->
+ not lists:member(disabled, Opts);
+ _ ->
+ false
+ end.
+
+get_group_opt(Host, Group, Opt, Default) ->
+ case catch mnesia:dirty_read(sr_group, {Group, Host}) of
+ [#sr_group{opts = Opts}] ->
+ case lists:keysearch(Opt, 1, Opts) of
+ {value, {_, Val}} ->
+ Val;
+ false ->
+ Default
+ end;
+ _ ->
+ false
+ end.
+
+get_group_users(Host, Group) ->
+ case get_group_opt(Host, Group, all_users, false) of
+ true ->
+ ejabberd_auth:get_vh_registered_users(Host);
+ false ->
+ []
+ end ++ get_group_explicit_users(Host, Group).
+
+get_group_explicit_users(Host, Group) ->
+ case catch mnesia:dirty_index_read(
+ sr_user, {Group, Host}, #sr_user.group_host) of
+ Rs when is_list(Rs) ->
+ [R#sr_user.us || R <- Rs];
+ _ ->
+ []
+ end.
+
+get_group_name(Host, Group) ->
+ get_group_opt(Host, Group, name, Group).
+
+get_all_users_groups(Host) ->
+ lists:filter(
+ fun(Group) -> get_group_opt(Host, Group, all_users, false) end,
+ list_groups(Host)).
+
+get_user_displayed_groups(US) ->
+ Host = element(2, US),
+ DisplayedGroups1 =
+ lists:usort(
+ lists:flatmap(
+ fun(Group) ->
+ case is_group_enabled(Host, Group) of
+ true ->
+ get_group_opt(Host, Group, displayed_groups, []);
+ false ->
+ []
+ end
+ end, get_user_groups(US))),
+ [Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)].
+
+
+
+
+add_user_to_group(Host, US, Group) ->
+ R = #sr_user{us = US, group_host = {Group, Host}},
+ F = fun() ->
+ mnesia:write(R)
+ end,
+ mnesia:transaction(F).
+
+remove_user_from_group(Host, US, Group) ->
+ R = #sr_user{us = US, group_host = {Group, Host}},
+ F = fun() ->
+ mnesia:delete_object(R)
+ end,
+ mnesia:transaction(F).
+
+
+
+find_user_attr(User, Host) ->
+ Attr = ejabberd_config:get_local_option({ad_uidattr, Host}),
+ Filter = eldap:equalityMatch(Attr, User),
+ Base = ejabberd_config:get_local_option({ad_base, Host}),
+
+ case eldap:search("mod_shared_roster_ad",
+ [{base, Base},
+ {filter, Filter},
+ {attributes, []}]) of
+ #eldap_search_result{entries = [E | _]} ->
+ E;
+ _ ->
+ false
+ end.
+
+get_user_displayed_groups_ad(US) ->
+ {_, Host} = US,
+ AdGroup = ejabberd_config:get_local_option({ad_group, Host}),
+ FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
+ Base = ejabberd_config:get_local_option({ad_base, Host}),
+
+ case eldap:search("mod_shared_roster_ad",
+ [{base, Base},
+ {filter, FilterGroup},
+ {attributes, []}]) of
+ #eldap_search_result{entries = E} ->
+ lists:usort(lists:map(
+ fun(X) ->
+ case X of
+ #eldap_entry{attributes = Attributes} ->
+ ldap_get_value(Attributes,"department");
+ false ->
+ ""
+ end
+ end, E
+ ));
+
+ _ ->
+ []
+ end.
+
+get_eldap_id(Host, Name) ->
+ atom_to_list(gen_mod:get_module_proc(Host, Name)).
+
+
+get_group_users_ad(Host, Group) ->
+ Attr = ejabberd_config:get_local_option({ad_uidattr, Host}),
+% AdGroup = ejabberd_config:get_local_option({ad_group, Host}),
+ FilterPerson = eldap:equalityMatch("objectCategory", "person"),
+ FilterComp = eldap:equalityMatch("objectClass", "computer"),
+ FilterHidden = eldap:equalityMatch("description", "hidden"),
+% FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
+ FilterDep = eldap:equalityMatch("department", Group),
+ FilterLive = eldap:equalityMatch("userAccountControl", "66050"),
+ FilterDef = eldap:present(Attr),
+ Filter = eldap:'and'([
+ FilterDef,
+ FilterPerson,
+% FilterGroup,
+ FilterDep,
+ eldap:'not'(FilterComp),
+ eldap:'not'(FilterHidden),
+ eldap:'not'(FilterLive)]),
+ Base = ejabberd_config:get_local_option({ad_base, Host}),
+ case eldap:search(get_eldap_id(Host, ejabberd),
+ [{base, Base},
+ {filter, Filter},
+ {attributes, [Attr]}]) of
+ #eldap_search_result{entries = Es} ->
+ lists:flatmap(
+ fun(E) ->
+ case lists:keysearch(Attr, 1, E#eldap_entry.attributes) of
+ {value, {_, [U]}} ->
+ case jlib:nodeprep(U) of
+ error ->
+ [];
+ LU ->
+ [{LU, Host}]
+ end;
+ _ ->
+ []
+ end
+ end, Es);
+ _ ->
+ []
+ end.
+
+
+
+
+
+ldap_get_value(E,Attribute) ->
+ case lists:filter(fun({A,_}) ->
+ string:equal(A,Attribute)
+ end,E) of
+ [{_,[Value|_]}] ->
+ Value;
+ _ ->
+ none
+ end.
+
+get_user_fn(User, Host) ->
+ case find_user_attr(User,Host) of
+ #eldap_entry{attributes = Attributes} ->
+ ldap_get_value(Attributes,"cn");
+
+ false ->
+ ""
+ end.
+
+
diff --git a/mod_vcard_ad.erl b/mod_vcard_ad.erl
new file mode 100644
index 0000000..52ddbec
--- /dev/null
+++ b/mod_vcard_ad.erl
@@ -0,0 +1,631 @@
+
+%%%----------------------------------------------------------------------
+%%% File : mod_vcard_ad.erl
+%%% Author : Stanislav Bogatyrev <realloc@realloc.spb.ru>
+%%% Author : Alexey Shchepin <alexey@sevcom.net>
+%%% Author : Alex <agent_007> Gorbachenko (agent_007@immo.ru)
+%%% Purpose :
+%%% Created : 2 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id : $Id: mod_vcard_ad.erl 437 2005-11-19 01:20:05Z agent_007 $
+%%%----------------------------------------------------------------------
+
+-module(mod_vcard_ad).
+-author('realloc@realloc.spb.ru').
+-author('alexey@sevcom.net').
+-author('agent_007@immo.ru').
+-vsn('$Revision: 437 $ ').
+
+-behaviour(gen_mod).
+
+-export([start/2, init/3, stop/1,
+ get_sm_features/5,
+ process_local_iq/3,
+ process_sm_iq/3,
+ remove_user/1]).
+
+-include("ejabberd.hrl").
+-include("eldap/eldap.hrl").
+-include("jlib.hrl").
+
+-define(PROCNAME, ejabberd_mod_vcard_ad).
+
+start(Host, Opts) ->
+ IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
+ gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD,
+ ?MODULE, process_local_iq, IQDisc),
+ gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_VCARD,
+ ?MODULE, process_sm_iq, IQDisc),
+ ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
+ LDAPServers = ejabberd_config:get_local_option({ad_servers, Host}),
+ RootDN = ejabberd_config:get_local_option({ad_rootdn, Host}),
+ Password = ejabberd_config:get_local_option({ad_password, Host}),
+ eldap:start_link("mod_vcard_ad", LDAPServers, 389, RootDN, Password),
+ MyHost = gen_mod:get_opt(host, Opts, "vjud." ++ Host),
+ Search = gen_mod:get_opt(search, Opts, true),
+ register(gen_mod:get_module_proc(Host, ?PROCNAME),
+ spawn(?MODULE, init, [MyHost, Host, Search])).
+
+init(Host, ServerHost, Search) ->
+ case Search of
+ false ->
+ loop(Host, ServerHost);
+ _ ->
+ ejabberd_router:register_route(Host),
+ loop(Host, ServerHost)
+ end.
+
+loop(Host, ServerHost) ->
+ receive
+ {route, From, To, Packet} ->
+ case catch do_route(ServerHost, From, To, Packet) of
+ {'EXIT', Reason} ->
+ ?ERROR_MSG("~p", [Reason]);
+ _ ->
+ ok
+ end,
+ loop(Host, ServerHost);
+ stop ->
+ ejabberd_router:unregister_route(Host),
+ ok;
+ _ ->
+ loop(Host, ServerHost)
+ end.
+
+stop(Host) ->
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_VCARD),
+ ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
+ Proc ! stop,
+ {wait, Proc}.
+
+get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
+ Acc;
+get_sm_features(Acc, _From, _To, Node, _Lang) ->
+ case Node of
+ [] ->
+ case Acc of
+ {result, Features} ->
+ {result, [?NS_VCARD | Features]};
+ empty ->
+ {result, [?NS_VCARD]}
+ end;
+ _ ->
+ Acc
+ end.
+
+process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
+ case Type of
+ set ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
+ get ->
+ IQ#iq{type = result,
+ sub_el = [{xmlelement, "vCard",
+ [{"xmlns", ?NS_VCARD}],
+ [{xmlelement, "FN", [],
+ [{xmlcdata, "ejabberd"}]},
+ {xmlelement, "URL", [],
+ [{xmlcdata,
+ "http://ejabberd.jabberstudio.org/"}]},
+ {xmlelement, "DESC", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang,
+ "Erlang Jabber Server\n"
+ "Copyright (c) 2002-2005 Alexey Shchepin")}]},
+ {xmlelement, "BDAY", [],
+ [{xmlcdata, "2002-11-16"}]}
+ ]}]}
+ end.
+
+find_ldap_user(Host, User) ->
+ Attr = ejabberd_config:get_local_option({ad_uidattr, Host}),
+ Filter = eldap:equalityMatch(Attr, User),
+ Base = ejabberd_config:get_local_option({ad_base, Host}),
+ case eldap:search("mod_vcard_ad", [{base, Base},
+ {filter, Filter},
+ {attributes, []}]) of
+ #eldap_search_result{entries = [E | _]} ->
+ E;
+ _ ->
+ false
+ end.
+
+is_attribute_read_allowed(Name,From,To) ->
+ true.
+
+ldap_attribute_to_vcard(Prefix,{Name,Values},From,To) ->
+ case is_attribute_read_allowed(Name,From,To) of
+ true ->
+ ldap_lca_to_vcard(Prefix,stringprep:tolower(Name),Values);
+ _ ->
+ none
+ end.
+
+ldap_lca_to_vcard(vCard,"displayname",[Value|_]) ->
+ {xmlelement,"FN",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCard,"cn",[Value|_]) ->
+ {xmlelement,"NICKNAME",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCard,"title",[Value|_]) ->
+ {xmlelement,"TITLE",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCard,"wwwhomepage",[Value|_]) ->
+ {xmlelement,"URL",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCard,"description",[Value|_]) ->
+ {xmlelement,"DESC",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCard,"telephonenumber",[Value|_]) ->
+ {xmlelement,"TEL",[],[{xmlelement,"VOICE",[],[]},
+ {xmlelement,"WORK",[],[]},
+ {xmlelement,"NUMBER",[],[{xmlcdata,Value}]}]};
+
+ldap_lca_to_vcard(vCard,"mail",[Value|_]) ->
+ {xmlelement,"EMAIL",[],[{xmlelement,"INTERNET",[],[]},
+ {xmlelement,"PREF",[],[]},
+ {xmlelement,"USERID",[],[{xmlcdata,Value}]}]};
+
+ldap_lca_to_vcard(vCardN,"sn",[Value|_]) ->
+ {xmlelement,"FAMILY",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCardN,"givenname",[Value|_]) ->
+ {xmlelement,"GIVEN",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCardN,"initials",[Value|_]) ->
+ {xmlelement,"MIDDLE",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCardAdr,"streetaddress",[Value|_]) ->
+ {xmlelement,"STREET",[],[{xmlcdata,Value}]};
+ldap_lca_to_vcard(vCardAdr,"co",[Value|_]) ->
+ {xmlelement,"CTRY",[],[{xmlcdata,Value}]};
+ldap_lca_to_vcard(vCardAdr,"l",[Value|_]) ->
+ {xmlelement,"LOCALITY",[],[{xmlcdata,Value}]};
+ldap_lca_to_vcard(vCardAdr,"st",[Value|_]) ->
+ {xmlelement,"REGION",[],[{xmlcdata,Value}]};
+ldap_lca_to_vcard(vCardAdr,"postalcode",[Value|_]) ->
+ {xmlelement,"PCODE",[],[{xmlcdata,Value}]};
+ldap_lca_to_vcard(vCardO,"company",[Value|_]) ->
+ {xmlelement,"ORGNAME",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(vCardO,"department",[Value|_]) ->
+ {xmlelement,"ORGUNIT",[],[{xmlcdata,Value}]};
+
+ldap_lca_to_vcard(_,_,_) -> none.
+
+ldap_attributes_to_vcard(Attributes,From,To) ->
+ Elts = lists:map(fun(Attr) ->
+ ldap_attribute_to_vcard(vCard,Attr,From,To)
+ end,Attributes),
+ FElts = [ X || X <- Elts, X /= none ],
+ NElts = lists:map(fun(Attr) ->
+ ldap_attribute_to_vcard(vCardN,Attr,From,To)
+ end,Attributes),
+ FNElts = [ X || X <- NElts, X /= none ],
+
+ ADRElts = lists:map(fun(Attr) ->
+ ldap_attribute_to_vcard(vCardAdr,Attr,From,To)
+ end,Attributes),
+ FADRElts = [ X || X <- ADRElts, X /= none ],
+
+ OElts = lists:map(fun(Attr) ->
+ ldap_attribute_to_vcard(vCardO,Attr,From,To)
+ end,Attributes),
+ FOElts = [ X || X <- OElts, X /= none ],
+ [{xmlelement, "vCard", [{"xmlns", ?NS_VCARD}],
+ lists:append(FElts,
+ [{xmlelement,"N",[],FNElts},
+ {xmlelement,"ADR",[],FADRElts},
+ {xmlelement,"ORG",[],FOElts}])
+ }].
+
+process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
+ case Type of
+ set ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
+ get ->
+ #jid{luser = LUser, lserver = LServer} = To,
+ case find_ldap_user(LServer, LUser) of
+ #eldap_entry{attributes = Attributes} ->
+ Vcard = ldap_attributes_to_vcard(Attributes,From,To),
+ IQ#iq{type = result, sub_el = Vcard};
+ _ ->
+ IQ#iq{type = result, sub_el = []}
+ end
+ end.
+
+-define(TLFIELD(Type, Label, Var),
+ {xmlelement, "field", [{"type", Type},
+ {"label", translate:translate(Lang, Label)},
+ {"var", Var}], []}).
+
+
+-define(FORM(JID),
+ [{xmlelement, "instructions", [],
+ [{xmlcdata, translate:translate(Lang, "You need an x:data capable client to search")}]},
+ {xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}],
+ [{xmlelement, "title", [],
+ [{xmlcdata, translate:translate(Lang, "Search users in ") ++
+ jlib:jid_to_string(JID)}]},
+ {xmlelement, "instructions", [],
+ [{xmlcdata, translate:translate(Lang, "Fill in fields to search "
+ "for any matching Jabber User")}]},
+ ?TLFIELD("text-single", "User", "user"),
+ ?TLFIELD("text-single", "Full Name", "fn"),
+ ?TLFIELD("text-single", "Given Name", "given"),
+ ?TLFIELD("text-single", "Middle Name", "middle"),
+ ?TLFIELD("text-single", "Family Name", "family"),
+ ?TLFIELD("text-single", "Nickname", "nickname"),
+ ?TLFIELD("text-single", "Birthday", "bday"),
+ ?TLFIELD("text-single", "Country", "ctry"),
+ ?TLFIELD("text-single", "City", "locality"),
+ ?TLFIELD("text-single", "email", "email"),
+ ?TLFIELD("text-single", "Organization Name", "orgname"),
+ ?TLFIELD("text-single", "Organization Unit", "orgunit")
+ ]}]).
+
+
+
+
+do_route(ServerHost, From, To, Packet) ->
+ #jid{user = User, resource = Resource} = To,
+ if
+ (User /= "") or (Resource /= "") ->
+ Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE),
+ ejabberd_router ! {route, To, From, Err};
+ true ->
+ IQ = jlib:iq_query_info(Packet),
+ case IQ of
+ #iq{type = Type, xmlns = ?NS_SEARCH, lang = Lang, sub_el = SubEl} ->
+ case Type of
+ set ->
+ XDataEl = find_xdata_el(SubEl),
+ case XDataEl of
+ false ->
+ Err = jlib:make_error_reply(
+ Packet, ?ERR_BAD_REQUEST),
+ ejabberd_router:route(To, From, Err);
+ _ ->
+ XData = jlib:parse_xdata_submit(XDataEl),
+ case XData of
+ invalid ->
+ Err = jlib:make_error_reply(
+ Packet,
+ ?ERR_BAD_REQUEST),
+ ejabberd_router:route(To, From,
+ Err);
+ _ ->
+ ResIQ =
+ IQ#iq{
+ type = result,
+ sub_el =
+ [{xmlelement,
+ "query",
+ [{"xmlns", ?NS_SEARCH}],
+ [{xmlelement, "x",
+ [{"xmlns", ?NS_XDATA},
+ {"type", "result"}],
+ search_result(Lang, To, ServerHost, XData)
+ }]}]},
+ ejabberd_router:route(
+ To, From, jlib:iq_to_xml(ResIQ))
+ end
+ end;
+ get ->
+ ResIQ = IQ#iq{type = result,
+ sub_el = [{xmlelement,
+ "query",
+ [{"xmlns", ?NS_SEARCH}],
+ ?FORM(To)
+ }]},
+ ejabberd_router:route(To,
+ From,
+ jlib:iq_to_xml(ResIQ))
+ end;
+ #iq{type = Type, xmlns = ?NS_DISCO_INFO, sub_el = SubEl} ->
+ case Type of
+ set ->
+ Err = jlib:make_error_reply(
+ Packet, ?ERR_NOT_ALLOWED),
+ ejabberd_router:route(To, From, Err);
+ get ->
+ ResIQ =
+ IQ#iq{type = result,
+ sub_el = [{xmlelement,
+ "query",
+ [{"xmlns", ?NS_DISCO_INFO}],
+ [{xmlelement, "identity",
+ [{"category", "directory"},
+ {"type", "user"},
+ {"name",
+ "vCard User Search"}],
+ []},
+ {xmlelement, "feature",
+ [{"var", ?NS_SEARCH}], []},
+ {xmlelement, "feature",
+ [{"var", ?NS_VCARD}], []}
+ ]
+ }]},
+ ejabberd_router:route(To,
+ From,
+ jlib:iq_to_xml(ResIQ))
+ end;
+ #iq{type = Type, xmlns = ?NS_DISCO_ITEMS, sub_el = SubEl} ->
+ case Type of
+ set ->
+ Err = jlib:make_error_reply(
+ Packet, ?ERR_NOT_ALLOWED),
+ ejabberd_router:route(To, From, Err);
+ get ->
+ ResIQ =
+ IQ#iq{type = result,
+ sub_el = [{xmlelement,
+ "query",
+ [{"xmlns", ?NS_DISCO_ITEMS}],
+ []}]},
+ ejabberd_router:route(To,
+ From,
+ jlib:iq_to_xml(ResIQ))
+ end;
+ #iq{type = get, xmlns = ?NS_VCARD, lang = Lang} ->
+ ResIQ =
+ IQ#iq{type = result,
+ sub_el = [{xmlelement,
+ "vCard",
+ [{"xmlns", ?NS_VCARD}],
+ iq_get_vcard(Lang)}]},
+ ejabberd_router:route(To,
+ From,
+ jlib:iq_to_xml(ResIQ));
+ _ ->
+ Err = jlib:make_error_reply(Packet,
+ ?ERR_SERVICE_UNAVAILABLE),
+ ejabberd_router:route(To, From, Err)
+ end
+ end.
+
+iq_get_vcard(Lang) ->
+ [{xmlelement, "FN", [],
+ [{xmlcdata, "ejabberd/mod_vcard"}]},
+ {xmlelement, "URL", [],
+ [{xmlcdata,
+ "http://ejabberd.jabberstudio.org/"}]},
+ {xmlelement, "DESC", [],
+ [{xmlcdata, translate:translate(
+ Lang,
+ "ejabberd vCard module\n"
+ "Copyright (c) 2003-2005 Alexey Shchepin")}]}].
+
+find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
+ find_xdata_el1(SubEls).
+
+find_xdata_el1([]) ->
+ false;
+find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
+ case xml:get_attr_s("xmlns", Attrs) of
+ ?NS_XDATA ->
+ {xmlelement, Name, Attrs, SubEls};
+ _ ->
+ find_xdata_el1(Els)
+ end;
+find_xdata_el1([_ | Els]) ->
+ find_xdata_el1(Els).
+
+-define(LFIELD(Label, Var),
+ {xmlelement, "field", [{"label", translate:translate(Lang, Label)},
+ {"var", Var}], []}).
+
+search_result(Lang, JID, ServerHost, Data) ->
+ [{xmlelement, "title", [],
+ [{xmlcdata, translate:translate(Lang, "Results of search in ") ++
+ jlib:jid_to_string(JID)}]},
+ {xmlelement, "reported", [],
+ [?LFIELD("JID", "jid"),
+ ?LFIELD("Full Name", "fn"),
+ ?LFIELD("Given Name", "given"),
+ ?LFIELD("Middle Name", "middle"),
+ ?LFIELD("Family Name", "family"),
+ ?LFIELD("Nickname", "nickname"),
+ ?LFIELD("Birthday", "bday"),
+ ?LFIELD("Country", "ctry"),
+ ?LFIELD("City", "locality"),
+ ?LFIELD("email", "email"),
+ ?LFIELD("Organization Name", "orgname"),
+ ?LFIELD("Organization Unit", "orgunit")
+ ]}] ++ lists:map(fun(E) ->
+ record_to_item(E#eldap_entry.attributes)
+ end, search(ServerHost, Data)).
+
+-define(FIELD(Var, Val),
+ {xmlelement, "field", [{"var", Var}],
+ [{xmlelement, "value", [],
+ [{xmlcdata, Val}]}]}).
+
+case_exact_compare(none,_) ->
+ false;
+case_exact_compare(_,none) ->
+ false;
+case_exact_compare(X,Y) ->
+ X > Y.
+
+ldap_sort_entries(L) ->
+ lists:sort(fun(E1,E2) ->
+ case_exact_compare(ldap_get_value(E1,"cn"),ldap_get_value(E2,"cn"))
+ end,L).
+
+ldap_get_value(E,Attribute) ->
+ #eldap_entry{attributes = Attributes} = E,
+ case lists:filter(fun({A,_}) ->
+ string:equal(A,Attribute)
+ end,Attributes) of
+ [{Attr,[Value|_]}] ->
+ Value;
+ _ ->
+ none
+ end.
+
+ldap_attribute_to_item("samaccountname",Value) ->
+ [
+ ?FIELD("jid",Value ++ "@" ++ ?MYNAME),
+ ?FIELD("uid",Value)
+ ];
+
+ldap_attribute_to_item("cn",Value) ->
+ [
+ ?FIELD("nickname",Value)
+ ];
+
+ldap_attribute_to_item("displayname",Value) ->
+ [
+ ?FIELD("fn",Value)
+ ];
+
+ldap_attribute_to_item("sn",Value) ->
+ [
+ ?FIELD("family",Value)
+ ];
+ldap_attribute_to_item("co",Value) ->
+ [
+ ?FIELD("ctry",Value)
+ ];
+ldap_attribute_to_item("l",Value) ->
+ [
+ ?FIELD("locality",Value)
+ ];
+
+ldap_attribute_to_item("givenname",Value) ->
+ [
+ ?FIELD("given",Value)
+ ];
+
+ldap_attribute_to_item("initials",Value) ->
+ [
+ ?FIELD("middle",Value)
+ ];
+
+ldap_attribute_to_item("mail",Value) ->
+ [
+ ?FIELD("email",Value)
+ ];
+
+ldap_attribute_to_item("company",Value) ->
+ [
+ ?FIELD("orgname",Value)
+ ];
+
+ldap_attribute_to_item("department",Value) ->
+ [
+ ?FIELD("orgunit",Value)
+ ];
+
+ldap_attribute_to_item(_,_) ->
+ [none].
+
+record_to_item(Attributes) ->
+ List = lists:append(lists:map(fun({Attr,[Value|_]}) ->
+ ldap_attribute_to_item(stringprep:tolower(Attr),Value)
+ end,Attributes)),
+ FList = [X || X <- List, X /= none],
+ {xmlelement, "item", [],FList}.
+
+search(LServer, Data) ->
+% AdGroup = ejabberd_config:get_local_option({ad_group, LServer}),
+ FilterDef = make_filter(Data),
+ FilterPerson = eldap:equalityMatch("objectCategory", "person"),
+ FilterComp = eldap:equalityMatch("objectClass", "computer"),
+ FilterHidden = eldap:equalityMatch("description", "hidden"),
+% FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
+ FilterLive = eldap:equalityMatch("userAccountControl", "66050"),
+ Filter = eldap:'and'([
+ FilterDef,
+ FilterPerson,
+% FilterGroup,
+ eldap:'not'(FilterComp),
+ eldap:'not'(FilterHidden),
+ eldap:'not'(FilterLive)]),
+ Base = ejabberd_config:get_local_option({ad_base, LServer}),
+ UIDAttr = ejabberd_config:get_local_option({ad_uidattr, LServer}),
+ case eldap:search("mod_vcard_ad",[{base, Base},
+ {filter, Filter},
+ {attributes, []}]) of
+ #eldap_search_result{entries = E} ->
+ [X || X <- E,
+ ejabberd_auth:is_user_exists(
+ ldap_get_value(X, UIDAttr), LServer)];
+ Err ->
+ ?ERROR_MSG("Bad search: ~p", [[LServer, {base, Base},
+ {filter, Filter},
+ {attributes, []}]])
+ end.
+
+
+make_filter(Data) ->
+ Filter = [X || X <- lists:map(fun(R) ->
+ make_assertion(R)
+ end, Data),
+ X /= none ],
+ case Filter of
+ [F] ->
+ F;
+ _ ->
+ eldap:'and'(Filter)
+ end.
+
+
+make_assertion("givenName",Value) ->
+ eldap:substrings("givenName",[{any,Value}]);
+
+make_assertion("cn",Value) ->
+ eldap:substrings("cn",[{any,Value}]);
+
+make_assertion("sn",Value) ->
+ eldap:substrings("sn",[{any,Value}]);
+
+make_assertion(Attr, Value) ->
+ eldap:equalityMatch(Attr,Value).
+
+make_assertion({SVar, [Val]}) ->
+ LAttr = ldap_attribute(SVar),
+ case LAttr of
+ none ->
+ none;
+ _ ->
+ if
+ is_list(Val) and (Val /= "") ->
+ make_assertion(LAttr,Val);
+ true ->
+ none
+ end
+ end.
+
+ldap_attribute("user") ->
+ "samaccountname";
+
+ldap_attribute("fn") ->
+ "cn";
+
+ldap_attribute("family") ->
+ "sn";
+
+ldap_attribute("given") ->
+ "givenName";
+
+ldap_attribute("middle") ->
+ "initials";
+
+ldap_attribute("email") ->
+ "mail";
+
+ldap_attribute("orgname") ->
+ "company";
+
+ldap_attribute("orgunit") ->
+ "department";
+
+ldap_attribute(_) ->
+ none.
+
+remove_user(User) ->
+ true.
+
diff --git a/sources b/sources
index e69de29..b53a882 100644
--- a/sources
+++ b/sources
@@ -0,0 +1 @@
+ef6fae4a3f9c7f807f21e9cd3dae195b ejabberd-1.1.1.tar.gz
reply other threads:[~2026-09-23 19:06 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=179019038904.1.3746773836589880712.rpms-ejabberd-73b15bb9533c@fedoraproject.org \
--to=jcollie@fedoraproject.org \
--cc=git-commits@fedoraproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox