Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a8e49ed5ee | |||
| 3991450318 | |||
| 1f3acfc83f | |||
| 1e3faf8bd9 | |||
| 3fce8ed570 | |||
| b6abe1b926 | |||
| 69b11b677d | |||
| 8f41436cd8 | |||
| b0864396ec | |||
| dcac4ed8c8 | |||
| 74c202a714 | |||
| 10b22b52ef | |||
| a6145084f1 | |||
| 367002f89a | |||
| 75a8ebf293 | |||
| 4e17b1463b | |||
| 5c73d160cc | |||
| ac69fbb24d | |||
| 3454f2fe04 | |||
| 5374c0b069 | |||
| e4db030f4e | |||
| abbfa96fc4 | |||
| 55942d1f4a | |||
| 06abf6331b | |||
| 8be6b8df76 | |||
| a1e7e737bf | |||
| fa08801574 | |||
| dcb05c4061 | |||
| 6ea29663a3 | |||
| ad48607327 | |||
| fea7eac245 | |||
| b1f3d56beb | |||
| 7c6ccbb744 | |||
| 02d7d0aa4c | |||
| 7b10b69031 | |||
| 606b1d1b52 | |||
| 2cd587bec5 | |||
| f6ee802960 | |||
| e6ecb38ed9 | |||
| cd29a77759 | |||
| 677387bc80 | |||
| f362fe93ec | |||
| 9b6343faa4 |
@@ -1,3 +1,180 @@
|
||||
2009-04-01 Badlop <badlop@process-one.net>
|
||||
|
||||
* doc/release_notes_2.0.5.txt: Added file for new release
|
||||
|
||||
2009-03-25 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/expat_erl.c: Fix implicit declaration of function
|
||||
x_fix_buff (thanks to Dennis Schridde)(EJAB-900)
|
||||
|
||||
2009-03-24 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/ejabberd_sm.erl: Partially retract SVN r1976
|
||||
EJAB-300 (EJAB-890). Check default privacy list when account, not
|
||||
a specific session, receives a presence subscription
|
||||
stanza (EJAB-300).
|
||||
* src/ejabberd_c2s.erl: Likewise
|
||||
|
||||
2009-03-10 Badlop <badlop@process-one.net>
|
||||
|
||||
* doc/release_notes_2.0.4.txt: Added file for new release
|
||||
|
||||
* src/tls/tls_drv.c: Fix encryption problem for ejabberd_http
|
||||
after timeout (thanks to Alexey Shchepin)(EJAB-880)
|
||||
|
||||
2009-03-10 Christophe Romain <christophe.romain@process-one.net>
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: Fix PEP with other domains and s2s
|
||||
(EJAB-825). Also fixes send last published items in subscription.
|
||||
|
||||
2009-03-09 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/tls/tls_drv.c: Fix to support OpenSSL older than
|
||||
0.9.8f (EJAB-877)(thanks to Jonathan Schleifer)
|
||||
* doc/guide.tex: It is again supported OpenSSL older than 0.9.8f
|
||||
* doc/guide.html: Likewise
|
||||
|
||||
* src/mod_proxy65/mod_proxy65_service.erl: if an ip option is not
|
||||
defined, the module takes an IP address of a local
|
||||
hostname (thanks to Evgeniy Khramtsov)
|
||||
|
||||
2009-03-07 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/ejabberd_c2s.erl: Enforce privacy rules also for
|
||||
subscription requests (EJAB-300)
|
||||
* src/ejabberd_sm.erl: Likewise
|
||||
|
||||
2009-03-06 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/eldap/eldap.erl: moves waiting for response queries to
|
||||
pending queue on an LDAP connection failure (thanks to Evgeniy
|
||||
Khramtsov)
|
||||
|
||||
* src/eldap/eldap.erl: implemented queue for pending
|
||||
queries (thanks to Evgeniy Khramtsov)
|
||||
|
||||
* src/eldap/eldap.erl: Close a connection on tcp_error (thanks to
|
||||
Evgeniy Khramtsov)
|
||||
|
||||
* src/eldap/Makefile.in: added +optimize and +driver compilation
|
||||
options (thanks to Evgeniy Khramtsov)
|
||||
* src/eldap/Makefile.win32: Likewise
|
||||
|
||||
2009-03-05 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/ejabberd_app.erl: In a Windows machine, explicitly add the
|
||||
nameservers, as it seems Erlang does not do itself (EJAB-860)
|
||||
* src/win32_dns.erl: Get name servers from Windows registy (thanks
|
||||
to Geoff Cant)
|
||||
|
||||
* doc/guide.tex: Require OpenSSL 0.9.8f or higher (EJAB-877)
|
||||
* doc/guide.html: Likewise
|
||||
|
||||
2009-03-04 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/ejabberd_auth.erl: If anonymous auth is enabled, when
|
||||
checking if the account already exists in other auth methods, take
|
||||
into account if the auth method failed (EJAB-882)
|
||||
* src/ejabberd_auth_anonymous.erl: Likewise
|
||||
* src/ejabberd_auth_external.erl: Likewise
|
||||
* src/ejabberd_auth_internal.erl: Likewise
|
||||
* src/ejabberd_auth_ldap.erl: Likewise
|
||||
* src/ejabberd_auth_odbc.erl: Likewise
|
||||
* src/ejabberd_auth_pam.erl: Likewise
|
||||
|
||||
2009-03-04 Christophe Romain <christophe.romain@process-one.net>
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: Allow node creation without configure
|
||||
item
|
||||
|
||||
2009-03-03 Christophe Romain <christophe.romain@process-one.net>
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: Add roster subscriptions handling
|
||||
so on_sub_and_presence if fully supported.
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: Allow to send PEP events to all
|
||||
connected ressources, even via s2s.
|
||||
* src/mod_caps.erl: Likewise
|
||||
|
||||
2009-02-27 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/web/ejabberd_http.erl: Added a workaround for inet:peername
|
||||
returning 'ebadf'
|
||||
|
||||
* src/cyrsasl_digest.erl: Unquote backslash in DIGEST-MD5 quoted
|
||||
strings (EJAB-304)
|
||||
|
||||
* src/tls/tls_drv.c: S2S connection with STARTTLS fails to Gtalk
|
||||
and recent Openfire (thanks to Philipp Hancke)(EJAB-877)
|
||||
|
||||
* doc/guide.tex: No mention to the release date in ejabberd Guide
|
||||
or release notes. The date of an ejabberd release is determined by
|
||||
the date of the corresponding release announcement.
|
||||
|
||||
2009-02-23 Christophe Romain <christophe.romain@process-one.net>
|
||||
|
||||
* src/ejabberd_c2s.erl: Do not call mod_caps:clear_caps, this previous
|
||||
optimization is too agressive and breaks PubSub/PEP standard behavior
|
||||
(EJAB-854)
|
||||
|
||||
2009-02-21 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Owner of a password protected room
|
||||
must provide the password, like other participants (EJAB-867)
|
||||
|
||||
* src/mod_muc/mod_muc_log.erl: Prevent XSS in MUC logs by
|
||||
linkifying only a few known protocols (EJAB-850)
|
||||
|
||||
* src/mod_roster.erl: When account is deleted, cancel presence
|
||||
subscription for all roster items (EJAB-790)
|
||||
* src/mod_roster_odbc.erl: Likewise
|
||||
|
||||
* src/mod_shared_roster.erl: Fix bug: a pending subscription
|
||||
request, and later the requester added to the roster due to a
|
||||
shared roster group, that request could neither be accepted or
|
||||
rejected (thanks to Brian Cully)(EJAB-869)
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: The table pubsub_node in ejabberd
|
||||
older than 2.0.2 had indexes for parentid and type. This is not
|
||||
required since ejabberd 2.0.2, so those indexes can be
|
||||
deleted. (EJAB-669)
|
||||
|
||||
* doc/Makefile: In Clean do not remove html. In new Distclean,
|
||||
remove also html.
|
||||
|
||||
* doc/Makefile: When cleaning, remove contributed_modules.tex
|
||||
|
||||
* doc/guide.tex: Provide only an example of language option
|
||||
* doc/guide.html: Likewise
|
||||
|
||||
* doc/guide.tex: mod_muc can run in several nodes of cluster
|
||||
* doc/guide.html: Likewise
|
||||
|
||||
* src/odbc/mysql.sql: Fix complain about comment syntax
|
||||
* src/odbc/pg.sql: Likewise
|
||||
|
||||
* doc/guide.tex: Explain that account creation is only supported
|
||||
by internal and odbc authentication methods
|
||||
* doc/guide.html: Likewise
|
||||
|
||||
* src/mod_privacy.erl: Privacy list items must be processed in the
|
||||
specified order (EJAB-848)
|
||||
* src/mod_privacy_odbc.erl: Likewise
|
||||
|
||||
* src/ejabberd_c2s.erl: Ensure unique ID in roster push (EJAB-721)
|
||||
* src/mod_roster.erl: Likewise
|
||||
* src/mod_roster_odbc.erl: Likewise
|
||||
* src/mod_shared_roster.erl: Likewise
|
||||
|
||||
2009-02-19 Christophe Romain <christophe.romain@process-one.net>
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: fix nodetree plugin resolver
|
||||
|
||||
2009-02-13 Christophe Romain <christophe.romain@process-one.net>
|
||||
|
||||
* src/ejabberd_auth.erl: prevent from calling
|
||||
get_vh_registered_users/2 when not available
|
||||
|
||||
2009-01-13 Badlop <badlop@process-one.net>
|
||||
|
||||
* doc/release_notes_2.0.3.txt: Minor changes
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
% List of ejabberd-modules to add for ejabberd packaging (source archive and installer)
|
||||
%
|
||||
% HTTP-binding:
|
||||
https://svn.process-one.net/ejabberd-modules/http_bind/branches/ejabberd-2.0.x
|
||||
https://svn.process-one.net/ejabberd-modules/mod_http_fileserver/branches/ejabberd-2.0.x
|
||||
https://svn.process-one.net/ejabberd-modules/http_bind/tags/ejabberd-2.0.5
|
||||
https://svn.process-one.net/ejabberd-modules/mod_http_fileserver/tags/ejabberd-2.0.4
|
||||
|
||||
+4
-1
@@ -32,7 +32,6 @@ pdf: guide.pdf features.pdf
|
||||
clean:
|
||||
rm -f *.aux
|
||||
rm -f *.haux
|
||||
rm -f *.html
|
||||
rm -f *.htoc
|
||||
rm -f *.idx
|
||||
rm -f *.ilg
|
||||
@@ -41,6 +40,10 @@ clean:
|
||||
rm -f *.out
|
||||
rm -f *.pdf
|
||||
rm -f *.toc
|
||||
[ ! -f contributed_modules.tex ] || rm contributed_modules.tex
|
||||
|
||||
distclean: clean
|
||||
rm -f *.html
|
||||
|
||||
guide.html: guide.tex
|
||||
hevea -fix -pedantic guide.tex
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
"http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>Ejabberd 2.0.x Developers Guide
|
||||
<TITLE>Ejabberd 2.0.5 Developers Guide
|
||||
</TITLE>
|
||||
|
||||
<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
|
||||
@@ -49,7 +49,7 @@ TD P{margin:0px;}
|
||||
<!--HEVEA command line is: /usr/bin/hevea -fix -pedantic dev.tex -->
|
||||
<!--CUT DEF section 1 --><P><A NAME="titlepage"></A>
|
||||
|
||||
</P><TABLE CLASS="title"><TR><TD><H1 CLASS="titlemain">Ejabberd 2.0.x Developers Guide</H1><H3 CLASS="titlerest">Alexey Shchepin<BR>
|
||||
</P><TABLE CLASS="title"><TR><TD><H1 CLASS="titlemain">Ejabberd 2.0.5 Developers Guide</H1><H3 CLASS="titlerest">Alexey Shchepin<BR>
|
||||
<A HREF="mailto:alexey@sevcom.net"><TT>mailto:alexey@sevcom.net</TT></A><BR>
|
||||
<A HREF="xmpp:aleksey@jabber.ru"><TT>xmpp:aleksey@jabber.ru</TT></A></H3></TD></TR>
|
||||
</TABLE><DIV CLASS="center">
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
"http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>Ejabberd 2.0.x Feature Sheet
|
||||
<TITLE>Ejabberd 2.0.5 Feature Sheet
|
||||
</TITLE>
|
||||
|
||||
<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
|
||||
@@ -50,7 +50,7 @@ SPAN{width:20%; float:right; text-align:left; margin-left:auto;}
|
||||
<!--HEVEA command line is: /usr/bin/hevea -fix -pedantic features.tex -->
|
||||
<!--CUT DEF section 1 --><P><A NAME="titlepage"></A>
|
||||
|
||||
</P><TABLE CLASS="title"><TR><TD><H1 CLASS="titlemain">Ejabberd 2.0.x Feature Sheet</H1><H3 CLASS="titlerest">Sander Devrieze<BR>
|
||||
</P><TABLE CLASS="title"><TR><TD><H1 CLASS="titlemain">Ejabberd 2.0.5 Feature Sheet</H1><H3 CLASS="titlerest">Sander Devrieze<BR>
|
||||
<A HREF="mailto:s.devrieze@pandora.be"><TT>mailto:s.devrieze@pandora.be</TT></A><BR>
|
||||
<A HREF="xmpp:sander@devrieze.dyndns.org"><TT>xmpp:sander@devrieze.dyndns.org</TT></A></H3></TD></TR>
|
||||
</TABLE><DIV CLASS="center">
|
||||
|
||||
+17
-21
@@ -6,7 +6,7 @@
|
||||
|
||||
|
||||
|
||||
ejabberd 2.0.x
|
||||
ejabberd 2.0.5
|
||||
|
||||
Installation and Operation Guide
|
||||
|
||||
@@ -76,7 +76,7 @@ BLOCKQUOTE.figure DIV.center DIV.center HR{display:none;}
|
||||
<HR SIZE=2><BR>
|
||||
<BR>
|
||||
|
||||
<TABLE CELLSPACING=6 CELLPADDING=0><TR><TD ALIGN=right NOWRAP> <FONT SIZE=6><B>ejabberd 2.0.x </B></FONT></TD></TR>
|
||||
<TABLE CELLSPACING=6 CELLPADDING=0><TR><TD ALIGN=right NOWRAP> <FONT SIZE=6><B>ejabberd 2.0.5 </B></FONT></TD></TR>
|
||||
<TR><TD ALIGN=right NOWRAP> </TD></TR>
|
||||
<TR><TD ALIGN=right NOWRAP> <FONT SIZE=6>Installation and Operation Guide</FONT></TD></TR>
|
||||
</TABLE><BR>
|
||||
@@ -457,7 +457,7 @@ variable.
|
||||
</P></LI><LI CLASS="li-enumerate">Install OpenSSL in <CODE>C:\sdk\OpenSSL</CODE> and add <CODE>C:\sdk\OpenSSL\lib\VC</CODE> to your path or copy the binaries to your system directory.
|
||||
</LI><LI CLASS="li-enumerate">Install ZLib in <CODE>C:\sdk\gnuWin32</CODE>. Copy
|
||||
<CODE>C:\sdk\GnuWin32\bin\zlib1.dll</CODE> to your system directory. If you change your path it should already be set after libiconv install.
|
||||
</LI><LI CLASS="li-enumerate">Make sure the you can access Erlang binaries from your path. For example: <CODE>set PATH=%PATH%;"C:\sdk\erl5.5.5\bin"</CODE>
|
||||
</LI><LI CLASS="li-enumerate">Make sure the you can access Erlang binaries from your path. For example: <CODE>set PATH=%PATH%;"C:\sdk\erl5.6.5\bin"</CODE>
|
||||
</LI><LI CLASS="li-enumerate">Depending on how you end up actually installing the library you might need to check and tweak the paths in the file configure.erl.
|
||||
</LI><LI CLASS="li-enumerate">While in the directory <CODE>ejabberd\src</CODE> run:
|
||||
<PRE CLASS="verbatim">configure.bat
|
||||
@@ -892,7 +892,7 @@ example authentication scripts</A>.
|
||||
<A HREF="#mssql">3.2.2</A> and <A HREF="#odbc">3.2.4</A>.
|
||||
</LI><LI CLASS="li-itemize">anonymous — See section <A HREF="#saslanonymous">3.1.4</A>.
|
||||
</LI><LI CLASS="li-itemize">pam — See section <A HREF="#pam">3.1.4</A>.
|
||||
</LI></UL><P> <A NAME="internalauth"></A> </P><!--TOC subsubsection Internal-->
|
||||
</LI></UL><P>Account creation is only supported by internal and odbc methods.</P><P> <A NAME="internalauth"></A> </P><!--TOC subsubsection Internal-->
|
||||
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#internalauth">Internal</A></H4><!--SEC END --><P> <A NAME="internalauth"></A>
|
||||
</P><P><TT>ejabberd</TT> uses its internal Mnesia database as the default authentication method.</P><UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
<TT>auth_method</TT>: The value <TT>internal</TT> will enable the internal
|
||||
@@ -1119,13 +1119,9 @@ To define a shaper named ‘<TT>normal</TT>’ with traffic speed limi
|
||||
can be seen by Jabber clients. If a Jabber client do not support
|
||||
<TT>xml:lang</TT>, the specified language is used. The default value is
|
||||
<TT>en</TT>. In order to take effect there must be a translation file
|
||||
<TT><language>.msg</TT> in <TT>ejabberd</TT>’s <TT>msgs</TT> directory.</P><P>Examples:
|
||||
</P><UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
To set Russian as default language:
|
||||
<PRE CLASS="verbatim">{language, "ru"}.
|
||||
</PRE></LI><LI CLASS="li-itemize">To set Spanish as default language:
|
||||
<PRE CLASS="verbatim">{language, "es"}.
|
||||
</PRE></LI></UL><P> <A NAME="database"></A> </P><!--TOC section Database and LDAP Configuration-->
|
||||
<TT><language>.msg</TT> in <TT>ejabberd</TT>’s <TT>msgs</TT> directory.</P><P>For example, to set Russian as default language:
|
||||
</P><PRE CLASS="verbatim">{language, "ru"}.
|
||||
</PRE><P> <A NAME="database"></A> </P><!--TOC section Database and LDAP Configuration-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc28">3.2</A>  <A HREF="#database">Database and LDAP Configuration</A></H2><!--SEC END --><P> <A NAME="database"></A>
|
||||
|
||||
</P><P><TT>ejabberd</TT> uses its internal Mnesia database by default. However, it is
|
||||
@@ -1152,7 +1148,7 @@ if you define several domains in ejabberd.cfg (see section <A HREF="#hostnames">
|
||||
you probably want that each virtual host uses a different configuration of database, authentication and storage,
|
||||
so that usernames do not conflict and mix between different virtual hosts.
|
||||
For that purpose, the options described in the next sections
|
||||
must be set inside a <TT>host_cofig</TT> for each vhost (see section <A HREF="#virtualhost">3.1.2</A>).
|
||||
must be set inside a <TT>host_config</TT> for each vhost (see section <A HREF="#virtualhost">3.1.2</A>).
|
||||
For example:
|
||||
</P><PRE CLASS="verbatim">{host_config, "public.example.org", [
|
||||
{odbc_server, {pgsql, "localhost", "database-public-example-org", "ejabberd", "password"}},
|
||||
@@ -2344,7 +2340,7 @@ In the body you can set a newline with the characters: <CODE>\n</CODE>
|
||||
list of JIDs which will be notified each time a new account is registered.
|
||||
</DD><DT CLASS="dt-description"><B><TT>iqdisc</TT></B></DT><DD CLASS="dd-description"> This specifies
|
||||
the processing discipline for In-Band Registration (<TT>jabber:iq:register</TT>) IQ queries (see section <A HREF="#modiqdiscoption">3.3.2</A>).
|
||||
</DD></DL><P>This module reads also another option defined globably for the server:
|
||||
</DD></DL><P>This module reads also another option defined globally for the server:
|
||||
<TT>{registration_timeout, Timeout}</TT>.
|
||||
This option limits the frequency of registration from a given IP or username.
|
||||
So, a user can’t register a new account from the same IP address or JID during
|
||||
@@ -2809,7 +2805,7 @@ they are automatically renamed to <TT>"*-old.log"</TT>. See section <A HREF="#lo
|
||||
Store internal Mnesia database to a binary backup file.
|
||||
</DD><DT CLASS="dt-description"><B><TT>restore ejabberd.backup</TT></B></DT><DD CLASS="dd-description">
|
||||
Restore immediately from a binary backup file the internal Mnesia database.
|
||||
This will comsume quite some memory for big servers.
|
||||
This will consume quite some memory for big servers.
|
||||
</DD><DT CLASS="dt-description"><B><TT>install-fallback ejabberd.backup</TT></B></DT><DD CLASS="dd-description">
|
||||
The binary backup file is installed as fallback:
|
||||
it will be used to restore the database at the next ejabberd start.
|
||||
@@ -2831,9 +2827,9 @@ This allows to administer a remote node.</P><P>The <TT>ejabberdctl</TT> script c
|
||||
This file includes detailed information about each configurable option.</P><P>The <TT>ejabberdctl</TT> script returns a numerical status code.
|
||||
Success is represented by <TT>0</TT>,
|
||||
error is represented by <TT>1</TT>,
|
||||
and other codes may be used for specifical results.
|
||||
and other codes may be used for specific results.
|
||||
This can be used by other scripts to determine automatically
|
||||
if a command succedded or failed,
|
||||
if a command succeeded or failed,
|
||||
for example using: <TT>echo $?</TT></P><P> <A NAME="erlangconfiguration"></A> </P><!--TOC subsection Erlang Runtime System-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc61">4.1.2</A>  <A HREF="#erlangconfiguration">Erlang Runtime System</A></H3><!--SEC END --><P> <A NAME="erlangconfiguration"></A> </P><P><TT>ejabberd</TT> is an Erlang/OTP application that runs inside an Erlang runtime system.
|
||||
This system is configured using environment variables and command line parameters.
|
||||
@@ -3128,11 +3124,11 @@ Writing, on the other hand, will be slower. And of course if machine with one
|
||||
of the replicas is down, other replicas will be used.</P><P>Also <A HREF="http://www.erlang.org/doc/apps/mnesia/Mnesia_chap5.html#5.3">section 5.3 (Table Fragmentation) of Mnesia User’s Guide</A> can be helpful.
|
||||
</P><P>(alt) Same as in previous item, but for other tables.</P></LI><LI CLASS="li-enumerate">Run ‘<CODE>init:stop().</CODE>’ or just ‘<CODE>q().</CODE>’ to exit from
|
||||
the Erlang shell. This probably can take some time if Mnesia has not yet
|
||||
transfered and processed all data it needed from <TT>first</TT>.</LI><LI CLASS="li-enumerate">Now run <TT>ejabberd</TT> on <TT>second</TT> with almost the same config as
|
||||
on <TT>first</TT> (you probably do not need to duplicate ‘<CODE>acl</CODE>’
|
||||
and ‘<CODE>access</CODE>’ options — they will be taken from
|
||||
<TT>first</TT>, and <CODE>mod_muc</CODE> and <CODE>mod_irc</CODE> should be
|
||||
enabled only on one machine in the cluster).
|
||||
transfered and processed all data it needed from <TT>first</TT>.</LI><LI CLASS="li-enumerate">Now run <TT>ejabberd</TT> on <TT>second</TT> with a configuration similar as
|
||||
on <TT>first</TT>: you probably do not need to duplicate ‘<CODE>acl</CODE>’
|
||||
and ‘<CODE>access</CODE>’ options because they will be taken from
|
||||
<TT>first</TT>; and <CODE>mod_irc</CODE> should be
|
||||
enabled only on one machine in the cluster.
|
||||
</LI></OL><P>You can repeat these steps for other machines supposed to serve this
|
||||
domain.</P><P> <A NAME="servicelb"></A> </P><!--TOC section Service Load-Balancing-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc78">6.3</A>  <A HREF="#servicelb">Service Load-Balancing</A></H2><!--SEC END --><P> <A NAME="servicelb"></A>
|
||||
|
||||
+13
-21
@@ -126,9 +126,6 @@ the processing discipline for #1 IQ queries (see section~\ref{modiqdiscoption}).
|
||||
{\rule{\larg}{1mm}}
|
||||
\begin{latexonly}
|
||||
\vspace{2mm} \\
|
||||
\begin{center}
|
||||
{\large \bf \today}
|
||||
\end{center}
|
||||
\vspace{5.5cm}
|
||||
\end{latexonly}
|
||||
}
|
||||
@@ -1035,6 +1032,8 @@ The following authentication methods are supported by \ejabberd{}:
|
||||
\item pam --- See section~\ref{pam}.
|
||||
\end{itemize}
|
||||
|
||||
Account creation is only supported by internal and odbc methods.
|
||||
|
||||
\makesubsubsection{internalauth}{Internal}
|
||||
\ind{internal authentication}\ind{Mnesia}
|
||||
|
||||
@@ -1387,17 +1386,10 @@ can be seen by \Jabber{} clients. If a \Jabber{} client do not support
|
||||
\term{en}. In order to take effect there must be a translation file
|
||||
\term{<language>.msg} in \ejabberd{}'s \term{msgs} directory.
|
||||
|
||||
Examples:
|
||||
\begin{itemize}
|
||||
\item To set Russian as default language:
|
||||
For example, to set Russian as default language:
|
||||
\begin{verbatim}
|
||||
{language, "ru"}.
|
||||
\end{verbatim}
|
||||
\item To set Spanish as default language:
|
||||
\begin{verbatim}
|
||||
{language, "es"}.
|
||||
\end{verbatim}
|
||||
\end{itemize}
|
||||
|
||||
\makesection{database}{Database and LDAP Configuration}
|
||||
\ind{database}
|
||||
@@ -1433,7 +1425,7 @@ if you define several domains in ejabberd.cfg (see section \ref{hostnames}),
|
||||
you probably want that each virtual host uses a different configuration of database, authentication and storage,
|
||||
so that usernames do not conflict and mix between different virtual hosts.
|
||||
For that purpose, the options described in the next sections
|
||||
must be set inside a \term{host\_cofig} for each vhost (see section \ref{virtualhost}).
|
||||
must be set inside a \term{host\_config} for each vhost (see section \ref{virtualhost}).
|
||||
For example:
|
||||
\begin{verbatim}
|
||||
{host_config, "public.example.org", [
|
||||
@@ -2994,7 +2986,7 @@ Options:
|
||||
\iqdiscitem{In-Band Registration (\ns{jabber:iq:register})}
|
||||
\end{description}
|
||||
|
||||
This module reads also another option defined globably for the server:
|
||||
This module reads also another option defined globally for the server:
|
||||
\term{\{registration\_timeout, Timeout\}}. \ind{options!registratimeout}
|
||||
This option limits the frequency of registration from a given IP or username.
|
||||
So, a user can't register a new account from the same IP address or JID during
|
||||
@@ -3584,7 +3576,7 @@ The more interesting ones are:
|
||||
Store internal Mnesia database to a binary backup file.
|
||||
\titem {restore ejabberd.backup}
|
||||
Restore immediately from a binary backup file the internal Mnesia database.
|
||||
This will comsume quite some memory for big servers.
|
||||
This will consume quite some memory for big servers.
|
||||
\titem {install-fallback ejabberd.backup}
|
||||
The binary backup file is installed as fallback:
|
||||
it will be used to restore the database at the next ejabberd start.
|
||||
@@ -3614,9 +3606,9 @@ This file includes detailed information about each configurable option.
|
||||
The \term{ejabberdctl} script returns a numerical status code.
|
||||
Success is represented by \term{0},
|
||||
error is represented by \term{1},
|
||||
and other codes may be used for specifical results.
|
||||
and other codes may be used for specific results.
|
||||
This can be used by other scripts to determine automatically
|
||||
if a command succedded or failed,
|
||||
if a command succeeded or failed,
|
||||
for example using: \term{echo \$?}
|
||||
|
||||
|
||||
@@ -4048,11 +4040,11 @@ mnesia:change_table_copy_type(schema, node(), disc_copies).
|
||||
transfered and processed all data it needed from \term{first}.
|
||||
|
||||
|
||||
\item Now run \ejabberd{} on \term{second} with almost the same config as
|
||||
on \term{first} (you probably do not need to duplicate `\verb|acl|'
|
||||
and `\verb|access|' options --- they will be taken from
|
||||
\term{first}, and \verb|mod_muc| and \verb|mod_irc| should be
|
||||
enabled only on one machine in the cluster).
|
||||
\item Now run \ejabberd{} on \term{second} with a configuration similar as
|
||||
on \term{first}: you probably do not need to duplicate `\verb|acl|'
|
||||
and `\verb|access|' options because they will be taken from
|
||||
\term{first}; and \verb|mod_irc| should be
|
||||
enabled only on one machine in the cluster.
|
||||
\end{enumerate}
|
||||
|
||||
You can repeat these steps for other machines supposed to serve this
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.0.4
|
||||
|
||||
ejabberd 2.0.4 is the fourth bugfix release for ejabberd 2.0.x branch.
|
||||
|
||||
ejabberd 2.0.4 includes several bugfixes.
|
||||
A detailed list of changes can be retrieved from:
|
||||
http://redir.process-one.net/ejabberd-2.0.4
|
||||
|
||||
The new code can be downloaded from ejabberd download page:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
The changes are:
|
||||
|
||||
- Ensure ID attribute in roster push is unique
|
||||
- Authentication: Fix Anonymous auth when enabled with broken ODBC
|
||||
- Authentication: Unquote correctly backslash in DIGEST-MD5 SASL responses
|
||||
- Authentication: Cancel presence subscriptions on account deletion
|
||||
- LDAP: Close a connection on tcp_error
|
||||
- LDAP: Implemented queue for pending queries
|
||||
- LDAP: On failure of LDAP connection, waiting is done on pending queue
|
||||
- MUC: Owner of a password protected room must also provide the password
|
||||
- MUC: Prevent XSS in MUC logs by linkifying only a few known protocols
|
||||
- Privacy rules: Items are now processed in the specified order
|
||||
- Privacy rules: Fix to correctly block subscription requests
|
||||
- Proxy65: If ip option is not defined, take an IP address of a local hostname
|
||||
- PubSub: Add roster subscription handling; send PEP events to all resources
|
||||
- PubSub: Allow node creation without configure item
|
||||
- PubSub: Requesting items on a node which exists, but empty returns an error
|
||||
- PEP: Fix sending notifications to other domains and s2s
|
||||
- S2S: Fix problem with encrypted connection to Gtalk and recent Openfire
|
||||
- S2S: Workaround to get DNS SRV lookup to work on Windows machine
|
||||
- Shared Roster Groups: Fix to not resend authorization request
|
||||
- WebAdmin: Fix encryption problem for ejabberd_http after timeout
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
|
||||
END
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.0.5
|
||||
|
||||
ejabberd 2.0.5 is the fifth bugfix release in ejabberd 2.0.x branch.
|
||||
|
||||
ejabberd 2.0.5 includes three bugfixes.
|
||||
More details of those fixes can be retrieved from:
|
||||
http://redir.process-one.net/ejabberd-2.0.5
|
||||
|
||||
The new code can be downloaded from ejabberd download page:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
The changes are:
|
||||
|
||||
- Fix two problems introduced in ejabberd 2.0.4: subscription request
|
||||
produced many authorization requests with some clients and
|
||||
transports; and subscription requests were not stored for later
|
||||
delivery when receiver was offline.
|
||||
|
||||
- Fix warning in expat_erl.c about implicit declaration of x_fix_buff
|
||||
|
||||
- HTTP-Bind (BOSH): Fix a missing stream:error in the returned
|
||||
remote-stream-error stanza
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
|
||||
END
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
% ejabberd version (automatically generated).
|
||||
\newcommand{\version}{2.0.x}
|
||||
\newcommand{\version}{2.0.5}
|
||||
|
||||
@@ -101,15 +101,17 @@ parse1([], [], T) ->
|
||||
parse1([], _S, _T) ->
|
||||
bad.
|
||||
|
||||
parse2([$" | Cs], Key, Val, Ts) ->
|
||||
parse2([$\" | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, Val, Ts);
|
||||
parse2([C | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, [C | Val], Ts);
|
||||
parse2([], _, _, _) ->
|
||||
bad.
|
||||
|
||||
parse3([$" | Cs], Key, Val, Ts) ->
|
||||
parse3([$\" | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, Val, Ts);
|
||||
parse3([$\\, C | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, [C | Val], Ts);
|
||||
parse3([C | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, [C | Val], Ts);
|
||||
parse3([], _, _, _) ->
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
{application, ejabberd,
|
||||
[{description, "ejabberd"},
|
||||
{vsn, "2.0.x"},
|
||||
{vsn, "2.0.5"},
|
||||
{modules, [acl,
|
||||
adhoc,
|
||||
configure,
|
||||
|
||||
@@ -62,6 +62,7 @@ start(normal, _Args) ->
|
||||
%eprof:start(),
|
||||
%eprof:profile([self()]),
|
||||
%fprof:trace(start, "/tmp/fprof"),
|
||||
maybe_add_nameservers(),
|
||||
start_modules(),
|
||||
Sup;
|
||||
start(_, _) ->
|
||||
@@ -168,3 +169,15 @@ connect_nodes() ->
|
||||
end, Nodes)
|
||||
end.
|
||||
|
||||
|
||||
%% If ejabberd is running on some Windows machine, get nameservers and add to Erlang
|
||||
maybe_add_nameservers() ->
|
||||
case os:type() of
|
||||
{win32, _} -> add_windows_nameservers();
|
||||
_ -> ok
|
||||
end.
|
||||
|
||||
add_windows_nameservers() ->
|
||||
IPTs = win32_dns:get_nameservers(),
|
||||
?INFO_MSG("Adding machine's DNS IPs to Erlang system:~n~p", [IPTs]),
|
||||
lists:foreach(fun(IPT) -> inet_db:add_ns(IPT) end, IPTs).
|
||||
|
||||
+48
-30
@@ -80,20 +80,21 @@ plain_password_required(Server) ->
|
||||
%% @spec (User::string(), Server::string(), Password::string()) ->
|
||||
%% true | false
|
||||
check_password(User, Server, Password) ->
|
||||
lists:any(
|
||||
fun(M) ->
|
||||
M:check_password(User, Server, Password)
|
||||
end, auth_modules(Server)).
|
||||
case check_password_with_authmodule(User, Server, Password) of
|
||||
{true, _AuthModule} -> true;
|
||||
false -> false
|
||||
end.
|
||||
|
||||
%% @doc Check if the user and password can login in server.
|
||||
%% @spec (User::string(), Server::string(), Password::string(),
|
||||
%% StreamID::string(), Digest::string()) ->
|
||||
%% true | false
|
||||
check_password(User, Server, Password, StreamID, Digest) ->
|
||||
lists:any(
|
||||
fun(M) ->
|
||||
M:check_password(User, Server, Password, StreamID, Digest)
|
||||
end, auth_modules(Server)).
|
||||
case check_password_with_authmodule(User, Server, Password,
|
||||
StreamID, Digest) of
|
||||
{true, _AuthModule} -> true;
|
||||
false -> false
|
||||
end.
|
||||
|
||||
%% @doc Check if the user and password can login in server.
|
||||
%% The user can login if at least an authentication method accepts the user
|
||||
@@ -106,27 +107,23 @@ check_password(User, Server, Password, StreamID, Digest) ->
|
||||
%% | ejabberd_auth_internal | ejabberd_auth_ldap
|
||||
%% | ejabberd_auth_odbc | ejabberd_auth_pam
|
||||
check_password_with_authmodule(User, Server, Password) ->
|
||||
Res = lists:dropwhile(
|
||||
fun(M) ->
|
||||
not apply(M, check_password,
|
||||
[User, Server, Password])
|
||||
end, auth_modules(Server)),
|
||||
case Res of
|
||||
[] -> false;
|
||||
[AuthMod | _] -> {true, AuthMod}
|
||||
end.
|
||||
check_password_loop(auth_modules(Server), [User, Server, Password]).
|
||||
|
||||
check_password_with_authmodule(User, Server, Password, StreamID, Digest) ->
|
||||
Res = lists:dropwhile(
|
||||
fun(M) ->
|
||||
not apply(M, check_password,
|
||||
[User, Server, Password, StreamID, Digest])
|
||||
end, auth_modules(Server)),
|
||||
case Res of
|
||||
[] -> false;
|
||||
[AuthMod | _] -> {true, AuthMod}
|
||||
check_password_loop(auth_modules(Server), [User, Server, Password,
|
||||
StreamID, Digest]).
|
||||
|
||||
check_password_loop([], _Args) ->
|
||||
false;
|
||||
check_password_loop([AuthModule | AuthModules], Args) ->
|
||||
case apply(AuthModule, check_password, Args) of
|
||||
true ->
|
||||
{true, AuthModule};
|
||||
false ->
|
||||
check_password_loop(AuthModules, Args)
|
||||
end.
|
||||
|
||||
|
||||
%% @spec (User::string(), Server::string(), Password::string()) ->
|
||||
%% ok | {error, ErrorType}
|
||||
%% where ErrorType = empty_password | not_allowed | invalid_jid
|
||||
@@ -187,7 +184,13 @@ get_vh_registered_users(Server) ->
|
||||
get_vh_registered_users(Server, Opts) ->
|
||||
lists:flatmap(
|
||||
fun(M) ->
|
||||
M:get_vh_registered_users(Server, Opts)
|
||||
case erlang:function_exported(
|
||||
M, get_vh_registered_users, 2) of
|
||||
true ->
|
||||
M:get_vh_registered_users(Server, Opts);
|
||||
false ->
|
||||
M:get_vh_registered_users(Server)
|
||||
end
|
||||
end, auth_modules(Server)).
|
||||
|
||||
get_vh_registered_users_number(Server) ->
|
||||
@@ -255,11 +258,26 @@ is_user_exists(User, Server) ->
|
||||
|
||||
%% Check if the user exists in all authentications module except the module
|
||||
%% passed as parameter
|
||||
%% @spec (Module::atom(), User, Server) -> true | false | maybe
|
||||
is_user_exists_in_other_modules(Module, User, Server) ->
|
||||
lists:any(
|
||||
fun(M) ->
|
||||
M:is_user_exists(User, Server)
|
||||
end, auth_modules(Server)--[Module]).
|
||||
is_user_exists_in_other_modules_loop(
|
||||
auth_modules(Server)--[Module],
|
||||
User, Server).
|
||||
is_user_exists_in_other_modules_loop([], _User, _Server) ->
|
||||
false;
|
||||
is_user_exists_in_other_modules_loop([AuthModule|AuthModules], User, Server) ->
|
||||
case AuthModule:is_user_exists(User, Server) of
|
||||
true ->
|
||||
true;
|
||||
false ->
|
||||
is_user_exists_in_other_modules_loop(AuthModules, User, Server);
|
||||
{error, Error} ->
|
||||
?DEBUG("The authentication module ~p returned an error~nwhen "
|
||||
"checking user ~p in server ~p~nError message: ~p",
|
||||
[AuthModule, User, Server, Error]),
|
||||
maybe
|
||||
end.
|
||||
|
||||
|
||||
%% @spec (User, Server) -> ok | error | {error, not_allowed}
|
||||
%% @doc Remove user.
|
||||
|
||||
@@ -178,7 +178,10 @@ check_password(User, Server, _Password, _StreamID, _Digest) ->
|
||||
%% they however are "reserved")
|
||||
case ejabberd_auth:is_user_exists_in_other_modules(?MODULE,
|
||||
User, Server) of
|
||||
%% If user exists in other module, reject anonnymous authentication
|
||||
true -> false;
|
||||
%% If we are not sure whether the user exists in other module, reject anon auth
|
||||
maybe -> false;
|
||||
false -> login(User, Server)
|
||||
end.
|
||||
|
||||
|
||||
@@ -83,8 +83,13 @@ get_password(_User, _Server) ->
|
||||
get_password_s(_User, _Server) ->
|
||||
"".
|
||||
|
||||
%% @spec (User, Server) -> true | false | {error, Error}
|
||||
is_user_exists(User, Server) ->
|
||||
extauth:is_user_exists(User, Server).
|
||||
try extauth:is_user_exists(User, Server) of
|
||||
Res -> Res
|
||||
catch
|
||||
_:Error -> {error, Error}
|
||||
end.
|
||||
|
||||
remove_user(_User, _Server) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
@@ -230,6 +230,7 @@ get_password_s(User, Server) ->
|
||||
[]
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> true | false | {error, Error}
|
||||
is_user_exists(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
@@ -239,8 +240,8 @@ is_user_exists(User, Server) ->
|
||||
false;
|
||||
[_] ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
Other ->
|
||||
{error, Other}
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> ok
|
||||
|
||||
@@ -186,10 +186,11 @@ get_password(_User, _Server) ->
|
||||
get_password_s(_User, _Server) ->
|
||||
"".
|
||||
|
||||
%% @spec (User, Server) -> true | false | {error, Error}
|
||||
is_user_exists(User, Server) ->
|
||||
case catch is_user_exists_ldap(User, Server) of
|
||||
{'EXIT', _} ->
|
||||
false;
|
||||
{'EXIT', Error} ->
|
||||
{error, Error};
|
||||
Result ->
|
||||
Result
|
||||
end.
|
||||
|
||||
+32
-11
@@ -61,6 +61,7 @@ start(Host) ->
|
||||
plain_password_required() ->
|
||||
false.
|
||||
|
||||
%% @spec (User, Server, Password) -> true | false | {error, Error}
|
||||
check_password(User, Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
@@ -68,14 +69,22 @@ check_password(User, Server, Password) ->
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch odbc_queries:get_password(LServer, Username) of
|
||||
try odbc_queries:get_password(LServer, Username) of
|
||||
{selected, ["password"], [{Password}]} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
true; %% Password is correct
|
||||
{selected, ["password"], [{_Password2}]} ->
|
||||
false; %% Password is not correct
|
||||
{selected, ["password"], []} ->
|
||||
false; %% Account does not exist
|
||||
{error, _Error} ->
|
||||
false %% Typical error is that table doesn't exist
|
||||
catch
|
||||
_:_ ->
|
||||
false %% Typical error is database not accessible
|
||||
end
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password, StreamID, Digest) -> true | false | {error, Error}
|
||||
check_password(User, Server, Password, StreamID, Digest) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
@@ -83,7 +92,8 @@ check_password(User, Server, Password, StreamID, Digest) ->
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch odbc_queries:get_password(LServer, Username) of
|
||||
try odbc_queries:get_password(LServer, Username) of
|
||||
%% Account exists, check if password is valid
|
||||
{selected, ["password"], [{Passwd}]} ->
|
||||
DigRes = if
|
||||
Digest /= "" ->
|
||||
@@ -96,8 +106,13 @@ check_password(User, Server, Password, StreamID, Digest) ->
|
||||
true ->
|
||||
(Passwd == Password) and (Password /= "")
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
{selected, ["password"], []} ->
|
||||
false; %% Account does not exist
|
||||
{error, _Error} ->
|
||||
false %% Typical error is that table doesn't exist
|
||||
catch
|
||||
_:_ ->
|
||||
false %% Typical error is database not accessible
|
||||
end
|
||||
end.
|
||||
|
||||
@@ -208,6 +223,7 @@ get_password_s(User, Server) ->
|
||||
end
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> true | false | {error, Error}
|
||||
is_user_exists(User, Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
@@ -215,11 +231,16 @@ is_user_exists(User, Server) ->
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch odbc_queries:get_password(LServer, Username) of
|
||||
try odbc_queries:get_password(LServer, Username) of
|
||||
{selected, ["password"], [{_Password}]} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
true; %% Account exists
|
||||
{selected, ["password"], []} ->
|
||||
false; %% Account does not exist
|
||||
{error, Error} ->
|
||||
{error, Error} %% Typical error is that table doesn't exist
|
||||
catch
|
||||
_:B ->
|
||||
{error, B} %% Typical error is database not accessible
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
@@ -80,6 +80,8 @@ get_password(_User, _Server) ->
|
||||
get_password_s(_User, _Server) ->
|
||||
"".
|
||||
|
||||
%% @spec (User, Server) -> true | false | {error, Error}
|
||||
%% TODO: Improve this function to return an error instead of 'false' when connection to PAM failed
|
||||
is_user_exists(User, Host) ->
|
||||
Service = get_pam_service(Host),
|
||||
case catch epam:acct_mgmt(Service, User) of
|
||||
|
||||
+25
-6
@@ -1091,13 +1091,17 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
Attrs1 = lists:keydelete("type", 1, Attrs),
|
||||
{true, [{"type", "unavailable"} | Attrs1], StateData};
|
||||
"subscribe" ->
|
||||
{true, Attrs, StateData};
|
||||
SRes = is_privacy_allow(From, To, Packet, StateData#state.privacy_list),
|
||||
{SRes, Attrs, StateData};
|
||||
"subscribed" ->
|
||||
{true, Attrs, StateData};
|
||||
SRes = is_privacy_allow(From, To, Packet, StateData#state.privacy_list),
|
||||
{SRes, Attrs, StateData};
|
||||
"unsubscribe" ->
|
||||
{true, Attrs, StateData};
|
||||
SRes = is_privacy_allow(From, To, Packet, StateData#state.privacy_list),
|
||||
{SRes, Attrs, StateData};
|
||||
"unsubscribed" ->
|
||||
{true, Attrs, StateData};
|
||||
SRes = is_privacy_allow(From, To, Packet, StateData#state.privacy_list),
|
||||
{SRes, Attrs, StateData};
|
||||
_ ->
|
||||
case ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, StateData#state.server,
|
||||
@@ -1113,7 +1117,9 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
%% Note contact availability
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"unavailable" ->
|
||||
mod_caps:clear_caps(From);
|
||||
%mod_caps:clear_caps(From);
|
||||
% caps clear disabled cause it breaks things
|
||||
ok;
|
||||
_ ->
|
||||
Caps = mod_caps:read_caps(Els),
|
||||
mod_caps:note_caps(StateData#state.server, From, Caps)
|
||||
@@ -1171,7 +1177,7 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
NewPL ->
|
||||
PrivPushIQ =
|
||||
#iq{type = set, xmlns = ?NS_PRIVACY,
|
||||
id = "push",
|
||||
id = "push" ++ randoms:get_string(),
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_PRIVACY}],
|
||||
[{xmlelement, "list",
|
||||
@@ -1617,6 +1623,19 @@ presence_track(From, To, Packet, StateData) ->
|
||||
pres_a = A}
|
||||
end.
|
||||
|
||||
%% Check if privacy rules allow this delivery
|
||||
is_privacy_allow(From, To, Packet, PrivacyList) ->
|
||||
User = To#jid.user,
|
||||
Server = To#jid.server,
|
||||
allow == ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, Server,
|
||||
allow,
|
||||
[User,
|
||||
Server,
|
||||
PrivacyList,
|
||||
{From, To, Packet},
|
||||
in]).
|
||||
|
||||
presence_broadcast(StateData, From, JIDSet, Packet) ->
|
||||
lists:foreach(fun(JID) ->
|
||||
FJID = jlib:make_jid(JID),
|
||||
|
||||
+34
-4
@@ -59,6 +59,7 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("ejabberd_ctl.hrl").
|
||||
-include("mod_privacy.hrl").
|
||||
|
||||
-record(session, {sid, usr, us, priority, info}).
|
||||
-record(state, {}).
|
||||
@@ -384,28 +385,32 @@ do_route(From, To, Packet) ->
|
||||
Reason = xml:get_path_s(
|
||||
Packet,
|
||||
[{elem, "status"}, cdata]),
|
||||
{ejabberd_hooks:run_fold(
|
||||
{is_privacy_allow(From, To, Packet) andalso
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
LServer,
|
||||
false,
|
||||
[User, Server, From, subscribe, Reason]),
|
||||
true};
|
||||
"subscribed" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
{is_privacy_allow(From, To, Packet) andalso
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
LServer,
|
||||
false,
|
||||
[User, Server, From, subscribed, ""]),
|
||||
true};
|
||||
"unsubscribe" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
{is_privacy_allow(From, To, Packet) andalso
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
LServer,
|
||||
false,
|
||||
[User, Server, From, unsubscribe, ""]),
|
||||
true};
|
||||
"unsubscribed" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
{is_privacy_allow(From, To, Packet) andalso
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
LServer,
|
||||
false,
|
||||
@@ -469,6 +474,31 @@ do_route(From, To, Packet) ->
|
||||
end
|
||||
end.
|
||||
|
||||
%% The default list applies to the user as a whole,
|
||||
%% and is processed if there is no active list set
|
||||
%% for the target session/resource to which a stanza is addressed,
|
||||
%% or if there are no current sessions for the user.
|
||||
is_privacy_allow(From, To, Packet) ->
|
||||
User = To#jid.user,
|
||||
Server = To#jid.server,
|
||||
PrivacyList = ejabberd_hooks:run_fold(privacy_get_user_list, Server,
|
||||
#userlist{}, [User, Server]),
|
||||
is_privacy_allow(From, To, Packet, PrivacyList).
|
||||
|
||||
%% Check if privacy rules allow this delivery
|
||||
%% Function copied from ejabberd_c2s.erl
|
||||
is_privacy_allow(From, To, Packet, PrivacyList) ->
|
||||
User = To#jid.user,
|
||||
Server = To#jid.server,
|
||||
allow == ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, Server,
|
||||
allow,
|
||||
[User,
|
||||
Server,
|
||||
PrivacyList,
|
||||
{From, To, Packet},
|
||||
in]).
|
||||
|
||||
route_message(From, To, Packet) ->
|
||||
LUser = To#jid.luser,
|
||||
LServer = To#jid.lserver,
|
||||
|
||||
@@ -6,6 +6,8 @@ CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
ASN_FLAGS = -bber_bin +optimize +driver
|
||||
|
||||
ERLANG_CFLAGS = @ERLANG_CFLAGS@
|
||||
ERLANG_LIBS = @ERLANG_LIBS@
|
||||
|
||||
@@ -25,7 +27,7 @@ all: $(BEAMS) ELDAPv3.beam
|
||||
ELDAPv3.beam: ELDAPv3.erl
|
||||
|
||||
ELDAPv3.erl: ELDAPv3.asn
|
||||
@ERLC@ -bber_bin -W $(EFLAGS) $<
|
||||
@ERLC@ $(ASN_FLAGS) -W $(EFLAGS) $<
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl ELDAPv3.erl
|
||||
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
@@ -6,6 +6,8 @@ EFLAGS = -I .. -pz ..
|
||||
OUTDIR = ..
|
||||
BEAMS = ..\eldap.beam ..\eldap_filter.beam ..\eldap_pool.beam ..\eldap_utils.beam
|
||||
|
||||
ASN_FLAGS = -bber_bin +optimize +driver
|
||||
|
||||
ALL : $(BEAMS)
|
||||
|
||||
Clean :
|
||||
@@ -16,7 +18,7 @@ Clean :
|
||||
-@erase $(BEAMS)
|
||||
|
||||
ELDAPv3.erl : ELDAPv3.asn
|
||||
erlc -bber_bin -W $(EFLAGS) ELDAPv3.asn
|
||||
erlc $(ASN_FLAGS) -W $(EFLAGS) ELDAPv3.asn
|
||||
|
||||
$(OUTDIR)\eldap.beam : eldap.erl ELDAPv3.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) eldap.erl
|
||||
|
||||
+116
-104
@@ -85,6 +85,10 @@
|
||||
-define(RETRY_TIMEOUT, 500).
|
||||
-define(BIND_TIMEOUT, 10000).
|
||||
-define(CMD_TIMEOUT, 100000).
|
||||
%% Used in gen_fsm sync calls.
|
||||
-define(CALL_TIMEOUT, ?CMD_TIMEOUT + ?BIND_TIMEOUT + ?RETRY_TIMEOUT).
|
||||
%% Used as a timeout for gen_tcp:send/2
|
||||
-define(SEND_TIMEOUT, 30000).
|
||||
-define(MAX_TRANSACTION_ID, 65535).
|
||||
-define(MIN_TRANSACTION_ID, 0).
|
||||
|
||||
@@ -98,7 +102,7 @@
|
||||
id = 0, % LDAP Request ID
|
||||
bind_timer, % Ref to bind timeout
|
||||
dict, % dict holding operation params and results
|
||||
bind_q % Queue for bind() requests
|
||||
req_q % Queue for requests
|
||||
}).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -141,7 +145,8 @@ close(Handle) ->
|
||||
%%% --------------------------------------------------------------------
|
||||
add(Handle, Entry, Attributes) when list(Entry),list(Attributes) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(Handle1, {add, Entry, add_attrs(Attributes)}).
|
||||
gen_fsm:sync_send_event(Handle1, {add, Entry, add_attrs(Attributes)},
|
||||
?CALL_TIMEOUT).
|
||||
|
||||
%%% Do sanity check !
|
||||
add_attrs(Attrs) ->
|
||||
@@ -166,7 +171,7 @@ add_attrs(Attrs) ->
|
||||
%%% --------------------------------------------------------------------
|
||||
delete(Handle, Entry) when list(Entry) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(Handle1, {delete, Entry}).
|
||||
gen_fsm:sync_send_event(Handle1, {delete, Entry}, ?CALL_TIMEOUT).
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Modify an entry. Given an entry a number of modification
|
||||
@@ -181,7 +186,7 @@ delete(Handle, Entry) when list(Entry) ->
|
||||
%%% --------------------------------------------------------------------
|
||||
modify(Handle, Object, Mods) when list(Object), list(Mods) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(Handle1, {modify, Object, Mods}).
|
||||
gen_fsm:sync_send_event(Handle1, {modify, Object, Mods}, ?CALL_TIMEOUT).
|
||||
|
||||
%%%
|
||||
%%% Modification operations.
|
||||
@@ -214,7 +219,10 @@ m(Operation, Type, Values) ->
|
||||
modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup)
|
||||
when list(Entry),list(NewRDN),atom(DelOldRDN),list(NewSup) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(Handle1, {modify_dn, Entry, NewRDN, bool_p(DelOldRDN), optional(NewSup)}).
|
||||
gen_fsm:sync_send_event(
|
||||
Handle1,
|
||||
{modify_dn, Entry, NewRDN, bool_p(DelOldRDN), optional(NewSup)},
|
||||
?CALL_TIMEOUT).
|
||||
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
@@ -228,7 +236,7 @@ modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup)
|
||||
bind(Handle, RootDN, Passwd)
|
||||
when list(RootDN),list(Passwd) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(Handle1, {bind, RootDN, Passwd}, infinity).
|
||||
gen_fsm:sync_send_event(Handle1, {bind, RootDN, Passwd}, ?CALL_TIMEOUT).
|
||||
|
||||
%%% Sanity checks !
|
||||
|
||||
@@ -273,7 +281,7 @@ search(Handle, L) when list(L) ->
|
||||
|
||||
call_search(Handle, A) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(Handle1, {search, A}, infinity).
|
||||
gen_fsm:sync_send_event(Handle1, {search, A}, ?CALL_TIMEOUT).
|
||||
|
||||
parse_search_args(Args) ->
|
||||
parse_search_args(Args, #eldap_search{scope = wholeSubtree}).
|
||||
@@ -382,7 +390,7 @@ init({Hosts, Port, Rootdn, Passwd}) ->
|
||||
passwd = Passwd,
|
||||
id = 0,
|
||||
dict = dict:new(),
|
||||
bind_q = queue:new()}, 0}.
|
||||
req_q = queue:new()}, 0}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
@@ -405,38 +413,20 @@ connecting(timeout, S) ->
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
connecting(_Event, _From, S) ->
|
||||
Reply = {error, connecting},
|
||||
{reply, Reply, connecting, S}.
|
||||
connecting(Event, From, S) ->
|
||||
Q = queue:in({Event, From}, S#eldap.req_q),
|
||||
{next_state, connecting, S#eldap{req_q=Q}}.
|
||||
|
||||
wait_bind_response(_Event, _From, S) ->
|
||||
Reply = {error, wait_bind_response},
|
||||
{reply, Reply, wait_bind_response, S}.
|
||||
wait_bind_response(Event, From, S) ->
|
||||
Q = queue:in({Event, From}, S#eldap.req_q),
|
||||
{next_state, wait_bind_response, S#eldap{req_q=Q}}.
|
||||
|
||||
active_bind(Event, From, S) ->
|
||||
Q = queue:in({Event, From}, S#eldap.req_q),
|
||||
{next_state, active_bind, S#eldap{req_q=Q}}.
|
||||
|
||||
active(Event, From, S) ->
|
||||
case catch send_command(Event, From, S) of
|
||||
{ok, NewS} ->
|
||||
case Event of
|
||||
{bind, _, _} ->
|
||||
{next_state, active_bind, NewS};
|
||||
_ ->
|
||||
{next_state, active, NewS}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
{reply, {error, Reason}, active, S};
|
||||
{'EXIT', Reason} ->
|
||||
{reply, {error, Reason}, active, S}
|
||||
end.
|
||||
|
||||
active_bind({bind, RootDN, Passwd}, From, #eldap{bind_q=Q} = S) ->
|
||||
NewQ = queue:in({{bind, RootDN, Passwd}, From}, Q),
|
||||
{next_state, active_bind, S#eldap{bind_q=NewQ}};
|
||||
active_bind(Event, From, S) ->
|
||||
case catch send_command(Event, From, S) of
|
||||
{ok, NewS} -> {next_state, active_bind, NewS};
|
||||
{error, Reason} -> {reply, {error, Reason}, active_bind, S};
|
||||
{'EXIT', Reason} -> {reply, {error, Reason}, active_bind, S}
|
||||
end.
|
||||
process_command(S, Event, From).
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/3
|
||||
@@ -446,21 +436,8 @@ active_bind(Event, From, S) ->
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(close, _StateName, S) ->
|
||||
gen_tcp:close(S#eldap.fd),
|
||||
{stop, closed, S};
|
||||
|
||||
handle_event(process_bind_q, active_bind, #eldap{bind_q=Q} = S) ->
|
||||
case queue:out(Q) of
|
||||
{{value, {BindEvent, To}}, NewQ} ->
|
||||
NewStateData = case catch send_command(BindEvent, To, S) of
|
||||
{ok, NewS} -> NewS;
|
||||
{error, Reason} -> gen_fsm:reply(To, {error, Reason}), S;
|
||||
{'EXIT', Reason} -> gen_fsm:reply(To, {error, Reason}), S
|
||||
end,
|
||||
{next_state, active_bind, NewStateData#eldap{bind_q=NewQ}};
|
||||
{empty, Q} ->
|
||||
{next_state, active, S}
|
||||
end;
|
||||
catch gen_tcp:close(S#eldap.fd),
|
||||
{stop, normal, S};
|
||||
|
||||
handle_event(_Event, StateName, S) ->
|
||||
{next_state, StateName, S}.
|
||||
@@ -489,59 +466,61 @@ handle_sync_event(_Event, _From, StateName, S) ->
|
||||
%% Packets arriving in various states
|
||||
%%
|
||||
handle_info({tcp, _Socket, Data}, connecting, S) ->
|
||||
?DEBUG("eldap. tcp packet received when disconnected!~n~p", [Data]),
|
||||
?DEBUG("tcp packet received when disconnected!~n~p", [Data]),
|
||||
{next_state, connecting, S};
|
||||
|
||||
handle_info({tcp, _Socket, Data}, wait_bind_response, S) ->
|
||||
cancel_timer(S#eldap.bind_timer),
|
||||
case catch recvd_wait_bind_response(Data, S) of
|
||||
bound -> {next_state, active, S};
|
||||
{fail_bind, _Reason} -> close_and_retry(S),
|
||||
{next_state, connecting, S#eldap{fd = null}};
|
||||
{'EXIT', _Reason} -> close_and_retry(S),
|
||||
{next_state, connecting, S#eldap{fd = null}};
|
||||
{error, _Reason} -> close_and_retry(S),
|
||||
{next_state, connecting, S#eldap{fd = null}}
|
||||
bound ->
|
||||
dequeue_commands(S);
|
||||
{fail_bind, _Reason} ->
|
||||
{next_state, connecting, close_and_retry(S)};
|
||||
{'EXIT', _Reason} ->
|
||||
{next_state, connecting, close_and_retry(S)};
|
||||
{error, _Reason} ->
|
||||
{next_state, connecting, close_and_retry(S)}
|
||||
end;
|
||||
|
||||
handle_info({tcp, _Socket, Data}, StateName, S)
|
||||
when StateName==active; StateName==active_bind ->
|
||||
when StateName == active orelse StateName == active_bind ->
|
||||
case catch recvd_packet(Data, S) of
|
||||
{reply, Reply, To, NewS} -> gen_fsm:reply(To, Reply),
|
||||
{next_state, StateName, NewS};
|
||||
{ok, NewS} -> {next_state, StateName, NewS};
|
||||
{'EXIT', _Reason} -> {next_state, StateName, S};
|
||||
{error, _Reason} -> {next_state, StateName, S}
|
||||
{response, Response, RequestType} ->
|
||||
NewS = case Response of
|
||||
{reply, Reply, To, S1} ->
|
||||
gen_fsm:reply(To, Reply),
|
||||
S1;
|
||||
{ok, S1} ->
|
||||
S1
|
||||
end,
|
||||
if (StateName == active_bind andalso
|
||||
RequestType == bindRequest) orelse
|
||||
(StateName == active) ->
|
||||
dequeue_commands(NewS);
|
||||
true ->
|
||||
{next_state, StateName, NewS}
|
||||
end;
|
||||
_ ->
|
||||
{next_state, StateName, S}
|
||||
end;
|
||||
|
||||
handle_info({tcp_closed, _Socket}, Fsm_state, S) ->
|
||||
?WARNING_MSG("LDAP server closed the connection: ~s:~p~nIn State: ~p",
|
||||
[S#eldap.host, S#eldap.port ,Fsm_state]),
|
||||
F = fun(_Id, [{Timer, From, _Name}|_]) ->
|
||||
gen_fsm:reply(From, {error, tcp_closed}),
|
||||
cancel_timer(Timer)
|
||||
end,
|
||||
dict:map(F, S#eldap.dict),
|
||||
{ok, NextState, NewS} = connect_bind(S#eldap{fd = null,
|
||||
dict = dict:new(),
|
||||
bind_q=queue:new()}),
|
||||
{next_state, NextState, NewS};
|
||||
{next_state, connecting, close_and_retry(S)};
|
||||
|
||||
handle_info({tcp_error, _Socket, Reason}, Fsm_state, S) ->
|
||||
?DEBUG("eldap received tcp_error: ~p~nIn State: ~p", [Reason, Fsm_state]),
|
||||
%% XXX wouldn't it be safer to try reconnect ?
|
||||
%% if we were waiting a result, we may mait forever
|
||||
%% cause request is probably lost....
|
||||
{next_state, Fsm_state, S};
|
||||
{next_state, connecting, close_and_retry(S)};
|
||||
|
||||
%%
|
||||
%% Timers
|
||||
%%
|
||||
handle_info({timeout, Timer, {cmd_timeout, Id}}, active, S) ->
|
||||
handle_info({timeout, Timer, {cmd_timeout, Id}}, StateName, S) ->
|
||||
case cmd_timeout(Timer, Id, S) of
|
||||
{reply, To, Reason, NewS} -> gen_fsm:reply(To, Reason),
|
||||
{next_state, active, NewS};
|
||||
{error, _Reason} -> {next_state, active, S}
|
||||
{next_state, StateName, NewS};
|
||||
{error, _Reason} -> {next_state, StateName, S}
|
||||
end;
|
||||
|
||||
handle_info({timeout, retry_connect}, connecting, S) ->
|
||||
@@ -549,8 +528,7 @@ handle_info({timeout, retry_connect}, connecting, S) ->
|
||||
{next_state, NextState, NewS};
|
||||
|
||||
handle_info({timeout, _Timer, bind_timeout}, wait_bind_response, S) ->
|
||||
close_and_retry(S),
|
||||
{next_state, connecting, S#eldap{fd = null}};
|
||||
{next_state, connecting, close_and_retry(S)};
|
||||
|
||||
%%
|
||||
%% Make sure we don't fill the message queue with rubbish
|
||||
@@ -579,6 +557,34 @@ code_change(_OldVsn, StateName, S, _Extra) ->
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
dequeue_commands(S) ->
|
||||
case queue:out(S#eldap.req_q) of
|
||||
{{value, {Event, From}}, Q} ->
|
||||
case process_command(S#eldap{req_q=Q}, Event, From) of
|
||||
{_, active, NewS} ->
|
||||
dequeue_commands(NewS);
|
||||
Res ->
|
||||
Res
|
||||
end;
|
||||
{empty, _} ->
|
||||
{next_state, active, S}
|
||||
end.
|
||||
|
||||
process_command(S, Event, From) ->
|
||||
case send_command(Event, From, S) of
|
||||
{ok, NewS} ->
|
||||
case Event of
|
||||
{bind, _, _} ->
|
||||
{next_state, active_bind, NewS};
|
||||
_ ->
|
||||
{next_state, active, NewS}
|
||||
end;
|
||||
{error, _Reason} ->
|
||||
Q = queue:in_r({Event, From}, S#eldap.req_q),
|
||||
NewS = close_and_retry(S#eldap{req_q=Q}),
|
||||
{next_state, connecting, NewS}
|
||||
end.
|
||||
|
||||
send_command(Command, From, S) ->
|
||||
Id = bump_id(S),
|
||||
{Name, Request} = gen_req(Command),
|
||||
@@ -589,7 +595,7 @@ send_command(Command, From, S) ->
|
||||
case gen_tcp:send(S#eldap.fd, Bytes) of
|
||||
ok ->
|
||||
Timer = erlang:start_timer(?CMD_TIMEOUT, self(), {cmd_timeout, Id}),
|
||||
New_dict = dict:store(Id, [{Timer, From, Name}], S#eldap.dict),
|
||||
New_dict = dict:store(Id, [{Timer, Command, From, Name}], S#eldap.dict),
|
||||
{ok, S#eldap{id = Id, dict = New_dict}};
|
||||
Error ->
|
||||
Error
|
||||
@@ -649,6 +655,7 @@ recvd_packet(Pkt, S) ->
|
||||
Dict = S#eldap.dict,
|
||||
Id = Msg#'LDAPMessage'.messageID,
|
||||
{Timer, From, Name, Result_so_far} = get_op_rec(Id, Dict),
|
||||
Answer =
|
||||
case {Name, Op} of
|
||||
{searchRequest, {searchResEntry, R}} when
|
||||
record(R,'SearchResultEntry') ->
|
||||
@@ -696,14 +703,14 @@ recvd_packet(Pkt, S) ->
|
||||
New_dict = dict:erase(Id, Dict),
|
||||
cancel_timer(Timer),
|
||||
Reply = check_bind_reply(Result, From),
|
||||
gen_fsm:send_all_state_event(self(), process_bind_q),
|
||||
{reply, Reply, From, S#eldap{dict = New_dict}};
|
||||
{OtherName, OtherResult} ->
|
||||
New_dict = dict:erase(Id, Dict),
|
||||
cancel_timer(Timer),
|
||||
{reply, {error, {invalid_result, OtherName, OtherResult}},
|
||||
From, S#eldap{dict = New_dict}}
|
||||
end;
|
||||
end,
|
||||
{response, Answer, Name};
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
@@ -723,7 +730,7 @@ check_bind_reply(Other, _From) ->
|
||||
|
||||
get_op_rec(Id, Dict) ->
|
||||
case dict:find(Id, Dict) of
|
||||
{ok, [{Timer, From, Name}|Res]} ->
|
||||
{ok, [{Timer, _Command, From, Name}|Res]} ->
|
||||
{Timer, From, Name, Res};
|
||||
error ->
|
||||
throw({error, unkown_id})
|
||||
@@ -784,13 +791,16 @@ check_tag(Data) ->
|
||||
end.
|
||||
|
||||
close_and_retry(S) ->
|
||||
gen_tcp:close(S#eldap.fd),
|
||||
retry_connect().
|
||||
|
||||
retry_connect() ->
|
||||
erlang:send_after(?RETRY_TIMEOUT, self(),
|
||||
{timeout, retry_connect}).
|
||||
|
||||
catch gen_tcp:close(S#eldap.fd),
|
||||
Queue = dict:fold(
|
||||
fun(_Id, [{Timer, Command, From, _Name}|_], Q) ->
|
||||
cancel_timer(Timer),
|
||||
queue:in_r({Command, From}, Q);
|
||||
(_, _, Q) ->
|
||||
Q
|
||||
end, S#eldap.req_q, S#eldap.dict),
|
||||
erlang:send_after(?RETRY_TIMEOUT, self(), {timeout, retry_connect}),
|
||||
S#eldap{fd=null, req_q=Queue, dict=dict:new()}.
|
||||
|
||||
%%-----------------------------------------------------------------------
|
||||
%% Sort out timed out commands
|
||||
@@ -798,7 +808,7 @@ retry_connect() ->
|
||||
cmd_timeout(Timer, Id, S) ->
|
||||
Dict = S#eldap.dict,
|
||||
case dict:find(Id, Dict) of
|
||||
{ok, [{Timer, From, Name}|Res]} ->
|
||||
{ok, [{Timer, _Command, From, Name}|Res]} ->
|
||||
case Name of
|
||||
searchRequest ->
|
||||
{Res1, Ref1} = polish(Res),
|
||||
@@ -841,7 +851,8 @@ polish([], Res, Ref) ->
|
||||
%%-----------------------------------------------------------------------
|
||||
connect_bind(S) ->
|
||||
Host = next_host(S#eldap.host, S#eldap.hosts),
|
||||
TcpOpts = [{packet, asn1}, {active, true}, {keepalive, true}, binary],
|
||||
TcpOpts = [{packet, asn1}, {active, true}, {keepalive, true},
|
||||
{send_timeout, ?SEND_TIMEOUT}, binary],
|
||||
?INFO_MSG("LDAP connection on ~s:~p", [Host, S#eldap.port]),
|
||||
case gen_tcp:connect(Host, S#eldap.port, TcpOpts) of
|
||||
{ok, Socket} ->
|
||||
@@ -853,15 +864,16 @@ connect_bind(S) ->
|
||||
host = Host,
|
||||
bind_timer = Timer}};
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("LDAP bind failed on ~s:~p~nReason: ~p", [Host, S#eldap.port, Reason]),
|
||||
gen_tcp:close(Socket),
|
||||
retry_connect(),
|
||||
{ok, connecting, S#eldap{host = Host}}
|
||||
?ERROR_MSG("LDAP bind failed on ~s:~p~nReason: ~p",
|
||||
[Host, S#eldap.port, Reason]),
|
||||
NewS = close_and_retry(S),
|
||||
{ok, connecting, NewS#eldap{host = Host}}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("LDAP connection failed on ~s:~p~nReason: ~p", [Host, S#eldap.port, Reason]),
|
||||
retry_connect(),
|
||||
{ok, connecting, S#eldap{host = Host}}
|
||||
?ERROR_MSG("LDAP connection failed on ~s:~p~nReason: ~p",
|
||||
[Host, S#eldap.port, Reason]),
|
||||
NewS = close_and_retry(S),
|
||||
{ok, connecting, NewS#eldap{host = Host}}
|
||||
end.
|
||||
|
||||
bind_request(Socket, S) ->
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
* Workaround for EI encode_string bug
|
||||
*/
|
||||
|
||||
int x_fix_buff(ei_x_buff* x, int szneeded);
|
||||
|
||||
#define put8(s,n) do { \
|
||||
(s)[0] = (char)((n) & 0xff); \
|
||||
(s) += 1; \
|
||||
|
||||
+18
-20
@@ -35,7 +35,7 @@
|
||||
note_caps/3,
|
||||
clear_caps/1,
|
||||
get_features/2,
|
||||
get_user_resource/2,
|
||||
get_user_resources/2,
|
||||
handle_disco_response/3]).
|
||||
|
||||
%% gen_mod callbacks
|
||||
@@ -61,7 +61,7 @@
|
||||
-record(caps, {node, version, exts}).
|
||||
-record(caps_features, {node_pair, features}).
|
||||
-record(user_caps, {jid, caps}).
|
||||
-record(user_caps_default, {uid, resource}).
|
||||
-record(user_caps_resources, {uid, resource}).
|
||||
-record(state, {host,
|
||||
disco_requests = ?DICT:new(),
|
||||
feature_queries = []}).
|
||||
@@ -109,21 +109,17 @@ clear_caps(JID) ->
|
||||
BJID = list_to_binary(jlib:jid_to_string(JID)),
|
||||
BUID = list_to_binary(jlib:jid_to_string({U, S, []})),
|
||||
catch mnesia:dirty_delete({user_caps, BJID}),
|
||||
case catch mnesia:dirty_read({user_caps_default, BUID}) of
|
||||
[#user_caps_default{resource=R}] ->
|
||||
catch mnesia:dirty_delete({user_caps_default, BUID});
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
catch mnesia:dirty_delete_object(#user_caps_resources{uid = BUID, resource = list_to_binary(R)}),
|
||||
ok.
|
||||
|
||||
%% give default user resource
|
||||
get_user_resource(LUser, LServer) ->
|
||||
get_user_resources(LUser, LServer) ->
|
||||
BUID = list_to_binary(jlib:jid_to_string({LUser, LServer, []})),
|
||||
case catch mnesia:dirty_read({user_caps_default, BUID}) of
|
||||
[#user_caps_default{resource=R}] ->
|
||||
R;
|
||||
_ ->
|
||||
[]
|
||||
case catch mnesia:dirty_read({user_caps_resources, BUID}) of
|
||||
{'EXIT', _} ->
|
||||
[];
|
||||
Resources ->
|
||||
lists:map(fun(#user_caps_resources{resource=R}) -> binary_to_list(R) end, Resources)
|
||||
end.
|
||||
|
||||
%% note_caps should be called to make the module request disco
|
||||
@@ -180,9 +176,11 @@ init([Host, _Opts]) ->
|
||||
mnesia:create_table(user_caps,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, user_caps)}]),
|
||||
mnesia:create_table(user_caps_default,
|
||||
mnesia:create_table(user_caps_resources,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, user_caps_default)}]),
|
||||
{type, bag},
|
||||
{attributes, record_info(fields, user_caps_resources)}]),
|
||||
mnesia:delete_table(user_caps_default),
|
||||
{ok, #state{host = Host}}.
|
||||
|
||||
maybe_get_features(#caps{node = Node, version = Version, exts = Exts}) ->
|
||||
@@ -239,11 +237,11 @@ handle_cast({note_caps, From,
|
||||
mnesia:dirty_write(#user_caps{jid = BJID, caps = Caps}),
|
||||
case ejabberd_sm:get_user_resources(U, S) of
|
||||
[] ->
|
||||
ok;
|
||||
_ ->
|
||||
% only store default resource of external contacts
|
||||
% only store resources of caps aware external contacts
|
||||
BUID = list_to_binary(jlib:jid_to_string(jlib:jid_remove_resource(From))),
|
||||
mnesia:dirty_write(#user_caps_default{uid = BUID, resource = R})
|
||||
mnesia:dirty_write(#user_caps_resources{uid = BUID, resource = list_to_binary(R)});
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
SubNodes = [Version | Exts],
|
||||
%% Now, find which of these are not already in the database.
|
||||
|
||||
@@ -701,7 +701,8 @@ htmlize2(S1, NoFollow) ->
|
||||
S2 = element(2, regexp:gsub(S1, "\\&", "\\&")),
|
||||
S3 = element(2, regexp:gsub(S2, "<", "\\<")),
|
||||
S4 = element(2, regexp:gsub(S3, ">", "\\>")),
|
||||
S5 = element(2, regexp:gsub(S4, "[-+.a-zA-Z0-9]+://[^] )\'\"}]+", link_regexp(NoFollow))),
|
||||
S5 = element(2, regexp:gsub(S4, "(http|https|ftp|mailto|xmpp)://[^] )\'\"}]+",
|
||||
link_regexp(NoFollow))),
|
||||
%% Remove 'right-to-left override' unicode character 0x202e
|
||||
element(2, regexp:gsub(S5, [226,128,174], "[RLO]")).
|
||||
|
||||
|
||||
@@ -1461,7 +1461,7 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
|
||||
From, Err),
|
||||
StateData;
|
||||
{_, _, _, Role} ->
|
||||
case check_password(Affiliation, Els, StateData) of
|
||||
case check_password(ServiceAffiliation, Els, StateData) of
|
||||
true ->
|
||||
NewState =
|
||||
add_user_presence(
|
||||
@@ -1518,8 +1518,9 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
|
||||
end.
|
||||
|
||||
check_password(owner, _Els, _StateData) ->
|
||||
%% Don't check pass if user is owner in MUC service (access_admin option)
|
||||
true;
|
||||
check_password(_Affiliation, Els, StateData) ->
|
||||
check_password(_ServiceAffiliation, Els, StateData) ->
|
||||
case (StateData#state.config)#config.password_protected of
|
||||
false ->
|
||||
true;
|
||||
|
||||
+2
-1
@@ -414,7 +414,8 @@ parse_items(Els) ->
|
||||
parse_items(Els, []).
|
||||
|
||||
parse_items([], Res) ->
|
||||
lists:reverse(Res);
|
||||
%% Sort the items by their 'order' attribute
|
||||
lists:keysort(#listitem.order, Res);
|
||||
parse_items([{xmlelement, "item", Attrs, SubEls} | Els], Res) ->
|
||||
Type = xml:get_attr("type", Attrs),
|
||||
Value = xml:get_attr("value", Attrs),
|
||||
|
||||
@@ -414,7 +414,8 @@ parse_items(Els) ->
|
||||
parse_items(Els, []).
|
||||
|
||||
parse_items([], Res) ->
|
||||
lists:reverse(Res);
|
||||
%% Sort the items by their 'order' attribute
|
||||
lists:keysort(#listitem.order, Res);
|
||||
parse_items([{xmlelement, "item", Attrs, SubEls} | Els], Res) ->
|
||||
Type = xml:get_attr("type", Attrs),
|
||||
Value = xml:get_attr("value", Attrs),
|
||||
|
||||
@@ -197,9 +197,9 @@ parse_options(ServerHost, Opts) ->
|
||||
ACL = gen_mod:get_opt(access, Opts, all),
|
||||
Name = gen_mod:get_opt(name, Opts, "SOCKS5 Bytestreams"),
|
||||
IP = case gen_mod:get_opt(ip, Opts, none) of
|
||||
none -> get_proxy_or_domainip(ServerHost, MyHost);
|
||||
Addr -> Addr
|
||||
end,
|
||||
none -> get_my_ip();
|
||||
Addr -> Addr
|
||||
end,
|
||||
StrIP = inet_parse:ntoa(IP),
|
||||
StreamAddr = [{"jid", MyHost}, {"host", StrIP}, {"port", integer_to_list(Port)}],
|
||||
{IP, #state{myhost = MyHost,
|
||||
@@ -209,13 +209,9 @@ parse_options(ServerHost, Opts) ->
|
||||
stream_addr = StreamAddr,
|
||||
acl = ACL}}.
|
||||
|
||||
%% Return the IP of the proxy host, or if not found, the ip of the xmpp domain
|
||||
get_proxy_or_domainip(ServerHost, MyHost) ->
|
||||
case inet:getaddr(MyHost, inet) of
|
||||
get_my_ip() ->
|
||||
{ok, MyHostName} = inet:gethostname(),
|
||||
case inet:getaddr(MyHostName, inet) of
|
||||
{ok, Addr} -> Addr;
|
||||
{error, _} ->
|
||||
case inet:getaddr(ServerHost, inet) of
|
||||
{ok, Addr} -> Addr;
|
||||
{error, _} -> {127,0,0,1}
|
||||
end
|
||||
{error, _} -> {127,0,0,1}
|
||||
end.
|
||||
|
||||
+105
-86
@@ -41,7 +41,7 @@
|
||||
|
||||
-module(mod_pubsub).
|
||||
-author('christophe.romain@process-one.net').
|
||||
-version('1.12-01').
|
||||
-version('1.12-02').
|
||||
|
||||
-behaviour(gen_server).
|
||||
-behaviour(gen_mod).
|
||||
@@ -56,6 +56,7 @@
|
||||
|
||||
%% exports for hooks
|
||||
-export([presence_probe/3,
|
||||
out_subscription/4,
|
||||
remove_user/2,
|
||||
disco_local_identity/5,
|
||||
disco_local_features/5,
|
||||
@@ -163,6 +164,7 @@ init([ServerHost, Opts]) ->
|
||||
ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
|
||||
ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
|
||||
ejabberd_hooks:add(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 50),
|
||||
ejabberd_hooks:add(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
|
||||
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50),
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
lists:foreach(
|
||||
@@ -235,6 +237,8 @@ init_nodes(Host, ServerHost, ServedHosts) ->
|
||||
ok.
|
||||
|
||||
update_database(Host) ->
|
||||
mnesia:del_table_index(pubsub_node, type),
|
||||
mnesia:del_table_index(pubsub_node, parentid),
|
||||
case catch mnesia:table_info(pubsub_node, attributes) of
|
||||
[host_node, host_parent, info] ->
|
||||
?INFO_MSG("upgrade pubsub tables",[]),
|
||||
@@ -409,10 +413,28 @@ disco_sm_items(Acc, _From, To, Node, _Lang) ->
|
||||
%% presence hooks handling functions
|
||||
%%
|
||||
|
||||
presence_probe(#jid{lserver = Host} = JID, JID, Pid) ->
|
||||
presence_probe(#jid{luser = User, lserver = Server, lresource = Resource} = JID, JID, _Pid) ->
|
||||
Proc = gen_mod:get_module_proc(Server, ?PROCNAME),
|
||||
gen_server:cast(Proc, {presence, JID}),
|
||||
gen_server:cast(Proc, {presence, User, Server, [Resource], JID});
|
||||
presence_probe(#jid{luser = User, lserver = Server, lresource = Resource}, #jid{lserver = Host} = JID, _Pid) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
gen_server:cast(Proc, {presence, JID, Pid});
|
||||
presence_probe(_, _, _) ->
|
||||
gen_server:cast(Proc, {presence, User, Server, [Resource], JID}).
|
||||
|
||||
%% -------
|
||||
%% subscription hooks handling functions
|
||||
%%
|
||||
|
||||
out_subscription(User, Server, JID, subscribed) ->
|
||||
Owner = jlib:make_jid(User, Server, ""),
|
||||
{PUser, PServer, PResource} = jlib:jid_tolower(JID),
|
||||
PResources = case PResource of
|
||||
[] -> user_resources(PUser, PServer);
|
||||
_ -> [PResource]
|
||||
end,
|
||||
Proc = gen_mod:get_module_proc(Server, ?PROCNAME),
|
||||
gen_server:cast(Proc, {presence, PUser, PServer, PResources, Owner});
|
||||
out_subscription(_, _, _, _) ->
|
||||
ok.
|
||||
|
||||
%% -------
|
||||
@@ -452,11 +474,9 @@ handle_call(stop, _From, State) ->
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
handle_cast({presence, JID, Pid}, State) ->
|
||||
handle_cast({presence, JID}, State) ->
|
||||
%% A new resource is available. send last published items
|
||||
LJID = jlib:jid_tolower(JID),
|
||||
Host = State#state.host,
|
||||
ServerHost = State#state.server_host,
|
||||
%% for each node From is subscribed to
|
||||
%% and if the node is so configured, send the last published item to From
|
||||
lists:foreach(fun(Type) ->
|
||||
@@ -478,42 +498,43 @@ handle_cast({presence, JID, Pid}, State) ->
|
||||
ok
|
||||
end, Subscriptions)
|
||||
end, State#state.plugins),
|
||||
%% and send to From last PEP events published by its contacts
|
||||
case catch ejabberd_c2s:get_subscribed(Pid) of
|
||||
Contacts when is_list(Contacts) ->
|
||||
lists:foreach(
|
||||
fun({User, Server, _}) ->
|
||||
Owner = {User, Server, ""},
|
||||
lists:foreach(fun(#pubsub_node{nodeid = {_, Node}, options = Options}) ->
|
||||
case get_option(Options, send_last_published_item) of
|
||||
on_sub_and_presence ->
|
||||
case is_caps_notify(ServerHost, Node, LJID) of
|
||||
true ->
|
||||
Subscribed = case get_option(Options, access_model) of
|
||||
open -> true;
|
||||
presence -> true;
|
||||
whitelist -> false; % subscribers are added manually
|
||||
authorize -> false; % likewise
|
||||
roster ->
|
||||
Grps = get_option(Options, roster_groups_allowed, []),
|
||||
element(2, get_roster_info(User, Server, LJID, Grps))
|
||||
end,
|
||||
if Subscribed ->
|
||||
send_last_item(Owner, Node, LJID);
|
||||
true ->
|
||||
ok
|
||||
end;
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
{noreply, State};
|
||||
|
||||
handle_cast({presence, User, Server, Resources, JID}, State) ->
|
||||
%% A new resource is available. send last published PEP items
|
||||
Owner = jlib:jid_remove_resource(jlib:jid_tolower(JID)),
|
||||
Host = State#state.host,
|
||||
ServerHost = State#state.server_host,
|
||||
lists:foreach(fun(#pubsub_node{nodeid = {_, Node}, options = Options}) ->
|
||||
case get_option(Options, send_last_published_item) of
|
||||
on_sub_and_presence ->
|
||||
lists:foreach(fun(Resource) ->
|
||||
LJID = {User, Server, Resource},
|
||||
case is_caps_notify(ServerHost, Node, LJID) of
|
||||
true ->
|
||||
Subscribed = case get_option(Options, access_model) of
|
||||
open -> true;
|
||||
presence -> true;
|
||||
whitelist -> false; % subscribers are added manually
|
||||
authorize -> false; % likewise
|
||||
roster ->
|
||||
Grps = get_option(Options, roster_groups_allowed, []),
|
||||
{OU, OS, _} = Owner,
|
||||
element(2, get_roster_info(OU, OS, LJID, Grps))
|
||||
end,
|
||||
if Subscribed ->
|
||||
send_last_item(Owner, Node, LJID);
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
end, tree_action(Host, get_nodes, [Owner]))
|
||||
end, Contacts);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
end;
|
||||
false ->
|
||||
ok
|
||||
end
|
||||
end, Resources);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end, tree_action(Host, get_nodes, [Owner])),
|
||||
{noreply, State};
|
||||
|
||||
handle_cast({remove_user, LUser, LServer}, State) ->
|
||||
@@ -580,6 +601,7 @@ terminate(_Reason, #state{host = Host,
|
||||
ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
|
||||
ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
|
||||
ejabberd_hooks:delete(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 50),
|
||||
ejabberd_hooks:delete(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
|
||||
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50),
|
||||
lists:foreach(fun({NS,Mod}) ->
|
||||
gen_iq_handler:remove_iq_handler(Mod, ServerHost, NS)
|
||||
@@ -869,30 +891,25 @@ iq_pubsub(Host, ServerHost, From, IQType, SubEl, _Lang, Access, Plugins) ->
|
||||
end,
|
||||
case {IQType, Name} of
|
||||
{set, "create"} ->
|
||||
case Configuration of
|
||||
[{xmlelement, "configure", _, Config}] ->
|
||||
%% Get the type of the node
|
||||
Type = case xml:get_attr_s("type", Attrs) of
|
||||
[] -> hd(Plugins);
|
||||
T -> T
|
||||
end,
|
||||
%% we use Plugins list matching because we do not want to allocate
|
||||
%% atoms for non existing type, this prevent atom allocation overflow
|
||||
case lists:member(Type, Plugins) of
|
||||
false ->
|
||||
{error, extended_error(
|
||||
?ERR_FEATURE_NOT_IMPLEMENTED,
|
||||
unsupported, "create-nodes")};
|
||||
true ->
|
||||
create_node(Host, ServerHost, Node, From,
|
||||
Type, Access, Config)
|
||||
end;
|
||||
_ ->
|
||||
%% this breaks backward compatibility!
|
||||
%% can not create node without <configure/>
|
||||
%% but this is the new spec anyway
|
||||
?INFO_MSG("Node ~p ; invalid configuration: ~p", [Node, Configuration]),
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
Config = case Configuration of
|
||||
[{xmlelement, "configure", _, C}] -> C;
|
||||
_ -> []
|
||||
end,
|
||||
%% Get the type of the node
|
||||
Type = case xml:get_attr_s("type", Attrs) of
|
||||
[] -> hd(Plugins);
|
||||
T -> T
|
||||
end,
|
||||
%% we use Plugins list matching because we do not want to allocate
|
||||
%% atoms for non existing type, this prevent atom allocation overflow
|
||||
case lists:member(Type, Plugins) of
|
||||
false ->
|
||||
{error, extended_error(
|
||||
?ERR_FEATURE_NOT_IMPLEMENTED,
|
||||
unsupported, "create-nodes")};
|
||||
true ->
|
||||
create_node(Host, ServerHost, Node, From,
|
||||
Type, Access, Config)
|
||||
end;
|
||||
{set, "publish"} ->
|
||||
case xml:remove_cdata(Els) of
|
||||
@@ -1961,7 +1978,7 @@ get_subscriptions(Host, Node, JID) ->
|
||||
if
|
||||
not RetrieveFeature ->
|
||||
%% Service does not support manage subscriptions
|
||||
{error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "manage-affiliations")};
|
||||
{error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "manage-subscriptions")};
|
||||
Affiliation /= {result, owner} ->
|
||||
%% Entity is not an owner
|
||||
{error, ?ERR_FORBIDDEN};
|
||||
@@ -2299,7 +2316,10 @@ broadcast_stanza(Host, NodeOpts, States, Stanza) ->
|
||||
%% broadcast Stanza to all contacts of the user that are advertising
|
||||
%% interest in this kind of Node.
|
||||
broadcast_by_caps({LUser, LServer, LResource}, Node, _Type, Stanza) ->
|
||||
SenderResource = user_resource(LUser, LServer, LResource),
|
||||
SenderResource = case LResource of
|
||||
[] -> hd(user_resources(LUser, LServer));
|
||||
_ -> LResource
|
||||
end,
|
||||
case ejabberd_sm:get_session_pid(LUser, LServer, SenderResource) of
|
||||
C2SPid when is_pid(C2SPid) ->
|
||||
%% set the from address on the notification to the bare JID of the account owner
|
||||
@@ -2309,14 +2329,17 @@ broadcast_by_caps({LUser, LServer, LResource}, Node, _Type, Stanza) ->
|
||||
%%ReplyTo = jlib:make_jid(LUser, LServer, SenderResource), % This has to be used
|
||||
case catch ejabberd_c2s:get_subscribed(C2SPid) of
|
||||
Contacts when is_list(Contacts) ->
|
||||
lists:foreach(fun({U, S, R}) ->
|
||||
LJID = {U, S, user_resource(U, S, R)},
|
||||
case is_caps_notify(LServer, Node, LJID) of
|
||||
true ->
|
||||
ejabberd_router ! {route, Sender, jlib:make_jid(LJID), Stanza};
|
||||
false ->
|
||||
ok
|
||||
end
|
||||
lists:foreach(fun({U, S, _}) ->
|
||||
JIDs = lists:foldl(fun(R, Acc) ->
|
||||
LJID = {U, S, R},
|
||||
case is_caps_notify(LServer, Node, LJID) of
|
||||
true -> [LJID | Acc];
|
||||
false -> Acc
|
||||
end
|
||||
end, [], user_resources(U, S)),
|
||||
lists:foreach(fun(JID) ->
|
||||
ejabberd_router ! {route, Sender, jlib:make_jid(JID), Stanza}
|
||||
end, JIDs)
|
||||
end, Contacts);
|
||||
_ ->
|
||||
ok
|
||||
@@ -2331,15 +2354,11 @@ broadcast_by_caps(_, _, _, _) ->
|
||||
|
||||
%% If we don't know the resource, just pick first if any
|
||||
%% If no resource available, check if caps anyway (remote online)
|
||||
user_resource(LUser, LServer, []) ->
|
||||
case ejabberd_sm:get_user_resources(LUser, LServer) of
|
||||
[R|_] ->
|
||||
R;
|
||||
[] ->
|
||||
mod_caps:get_user_resource(LUser, LServer)
|
||||
end;
|
||||
user_resource(_, _, LResource) ->
|
||||
LResource.
|
||||
user_resources(User, Server) ->
|
||||
case ejabberd_sm:get_user_resources(User, Server) of
|
||||
[] -> mod_caps:get_user_resources(User, Server);
|
||||
Rs -> Rs
|
||||
end.
|
||||
|
||||
is_caps_notify(Host, Node, LJID) ->
|
||||
case mod_caps:get_caps(LJID) of
|
||||
@@ -2698,7 +2717,7 @@ tree_call({_User, Server, _Resource}, Function, Args) ->
|
||||
tree_call(Host, Function, Args) ->
|
||||
Module = case ets:lookup(gen_mod:get_module_proc(Host, pubsub_state), nodetree) of
|
||||
[{nodetree, N}] -> N;
|
||||
_ -> list_to_atom(?TREE_PREFIX ++ ?STDNODE)
|
||||
_ -> list_to_atom(?TREE_PREFIX ++ ?STDTREE)
|
||||
end,
|
||||
catch apply(Module, Function, Args).
|
||||
tree_action(Host, Function, Args) ->
|
||||
|
||||
+48
-29
@@ -253,34 +253,7 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
||||
push_item(User, LServer, To, Item),
|
||||
case Item#roster.subscription of
|
||||
remove ->
|
||||
IsTo = case OldItem#roster.subscription of
|
||||
both -> true;
|
||||
to -> true;
|
||||
_ -> false
|
||||
end,
|
||||
IsFrom = case OldItem#roster.subscription of
|
||||
both -> true;
|
||||
from -> true;
|
||||
_ -> false
|
||||
end,
|
||||
if IsTo ->
|
||||
ejabberd_router:route(
|
||||
jlib:jid_remove_resource(From),
|
||||
jlib:make_jid(OldItem#roster.jid),
|
||||
{xmlelement, "presence",
|
||||
[{"type", "unsubscribe"}],
|
||||
[]});
|
||||
true -> ok
|
||||
end,
|
||||
if IsFrom ->
|
||||
ejabberd_router:route(
|
||||
jlib:jid_remove_resource(From),
|
||||
jlib:make_jid(OldItem#roster.jid),
|
||||
{xmlelement, "presence",
|
||||
[{"type", "unsubscribed"}],
|
||||
[]});
|
||||
true -> ok
|
||||
end,
|
||||
send_unsubscribing_presence(From, OldItem),
|
||||
ok;
|
||||
_ ->
|
||||
ok
|
||||
@@ -371,7 +344,7 @@ push_item(User, Server, Resource, _From, Item) ->
|
||||
|
||||
push_item(User, Server, Resource, From, Item) ->
|
||||
ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
|
||||
id = "push",
|
||||
id = "push" ++ randoms:get_string(),
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
[item_to_xml(Item)]}]},
|
||||
@@ -607,6 +580,7 @@ remove_user(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
send_unsubscription_to_rosteritems(LUser, LServer),
|
||||
F = fun() ->
|
||||
lists:foreach(fun(R) ->
|
||||
mnesia:delete_object(R)
|
||||
@@ -615,6 +589,51 @@ remove_user(User, Server) ->
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
%% For each contact with Subscription:
|
||||
%% Both or From, send a "unsubscribed" presence stanza;
|
||||
%% Both or To, send a "unsubscribe" presence stanza.
|
||||
send_unsubscription_to_rosteritems(LUser, LServer) ->
|
||||
RosterItems = get_user_roster([], {LUser, LServer}),
|
||||
From = jlib:make_jid({LUser, LServer, ""}),
|
||||
lists:foreach(fun(RosterItem) ->
|
||||
send_unsubscribing_presence(From, RosterItem)
|
||||
end,
|
||||
RosterItems).
|
||||
|
||||
%% @spec (From::jid(), Item::roster()) -> ok
|
||||
send_unsubscribing_presence(From, Item) ->
|
||||
IsTo = case Item#roster.subscription of
|
||||
both -> true;
|
||||
to -> true;
|
||||
_ -> false
|
||||
end,
|
||||
IsFrom = case Item#roster.subscription of
|
||||
both -> true;
|
||||
from -> true;
|
||||
_ -> false
|
||||
end,
|
||||
if IsTo ->
|
||||
send_presence_type(
|
||||
jlib:jid_remove_resource(From),
|
||||
jlib:make_jid(Item#roster.jid), "unsubscribe");
|
||||
true -> ok
|
||||
end,
|
||||
if IsFrom ->
|
||||
send_presence_type(
|
||||
jlib:jid_remove_resource(From),
|
||||
jlib:make_jid(Item#roster.jid), "unsubscribed");
|
||||
true -> ok
|
||||
end,
|
||||
ok.
|
||||
|
||||
send_presence_type(From, To, Type) ->
|
||||
ejabberd_router:route(
|
||||
From, To,
|
||||
{xmlelement, "presence",
|
||||
[{"type", Type}],
|
||||
[]}).
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
set_items(User, Server, SubEl) ->
|
||||
|
||||
+48
-29
@@ -295,34 +295,7 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
||||
push_item(User, LServer, To, Item),
|
||||
case Item#roster.subscription of
|
||||
remove ->
|
||||
IsTo = case OldItem#roster.subscription of
|
||||
both -> true;
|
||||
to -> true;
|
||||
_ -> false
|
||||
end,
|
||||
IsFrom = case OldItem#roster.subscription of
|
||||
both -> true;
|
||||
from -> true;
|
||||
_ -> false
|
||||
end,
|
||||
if IsTo ->
|
||||
ejabberd_router:route(
|
||||
jlib:jid_remove_resource(From),
|
||||
jlib:make_jid(OldItem#roster.jid),
|
||||
{xmlelement, "presence",
|
||||
[{"type", "unsubscribe"}],
|
||||
[]});
|
||||
true -> ok
|
||||
end,
|
||||
if IsFrom ->
|
||||
ejabberd_router:route(
|
||||
jlib:jid_remove_resource(From),
|
||||
jlib:make_jid(OldItem#roster.jid),
|
||||
{xmlelement, "presence",
|
||||
[{"type", "unsubscribed"}],
|
||||
[]});
|
||||
true -> ok
|
||||
end,
|
||||
send_unsubscribing_presence(From, OldItem),
|
||||
ok;
|
||||
_ ->
|
||||
ok
|
||||
@@ -407,7 +380,7 @@ push_item(User, Server, Resource, _From, Item) ->
|
||||
|
||||
push_item(User, Resource, From, Item) ->
|
||||
ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
|
||||
id = "push",
|
||||
id = "push" ++ randoms:get_string(),
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
[item_to_xml(Item)]}]},
|
||||
@@ -669,9 +642,55 @@ remove_user(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
send_unsubscription_to_rosteritems(LUser, LServer),
|
||||
odbc_queries:del_user_roster_t(LServer, Username),
|
||||
ok.
|
||||
|
||||
%% For each contact with Subscription:
|
||||
%% Both or From, send a "unsubscribed" presence stanza;
|
||||
%% Both or To, send a "unsubscribe" presence stanza.
|
||||
send_unsubscription_to_rosteritems(LUser, LServer) ->
|
||||
RosterItems = get_user_roster([], {LUser, LServer}),
|
||||
From = jlib:make_jid({LUser, LServer, ""}),
|
||||
lists:foreach(fun(RosterItem) ->
|
||||
send_unsubscribing_presence(From, RosterItem)
|
||||
end,
|
||||
RosterItems).
|
||||
|
||||
%% @spec (From::jid(), Item::roster()) -> ok
|
||||
send_unsubscribing_presence(From, Item) ->
|
||||
IsTo = case Item#roster.subscription of
|
||||
both -> true;
|
||||
to -> true;
|
||||
_ -> false
|
||||
end,
|
||||
IsFrom = case Item#roster.subscription of
|
||||
both -> true;
|
||||
from -> true;
|
||||
_ -> false
|
||||
end,
|
||||
if IsTo ->
|
||||
send_presence_type(
|
||||
jlib:jid_remove_resource(From),
|
||||
jlib:make_jid(Item#roster.jid), "unsubscribe");
|
||||
true -> ok
|
||||
end,
|
||||
if IsFrom ->
|
||||
send_presence_type(
|
||||
jlib:jid_remove_resource(From),
|
||||
jlib:make_jid(Item#roster.jid), "unsubscribed");
|
||||
true -> ok
|
||||
end,
|
||||
ok.
|
||||
|
||||
send_presence_type(From, To, Type) ->
|
||||
ejabberd_router:route(
|
||||
From, To,
|
||||
{xmlelement, "presence",
|
||||
[{"type", Type}],
|
||||
[]}).
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
set_items(User, Server, SubEl) ->
|
||||
|
||||
@@ -187,7 +187,24 @@ process_item(RosterItem, Host) ->
|
||||
%% Check if the list of groups of the new roster item
|
||||
%% include at least a new one
|
||||
case lists:subtract(RosterItem#roster.groups, CommonGroups) of
|
||||
%% If it doesn't, then remove this user from any
|
||||
%% existing roster groups.
|
||||
[] ->
|
||||
%% Remove pending subscription by setting it
|
||||
%% unsubscribed.
|
||||
Mod = get_roster_mod(ServerFrom),
|
||||
|
||||
%% Remove pending out subscription
|
||||
Mod:out_subscription(UserTo, ServerTo,
|
||||
jlib:make_jid(UserFrom, ServerFrom, ""),
|
||||
unsubscribe),
|
||||
|
||||
%% Remove pending in subscription
|
||||
Mod:in_subscription(aaaa, UserFrom, ServerFrom,
|
||||
jlib:make_jid(UserTo, ServerTo, ""),
|
||||
unsubscribe, ""),
|
||||
|
||||
%% But we're still subscribed, so respond as such.
|
||||
RosterItem#roster{subscription = both, ask = none};
|
||||
%% If so, it means the user wants to add that contact
|
||||
%% to his personal roster
|
||||
@@ -212,11 +229,7 @@ build_roster_record(User1, Server1, User2, Server2, Name2, Groups) ->
|
||||
|
||||
set_new_rosteritems(UserFrom, ServerFrom,
|
||||
UserTo, ServerTo, ResourceTo, NameTo, GroupsFrom) ->
|
||||
Mod = case lists:member(mod_roster_odbc,
|
||||
gen_mod:loaded_modules(ServerFrom)) of
|
||||
true -> mod_roster_odbc;
|
||||
false -> mod_roster
|
||||
end,
|
||||
Mod = get_roster_mod(ServerFrom),
|
||||
|
||||
RIFrom = build_roster_record(UserFrom, ServerFrom,
|
||||
UserTo, ServerTo, NameTo, GroupsFrom),
|
||||
@@ -248,7 +261,7 @@ set_new_rosteritems(UserFrom, ServerFrom,
|
||||
|
||||
set_item(User, Server, Resource, Item) ->
|
||||
ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
|
||||
id = "push",
|
||||
id = "push" ++ randoms:get_string(),
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
[mod_roster:item_to_xml(Item)]}]},
|
||||
@@ -302,6 +315,18 @@ get_jid_info({Subscription, Groups}, User, Server, JID) ->
|
||||
in_subscription(Acc, User, Server, JID, Type, _Reason) ->
|
||||
process_subscription(in, User, Server, JID, Type, Acc).
|
||||
|
||||
out_subscription(UserFrom, ServerFrom, JIDTo, unsubscribed) ->
|
||||
Mod = get_roster_mod(ServerFrom),
|
||||
|
||||
%% Remove pending out subscription
|
||||
#jid{luser = UserTo, lserver = ServerTo} = JIDTo,
|
||||
JIDFrom = jlib:make_jid(UserFrom, UserTo, ""),
|
||||
Mod:out_subscription(UserTo, ServerTo, JIDFrom, unsubscribe),
|
||||
|
||||
%% Remove pending in subscription
|
||||
Mod:in_subscription(aaaa, UserFrom, ServerFrom, JIDTo, unsubscribe, ""),
|
||||
|
||||
process_subscription(out, UserFrom, ServerFrom, JIDTo, unsubscribed, false);
|
||||
out_subscription(User, Server, JID, Type) ->
|
||||
process_subscription(out, User, Server, JID, Type, false).
|
||||
|
||||
@@ -617,7 +642,7 @@ push_item(User, Server, From, Item) ->
|
||||
Item#roster.subscription}]}),
|
||||
Stanza = jlib:iq_to_xml(
|
||||
#iq{type = set, xmlns = ?NS_ROSTER,
|
||||
id = "push",
|
||||
id = "push" ++ randoms:get_string(),
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
[item_to_xml(Item)]}]}),
|
||||
@@ -910,6 +935,14 @@ shared_roster_group_parse_query(Host, Group, Query) ->
|
||||
nothing
|
||||
end.
|
||||
|
||||
%% Get the roster module for Server.
|
||||
get_roster_mod(Server) ->
|
||||
case lists:member(mod_roster_odbc,
|
||||
gen_mod:loaded_modules(Server)) of
|
||||
true -> mod_roster_odbc;
|
||||
false -> mod_roster
|
||||
end.
|
||||
|
||||
get_opt(Opts, Opt, Default) ->
|
||||
case lists:keysearch(Opt, 1, Opts) of
|
||||
{value, {_, Val}} ->
|
||||
|
||||
+1
-1
@@ -148,7 +148,7 @@ CREATE TABLE private_storage (
|
||||
CREATE INDEX i_private_storage_username USING BTREE ON private_storage(username);
|
||||
CREATE UNIQUE INDEX i_private_storage_username_namespace USING BTREE ON private_storage(username(75), namespace(75));
|
||||
|
||||
--- To update from 1.x:
|
||||
-- To update from 1.x:
|
||||
-- ALTER TABLE rosterusers ADD COLUMN askmessage text AFTER ask;
|
||||
-- UPDATE rosterusers SET askmessage = '';
|
||||
-- ALTER TABLE rosterusers ALTER COLUMN askmessage SET NOT NULL;
|
||||
|
||||
+2
-2
@@ -146,14 +146,14 @@ CREATE INDEX i_private_storage_username ON private_storage USING btree (username
|
||||
CREATE UNIQUE INDEX i_private_storage_username_namespace ON private_storage USING btree (username, namespace);
|
||||
|
||||
|
||||
--- To update from 0.9.8:
|
||||
-- To update from 0.9.8:
|
||||
-- CREATE SEQUENCE spool_seq_seq;
|
||||
-- ALTER TABLE spool ADD COLUMN seq integer;
|
||||
-- ALTER TABLE spool ALTER COLUMN seq SET DEFAULT nextval('spool_seq_seq');
|
||||
-- UPDATE spool SET seq = DEFAULT;
|
||||
-- ALTER TABLE spool ALTER COLUMN seq SET NOT NULL;
|
||||
|
||||
--- To update from 1.x:
|
||||
-- To update from 1.x:
|
||||
-- ALTER TABLE rosterusers ADD COLUMN askmessage text;
|
||||
-- UPDATE rosterusers SET askmessage = '';
|
||||
-- ALTER TABLE rosterusers ALTER COLUMN askmessage SET NOT NULL;
|
||||
|
||||
+6
-1
@@ -40,6 +40,10 @@ typedef struct {
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#endif
|
||||
|
||||
#ifndef SSL_OP_NO_TICKET
|
||||
#define SSL_OP_NO_TICKET 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* str_hash is based on the public domain code from
|
||||
* http://www.burtleburtle.net/bob/hash/doobs.html
|
||||
@@ -340,6 +344,7 @@ static int tls_drv_control(ErlDrvData handle,
|
||||
res = SSL_CTX_check_private_key(ctx);
|
||||
die_unless(res > 0, "SSL_CTX_check_private_key failed");
|
||||
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
||||
SSL_CTX_set_default_verify_paths(ctx);
|
||||
|
||||
if (command == SET_CERTIFICATE_FILE_ACCEPT)
|
||||
@@ -367,7 +372,7 @@ static int tls_drv_control(ErlDrvData handle,
|
||||
if (command == SET_CERTIFICATE_FILE_ACCEPT)
|
||||
SSL_set_accept_state(d->ssl);
|
||||
else {
|
||||
SSL_set_options(d->ssl, SSL_OP_NO_SSLv2);
|
||||
SSL_set_options(d->ssl, SSL_OP_NO_SSLv2|SSL_OP_NO_TICKET);
|
||||
SSL_set_connect_state(d->ssl);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -328,6 +328,13 @@ process_request(#state{request_method = 'POST',
|
||||
socket = Socket,
|
||||
request_handlers = RequestHandlers} = State)
|
||||
when is_integer(Len) ->
|
||||
{ok, IP} =
|
||||
case SockMod of
|
||||
gen_tcp ->
|
||||
inet:peername(Socket);
|
||||
_ ->
|
||||
SockMod:peername(Socket)
|
||||
end,
|
||||
case SockMod of
|
||||
gen_tcp ->
|
||||
inet:setopts(Socket, [{packet, 0}]);
|
||||
@@ -347,13 +354,6 @@ process_request(#state{request_method = 'POST',
|
||||
LQ ->
|
||||
LQ
|
||||
end,
|
||||
{ok, IP} =
|
||||
case SockMod of
|
||||
gen_tcp ->
|
||||
inet:peername(Socket);
|
||||
_ ->
|
||||
SockMod:peername(Socket)
|
||||
end,
|
||||
Request = #request{method = 'POST',
|
||||
path = LPath,
|
||||
q = LQuery,
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : win32_dns.erl
|
||||
%%% Author : Geoff Cant
|
||||
%%% Purpose : Get name servers in a Windows machine
|
||||
%%% Created : 5 Mar 2009 by Geoff Cant
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(win32_dns).
|
||||
-export([get_nameservers/0]).
|
||||
|
||||
-define(IF_KEY, "\\hklm\\system\\CurrentControlSet\\Services\\TcpIp\\Parameters\\Interfaces").
|
||||
-define(TOP_KEY, "\\hklm\\system\\CurrentControlSet\\Services\\TcpIp\\Parameters").
|
||||
|
||||
get_nameservers() ->
|
||||
{_, Config} = pick_config(),
|
||||
IPTs = get_value(["NameServer"], Config),
|
||||
lists:filter(fun(IPTuple) -> is_good_ns(IPTuple) end, IPTs).
|
||||
|
||||
is_good_ns(Addr) ->
|
||||
element(1,
|
||||
inet_res:nnslookup("a.root-servers.net", in, any, [{Addr,53}],
|
||||
timer:seconds(5)
|
||||
)
|
||||
) =:= ok.
|
||||
|
||||
reg() ->
|
||||
{ok, R} = win32reg:open([read]),
|
||||
R.
|
||||
|
||||
interfaces(R) ->
|
||||
ok = win32reg:change_key(R, ?IF_KEY),
|
||||
{ok, I} = win32reg:sub_keys(R),
|
||||
I.
|
||||
config_keys(R, Key) ->
|
||||
ok = win32reg:change_key(R, Key),
|
||||
[ {K,
|
||||
case win32reg:value(R, K) of
|
||||
{ok, V} -> translate(K, V);
|
||||
_ -> undefined
|
||||
end
|
||||
} || K <- ["Domain", "DhcpDomain",
|
||||
"NameServer", "DhcpNameServer", "SearchList"]].
|
||||
|
||||
translate(NS, V) when NS =:= "NameServer"; NS =:= "DhcpNameServer" ->
|
||||
IPsStrings = [string:tokens(IP, ".") || IP <- string:tokens(V, ",")],
|
||||
[ list_to_tuple([list_to_integer(String) || String <- IpStrings])
|
||||
|| IpStrings <- IPsStrings];
|
||||
translate(_, V) -> V.
|
||||
|
||||
interface_configs(R) ->
|
||||
[{If, config_keys(R, ?IF_KEY ++ "\\" ++ If)}
|
||||
|| If <- interfaces(R)].
|
||||
|
||||
sort_configs(Configs) ->
|
||||
lists:sort(fun ({_, A}, {_, B}) ->
|
||||
ANS = proplists:get_value("NameServer", A),
|
||||
BNS = proplists:get_value("NameServer", B),
|
||||
if ANS =/= undefined, BNS =:= undefined -> false;
|
||||
true -> count_undef(A) < count_undef(B)
|
||||
end
|
||||
end,
|
||||
Configs).
|
||||
|
||||
count_undef(L) when is_list(L) ->
|
||||
lists:foldl(fun ({_K, undefined}, Acc) -> Acc +1;
|
||||
({_K, []}, Acc) -> Acc +1;
|
||||
(_, Acc) -> Acc
|
||||
end, 0, L).
|
||||
|
||||
all_configs() ->
|
||||
R = reg(),
|
||||
TopConfig = config_keys(R, ?TOP_KEY),
|
||||
Configs = [{top, TopConfig}
|
||||
| interface_configs(R)],
|
||||
win32reg:close(R),
|
||||
{TopConfig, Configs}.
|
||||
|
||||
pick_config() ->
|
||||
{TopConfig, Configs} = all_configs(),
|
||||
NSConfigs = [{If, C} || {If, C} <- Configs,
|
||||
get_value(["DhcpNameServer","NameServer"], C)
|
||||
=/= undefined],
|
||||
case get_value(["DhcpNameServer","NameServer"],
|
||||
TopConfig) of
|
||||
%% No top level nameserver to pick interface with
|
||||
undefined ->
|
||||
hd(sort_configs(NSConfigs));
|
||||
%% Top level has a nameserver - use this to select an interface.
|
||||
NS ->
|
||||
Cs = [ {If, C}
|
||||
|| {If, C} <- Configs,
|
||||
lists:member(NS,
|
||||
[get_value(["NameServer"], C),
|
||||
get_value(["DhcpNameServer"], C)])],
|
||||
hd(sort_configs(Cs))
|
||||
end.
|
||||
|
||||
get_value([], _Config) -> undefined;
|
||||
get_value([K|Keys], Config) ->
|
||||
case proplists:get_value(K, Config) of
|
||||
undefined -> get_value(Keys, Config);
|
||||
V -> V
|
||||
end.
|
||||
Reference in New Issue
Block a user